Sunteți pe pagina 1din 36

Atestat informatic PROGRAMARE DINAMIC

PROGRAMARE DINAMIC
LUCRARE PENTRU ATESTAREA COMPETENELOR PROFESIONALE

Elev: Arhip Georgiana Profesor ndrumtor: Balacea Georgeta Colegiul Naional Vasile Alecsandri Galai Sesiunea Mai 2013

Atestat informatic PROGRAMARE DINAMIC

Cuprins
1. 2. 3. 4. 5. 6. 7. Motivarea alegerii temei pag. 3 Introducere n programarea dinamic pag. 4-6 Descrierea metodei pag. 7-8 Probleme de alocare unidimensional pag. 9 Un model mathematic pag. 10 Posibiliti de rezolvare pag. 11-12 Probleme clasice ce se rezolv prin metoda programrii dinamice pag. 12-18 8. Tehnici avansate de programare dinamic pag. 19-36

Atestat informatic PROGRAMARE DINAMIC

MOTIVAREA ALEGERII TEMEI

Am ales ca tem pentru acest proiect programarea dinamic deoarece reprezint o metod de programare foarte interesant. Avnd ca scop rezolvarea ntr-un mod optim a problemelor, implementarea acestei metode este provocatoare, fiind mai complex dect o rezolvare neoptim a aceleiai probleme. Provocarea const n a gndi o rezolvare dinamic a unei probleme, i apoi n implementarea acestei rezolvri n limbajul de programare, lucru ce poate fi mai dificil dect sun. Scopul acestui proiect este de a explica pe nelesul tuturor aceast metod de programare i de a strni interesul pentru aceasta i pentru tipurile de probleme ce suport o rezolvare dinamic. Majoritatea problemelor date la Olimpiada de Informatic se rezolv cu ajutorul acestei metode, de aceea consider c programarea dinamic este important.

Atestat informatic PROGRAMARE DINAMIC

INTRODUCERE N PROGRAMAREA DINAMIC


Dac analizm natura problemelor care au fost rezolvate prin programare liniar sau prin teoria grafurilor, constatm c procesul economic pe care doream s l optimizm se desfura ntro singur faz (etap sau perioad). innd cont de faptul c exist numeroase probleme de optimizare care modeleaz procese economice care se desfoar n mai multe perioade i la fiecare perioad trebuie s stabilim soluia optim, viziunea static poate constitui un neajuns. Este evident faptul c succesiunea de soluii nu se poate determina innd seama numai de parametrii fiecrei perioade analizate n parte, i c este necesar s identificm o succesiune de soluii care optimizeaz ntregul proces analizat. Problemele economice care reclam o suit de decizii secveniale se caracterizeaz prin faptul c o decizie care adoptat ntr-o anumit perioad are att un efect economic imediat, ct i unul de lung durat, care influeneaz i asupra celorlalte etape. Optimizarea proceselor secveniale se obine prin metodele unei teorii matematice relativ recent constituite i care se numete programare dinamic. Creatorul acestei teorii este Richard Bellman, iar lucrarea sa fundamental este Dynamic Programming aprut n anul 1957. Programarea dinamic are un cmp larg de aplicaie n cercetarea operaional (organizarea produciei, gestiunea stocurilor, rennoirea echipamentelor), precum i n alte domenii (navigaie cosmic, procese cu conexiune invers etc.). S presupunem un proces secvenial a crui desfurare n timp depinde de o variabil care poate lua o mulime de valori n fiecare etap. Ne putem decide pentru o valoare determinat a variabilei n fiecare etap i din aceast cauz ea se numete variabil de decizie sau de control. O succesiune oarecare de decizii constituie o politic i cea care ne intereseaz este politica optim, de pild aceea care conduce la un cost total minim al procesului. Deosebim dou tipuri principale de procese secveniale:
4

Atestat informatic PROGRAMARE DINAMIC


a) deterministe, cnd la fiecare faz procesul este controlat n ntregime de decizia pe care o lum; b) stohastice, atunci cnd evoluia procesului se desfoar sub dubla influen a deciziilor i a hazardului. Se numete politic optim acea succesiune de decizii care optimizeaz procesul n ansamblu lui, fiind vorba de un proces determinist. n cazul unui proces stohastic, se folosete n mod corespunztor noiunea de strategie optim. Procesele dinamice pot fi continue sau discrete. Un exemplu de proces discret este urmtorul: o ntreprindere trebuie s-i ntocmeasc planul de aprovizionare anual pentru un anumitmaterial; se consider 12 perioade (luni) i pentru fiecare perioad se stabilete cantitatea de aprovizionat, astfel ca pe ntregul an s rezulte un cost total minim. Procesele dinamice discrete pot avea orizontul limitat (n exemplu de mai sus 12 perioade) sau nelimitat. Programarea Dinamic (PD) este o metod de rezolvare a unor probleme de optimizare n care se opereaza pe FAZE (SECVENTE) numite n continuare PERIOADE. Prin aplicarea acestei metode, soluia optim se obtine n urma rezolvarii unui ir de probleme de optimizare LEGATE INTRE ELE, fiecare de de dimensiune si complexitate mult mai mic decat problema original. Dei teoretic PD este aplicabil oricarei probleme de optimizare multidimensional, totui metoda a dat rezultate foarte bune - n sensul uurrii rezolvrii - numai pe anumite clase de probleme cum sunt cele care modeleaz evoluia n timp a unui proces economic sau probleme de alocare (repartiie). Programarea dinamic rezolv problemele prin descompunerea lor n subprobleme i prin combinarea rezolvrilor acestora (termenul programare" se refer aici la o metod tabular). Spre deosebire de divide et impera, care considera c subproblemele sunt independente, programarea dinamic se aplic atunci cnd subproblemele nu sunt independente. ntr-un astfel de caz, divide et impera ar efectua calcule redundante, rezolvnd fiecare subproblem ca i cnd nu ar mai fi ntlnit-o. Programarea dinamic, ns, salveaz rezultatul fiecrei subprobleme ntr-o tabel, evitnd astfel rezolvarea redundant a aceleiai probleme.
5

Atestat informatic PROGRAMARE DINAMIC

Programarea dinamic se aplic n general problemelor de optimizare, atunci cnd dorim s determinm rapid soluia optim pentru o problem. De fapt, aplicnd aceast tehnic determinm una din soluiile optime, problema putnd avea mai multe soluii optime. Aplicarea acestei tehnici de programare poate fi descompus n urmtoarea secven de pai: 1. Descoperirea structurii i "msurii" pe care o are o soluie optim. 2. Definirea recursiv a valorii care caracterizeaz o soluie optim. 3. Calcularea "de jos n sus" a acestei valori. 4. Construirea soluiei optime pornind de la calculele efectuate anterior. Primii trei pai reprezint baza programrii dinamice. Dac trebuie doar s aflm doarvaloarea soluiei optime, ultimul pas nu mai este necesar. De aceea, pentru cazul n care dorim s determinm i soluia optim poate fi nevoie de construirea unor structuri de date suplimentare.

Atestat informatic PROGRAMARE DINAMIC

DESCRIEREA METODEI
O problem ar putea fi rezolvat prin programare dinamic dac poate fi descompus n subprobleme asemntoare de dimensiuni mai mici i soluia optim a problemei depinde de soluiile optime ale subproblemelor sale. Dar acest lucru nu garanteaz c programarea dinamic e soluia, sunt situaii n care se poate aplica cu succes metoda Greedy sau Divide et Impera. Dac ns subproblemele nu sunt independente, ci se suprapun (Overlapping subproblems), un algoritm Divide et Impera ar duce la un timp mare de execuie, pe motiv c o aceeai subproblem se rezolv de mai multe ori. S analizm calculul coeficientului binomial

O funcie recursiv ar arta astfel: Code: int comb(int n, int k){ if (k==0 || k==n) return 1; else return comb(n-1,k)+comb(n-1,k-1); } Multe din valorile comb(n,k) sunt calculate n mod repetat, ceea ce face aceast abordare neeficient. n astfel de situaii n care exist un numr relativ mic de subprobleme independente, restul suprapunndu-se, soluiile subproblemelor nu vor fi calculate dect o singur dat i vor fi reinute ntr-o structur de date intern (de obicei un tablou). n acest caz, funcia se poate scrie: Code: int c[100][100],k,n; void comb(){ for(int i=0;i<=n;i++) c[i][0]=c[i][i]=1; for(i=1;i<=n;i++) for(int j=1;j<i;j++)

Atestat informatic PROGRAMARE DINAMIC


c[i][j]=c[i-1][j]+c[i-1][j-1]; } c[n][k] este elementul cutat. Am completat astfel triunghiul lui Pascal:

Acelai lucru se poate spune despre calculul termenilor irului lui Fibonacci: o funcie recursiv ar calcula n mod repetat aceiai termeni, pe cnd un vector poate reine termenii calculai o singur dat: Code: int f[100],n; f[0]=f[1]=1; for(int i=2;i<=n;i++) f[i]=f[i-1]+f[i-2]; Putem spune c metoda Divide et Impera opereaz de sus n jos (topdown), descompunnd problema n subprobleme din ce n ce mai mici, pe care le rezolv apoi separat. Programarea dinamic opereaz de jos n sus (bottom-up). Se pornete de obicei de la cele mai mici subprobleme. Combinnd soluiile lor, se obin soluii pentru subprobleme din ce in ce mai mari, pn se ajunge, n final, la soluia problemei iniiale. Rezolvarea unei probleme prin programare dinamic presupune urmtorii pai: Se identific subproblemele problemei date; Se alege o structur de date suplimentar, care s rein soluiile subproblemelor; Se caracterizeaz substructura optimal a problemei printr-o relaie de recuren; Pentru a determina soluia optim, se rezolv relaia de recuren n mod bottom-up (se rezolv subproblemele n ordinea cresctoare a dimensiunii lor).

Atestat informatic PROGRAMARE DINAMIC

Probleme de alocare unidimensional


Presupunem ca avem la dispoziie o anumit resurs economic. Termenul poate reprezenta, dupa caz, un anume tip de materii prime, for de munc, energie, bani sau un anumit serviciu etc. Se produce un CONFLICT de INTERESE din faptul ca aceast resurs poate fi utilizat n MAI MULTE MODURI. Un mod particular de utilizare a resursei n cauz se va numi n continuare ACTIVITATE. Ca urmare a utilizrii resursei ntr-o activitate sau alta rezult un anumit VENIT. i acest termen are o sfer foarte larg putnd desemna un produs finit, o sum de bani sau pur i simplu satisfacia. Mrimea venitului depinde de cantitatea de resurse alocat activitii respective, dar i de specificul acesteia. n continuare vom adopta urmatoarele ipoteze simplificatoare: 1) Venitul rezultat din diferitele activiti poate fi msurat cu o unitate de masura comuna. 2) Venitul rezultat dintr-o activitate nu depinde de alocrile fcute n alte activiti. 3) Venitul total este egal cu suma veniturilor individuale. Problema fundamental const n a repartiza resursa ntre activitile concurente de aa manier nct venitul total sa fie MAXIM.

Atestat informatic PROGRAMARE DINAMIC

Un model matematic
Ordonm ntr-un mod oarecare activitile i le numerotm: 1, 2,, N. n continuare, fiecare activitate va fi identificat prin numrul su de ordine. Asociem fiecrei activiti i o FUNCIE DE UTILITATE gi reprezentnd DEPENDENA VENITULUI su de cantitatea de RESURS ALOCAT. Conform ipotezei, gi depinde numaide cantitile xi de resurs alocat activitii i, asa c gi(xi) va reprezenta venitul obinut din activitatea i ca urmare a alocrii cantitii xi. Indicele i din simbolul gial funciei de utilitate este menit s arate c venitul rezultat din activitatea i depinde nu numai de volumul de resurse alocat, dar i de specificul acestei activiti (altfel spus, funciile de utilitate pot diferi de la o activitate la alta). n diferitele situaii practice functia gi poate fi dat: printr-o expresie ANALITIC, de exemplu: gi(xi)=ai xi+bi, cu ai, bi constante (CAZ LINIAR); sau gi(xi)=aixi2+bi xi (CAZ NELINIAR-PATRATIC). printr-o lista de VALORI NUMERICE corespunztoare unor NIVELE ale resursei alocate Venitul total rezultat n urma alocrii resursei n cele N activiti va fi dat de expresia: V(x1,x2,,xN) = g1(x1) + g2(x2) ++ gN(xN) . Maximizarea functiei V se impune, datorit faptului c resursa se afl disponibil ntr-o cantitate limitat S. Astfel, alocrile fcute diferitelor activiti trebuie sa satisfac cerinele: x1 + x2 + +xN=S (2) xi 0, i = 1,,N (3) Obiectivul nostru este acela de a maximiza funcia V(x1,x2,,xN) pe multimea tuturor ALOCRILOR (x1,x2,,xN) care satisfac restricia i condiiile de nenegativitate.
10

Atestat informatic PROGRAMARE DINAMIC

Posibiliti de rezolvare
3.5. Rezolvarea problemelor de alocare unidimensionale (P) prin (PD) Ideea metodei const n a ngloba problema (P) ntr-o FAMILIE de probleme de alocare difereniate prin numrul de activitii cantitatea de resurs disponibil. n loc de a privi STATIC procesul alocarii disponibilului S ntre cele N activiti, aceasta nsemnnd considerarea diferitelor variante de repartiie (x1,,xN) i clasificarea lor dup venitul posibil de realizat, vom adopta un punct de vedere DINAMIC n care alocrile se fac UNA CATE UNA. Mai precis, vom determina venitul maxim care se obine efectund o alocare numai n PRIMA activitate, dup care vom afla care este venitul maxim rezultat din efectuarea unei alocri n PRIMELE DOU activiti, apoi n PRIMELE TREI, s.a.m.d. n toate aceste procese PARIALE cantitatea de resurs alocat va fi VARIABIL dar nedepind plafonul maxim S - aa c rezultatele nu vor fi simple mrimi numerice, ci niste FUNCTII reprezentnd DEPENDENA VENITULUI MAXIM fa de volumul de resurs alocat. n termeni mai precii, pentru fiecare ntreg 1 n N i fiecare 0 s S vom considera problema de alocare unidimensional similar cu (P): (max)V(x1,,xn) = g1(x1) ++ gn(xn) Pn(s) x1 ++ xn = s xi 0, i=1,, n Cu noua notatie (P) devine PN(S). Vom nota cu fn(s) maximul obiectivului problemei Pn(s), adic venitul maxim rezultat dinalocarea cantitii s n regiunile (activitile) 1, 2,, n: fn(s) = MAX *g1(x1) ++ gn(xn)+, x1 ++ xn = s i xi 0 (4) Rezult c maximul obiectivului problemei originale (P) este fN(S). Notm c pentru n fixat i s variabil fn este o FUNCIE de o singur variabil s al crei domeniu de valori admisibile este intervalul [0, S]. Este evident ca pentru n=1, f1=g1, adic: f1(s) = g1(s) pentru () 0 s S
11

Atestat informatic PROGRAMARE DINAMIC


Pentru n>1 valorile funciei fn se deduc pe baza urmtoarei relaii de RECUREN: Pentru 0 s S fn(s) = MAX*f n-1(s-xn)+gn(xn)+, 0 xn s (5) ECUAIA FUNDAMENTAL A PD Demonstratia relatiei de recurenta (5) relev urmtorul principiu general cunoscut sub numele de PRINCIPIUL DE OPTIMALITATE al lui BELLMAN. O strategie OPTIM are proprietatea c oricare ar fi STAREA INIIAL i DECIZIA INIIAL, deciziile rmase constituie o strategie OPTIM n raport cuSTAREA care rezult din prima decizie. ALGORITM DE REZOLVARE A PROBLEMEI (P) PRIN PD Etapa I(INIIALIZARE): Pentru fiecare 0 s S definim f1(s) = g1(s) Etapa n (1<n<N): Definim funcia fn n fiecare 0 s S dup formula (5) i notm cu xn*(s) acea valoare a variabilei xn n care se realizeaz EFECTIV maximul din (5).. : : Etapa N: Calculm f N(S)= max [fN-1(S-xn)+gN(xN)] 0 xN S Etapa final (de determinare a alocrii OPTIMALE (x1*,,xN*)). Componentele acesteia se determina DIN APROAPE N APROAPE de la sfrit la nceput astfel: xN*=xN*(S) Pentru () 1 n N xn*=xn*(s - sn), unde sn=xn+1*++xN*
12

Atestat informatic PROGRAMARE DINAMIC

Probleme clasice care se rezolv prin programare dinamic


1.Cel mai lung subsir crescator
Fie un ir de n numere naturale. Se cere s se gseasc cel mai lung subir cresctor al su. Exemplu: irul {2, 4, 3, 5, 3, 6} are cel mai lung subsir crescator de lungime 4: {2, 4, 5, 6} sau {2, 3, 5, 6}. Rezolvare: Soluia problemei nu este unic, dar lungimea maxim a subirului cresctor, da. Vom nota cu L[k] lungimea celui mai lung subir cresctor care ncepe de la poziia k i pn la sfritul irului iniial. Calculm, pe rnd, L[n], L[n-1], L[n-2] L[2], L[1]. Lungimea celui mai lung subir cresctor va fi dat de cea mai mare valoare a lui L. L[n] = 1 L[k] = 1+ max {L[i ], unde k<in i v[k]v[i ]}, k=n-1,1 Code: #include<fstream.h> int v[10000],n,i,L[1000],max,mx,k,t; int main(){ fstream f("subsir.txt",ios::in); for(i=1;i<=n;i++) f>>v[i]; L[n]=1; //subsir maxim de lung 1 for(k=n-1;k>0;k--) {mx=0; for(i=k+1;i<=n;i++) if(v[i]>=v[k] && L[i]>mx) mx=L[i]; L[k]=mx+1; if(L[k]>max) {max=L[k]; t=k;}
13

Atestat informatic PROGRAMARE DINAMIC


} cout<<"lungimea maxima:"<<max; //afisarea subsirului cout<<endl<<v[t]<<' '; for(i=t+1;i<=n;i++) if ((v[i]>=v[t]) && (L[i]==max-1)) {cout<<v[i]<<' '; max--;} return 0; }

14

Atestat informatic PROGRAMARE DINAMIC 2. Subir comun maximal


Fie X=(x1, x2, ..., xn) i Y=(y1, y2, ..., ym) dou iruri de n, respectiv m numere ntregi. Determinai un subir comun de lungime maxim. Exemplu Pentru X=(2,5,5,6,2,8,4,0,1,3,5,8) i Y=(6,2,5,6,5,5,4,3,5,8) o soluie posibil este: Z=(2,5,5,4,3,5,8) Rezolvare: Notm cu Xk=(x1, x2, ..., xk) (prefixul lui X de lungime k) i cu Yh=(y1, y2, ..., yh) prefixul lui Y de lungime h. O subproblem a problemei date const n determinarea celui mai lung subir comun al lui Xk, Yh. Notm cu LCS(k,h) lungimea celui mai lung subir comun al lui Xk, Yh. Utiliznd aceste notaii, problema cere determinarea LCS(n,m), precum i un astfel de subir. Pentru a reine soluiile subproblemelor vom utiliza o matrice cu n+1 linii i m+1 coloane, denumit LCS. Linia i coloana 0 sunt iniializate cu 0, iar elementul LCS[k][h] va fi lungimea celui mai lung subir comun al irurilor Xk i Yh. Vom caracteriza substructura optimal a problemei prin urmtoarea relaie de recuren: lcs[k][0]=lcs[0][h]=0, k din {1,2,..,n}, h din {1,2,..,m} lcs[k][h]=1+lcs[k-1][h-1], dac x[k]=y[h] max{lcs[k][h-1], lcs[k-1][h]}, dac x[k]<>y[h]

Code: #include<fstream.h> int x[100],y[100],n,m; int lcs[100][100],max; void rezolva(){ for(int k=1;k<=n;k++) for(int h=1;h<=m;h++) if(x[k]==y[h]) lcs[k][h]=1+lcs[k-1][h-1]; else if (lcs[k-1][h]>lcs[k][h-1]) lcs[k][h]=lcs[k1][h]; else lcs[k][h]=lcs[k][h-1]; }
15

Atestat informatic PROGRAMARE DINAMIC


void afiseaza_solutie_max(int k,int h){ if(lcs[k][h]) if(x[k]==y[h]) {afiseaza_solutie_max(k-1,h-1); cout<<x[k]<<' ';} else {if (lcs[k][h]==lcs[k-1][h]) afiseaza_solutie_max(k-1,h); else if (lcs[k][h]==lcs[k][h-1]) afiseaza_solutie_max(k,h-1); } } int main(){ ifstream f("lcs.txt",ios::in); f>>n>>m; for(int i=1;i<=n;i++) f>>x[i]; for(i=1;i<=m;i++) f>>y[i]; rezolva(); afiseaza_solutie_max(n,m); return 0; }

16

Atestat informatic PROGRAMARE DINAMIC 3. Sum maxim n triunghi


Fie triunghiul format din n linii (1<n<=100), fiecare linie coninnd numere ntregi din domeniul [1,99], ca n exemplul urmtor:

Determinai cea mai mare sum de numere aflate pe un drum ntre numrul de pe prima linie i un numr de pe ultima linie. Fiecare numr din acest drum este situat sub precedentul, la stnga sau la dreapta acestuia. Rezolvare 1. Vom reine triunghiul ntr-o matrice ptratic T, de ordin n, sub diagonala principal. Subproblemele problemei date constau n determinarea sumei maxime care se poate obine din numere aflate pe un drum ntre numrul T[i ][j], pn la un numr de pe ultima linie, fiecare numr din acest drum fiind situat sub precedentul, la stnga sau la dreapta sa. Evident, subproblemele nu sunt independente: pentru a calcula suma maxim a numerelor de pe un drum de la T[i ][j] la ultima linie, trebuie s calculm suma maxim a numerelor de pe un drum de la T[i+1][j] la ultima linie i suma maxim a numerelor de pe un drum de la T[i+1][j+1] la ultima linie. 2. Pentru a reine soluiile subproblemelor, vom utiliza o matrice suplimentar S, ptratic de ordin n, cu semnificaia S[i ][j]= suma maxim ce se poate obine pe un drum de la T[i ][j] la un element de pe ultima linie, respectnd condiiile problemei. Soluia problemei va fi S[1][1]. 3. Relaia de recuren care caracterizeaz substructura optimal a problemei este: S[n][ i]=T[n][i ], i din {1,2,...,n} S[i ][j]=T[i ][j]+max{S[i+1][j], S[i+1][j+1]} Secvena de program este: Code: for (i=1; i<=n; i++) S[n][i]=T[n][i]; for (i=n-1; i>=1; i--) for (j=1; j<=i; j++) {if (S[i+1][j]<S[i+1][j+1]) S[i][j]=T[i][j]+S[i+1][j+1]);
17

Atestat informatic PROGRAMARE DINAMIC


else S[i][j]=T[i][j]+S[i+1][j];}

18

Atestat informatic PROGRAMARE DINAMIC

Tehnici avansate de programare dinamic


Reducerea loop-ului interior

n problemele de programare dinamic de multe ori prima complexitate obinut nu este optim. Mai exact, recurena obinut iniial poate fi mbuntit sau calculat mai rapid. De multe ori, pentru aceasta se optimizeaza loop-ul interior din calculul recurenei. Acest lucru se poate face bazat fie pe observaii specifice problemei, fie pe tehnici mai clasice. S considerm urmtoarea problema:

Problema 1: Drilling (Algorithmic Engagements 2009, Round 3)


Se da un ir de N numere naturale (a1, a2, ..., aN). Se tie c un prefix al acestui ir (adic o subsecven de la nceputul irului, posibil nul) este defect. Toate elementele din acel prefix (i doar din acel prefix) sunt defecte. Se tie de asemenea c se poate testa orice poziie i n timp ai pentru a afla dac este defect. S se determine timpul minim n cazul cel mai defavorabil pentru aflarea lungimii maxime a prefixului irului care este defect.

Exemplu:
Pentru secvena de numere (8 24 12 6), rspunsul ar fi 42. Astfel, se testeaz mai nti poziia 2. Dac nu este defect, mai rmne de testat poziia 1. Altfel, n cel mai ru caz mai trebuie testate poziiile 3 i 4, aducnd timpul total la 24 + 12 + 6 = 42.

Rezolvare:
O s ncepem prin a da alt mbrcminte problemei. Astfel, s ne imaginm c am construi un arbore binar de cutare pe irul dat, avnd drept chei poziiile din ir, i drept valori auxiliare valorile poziiilor respective din ir (un nod cu cheia i va avea valoarea auxiliara ai. Acum, exist muli arbori binari de cutare posibile pentru irul iniial. Totui, s presupunem c am construit unul, pe care l considerm de acum ca fixat. Atunci, strategia noastra de testare a elementelor din ir se va desfura conforma arborelui. Astfel, testm mai nti n poziia din rdcin. Dac poziia respectiv este defect, ne ducem n fiul drept. Altfel, mergem in fiul stng. n ambele cazuri se reia procedeul. Cutarea se termin cand nu mai putem merge ntr-un fiu.

19

Atestat informatic PROGRAMARE DINAMIC


S observm c dac considerm costul unui drum n arbore de la rdcin la o frunz ca suma valorilor auxiliare din drumul (unic) respectiv, atunci se observ c costul strategiei bazate pe acest arbore este chiar costul maxim al unui drum ! De acum ncolo, vom defini costul unui arbore ca costul maxim al unui drum de la rdcin la o frunz. S exemplificm pe irul din exemplu:

Se observ c costul maxim al unui drum este 42, minim posibil, exact ca rspunsul din exemplu. Deci, am redus problema la construirea unui arbore binar de cutare, care are costul minim. Acum devine clar o prim soluie folosind programarea dinamic. Astfel, s presupunem c notm cu opt[ i ][ j ] costul minim al unui arbore construit pe cheile din [i,j](prin asta notm (i, i + 1, ..., j). Fie r[ i ][ j ] poziia rdcinii arborelui cu costul minim. n cazul existenei mai multor rdcini posibile, se alege cea mai din stnga. Cum am putea construi arborele de cost minim pe cheile [i,j] tiind rspunsul pentru instanele mai mici (subsecvenele de lungime mai mic ca j-i+1 pe [i,j])? (!) Simplu, arborele de cost minim pe [i,j] se obine alegnd drept rdcin n mod optim o poziie k, cu , la care se pune ca fiul stng arborele optim pentru [i, k-1], i ca fiu drept arborele optim pentru [k+1, j]. Relaia de recuren devine:
opt[ i ][ j ] = r[ i ][ j ] =

Fig. 1: n parantez sunt valorile auxiliare ataate cheilor

Avem opt[i][j] = 0, pentru j < i. Cazurile de baz sunt opt[i][i] = ai si r[i][i] = i. Se observ c rspunsul problemei se afl n opt[1][N]. Astfel, am obinut o soluie n timp O(N^3). Aici trebuie remarcat asemnarea acestei probleme cu cea a determinrii arborelui de cutare optim, atunci cand se dau probabilitile relative de cutare a cheilor. Aceast problem se numete 'Optimal Binary Search
20

Atestat informatic PROGRAMARE DINAMIC


Tree' n literatura de specialitate i poate fi gsit i n Introduction to Algorithms. n aceasta problem, se poate aplica o reducere a complexitii datorit faptului c pentru a reduce complexitatea la O(N^2). Aceast proprietate nu se poate aplica din pcate i n cazul problemei noastre. Pn acum avem urmtorul pseudocod pentru calculul recurenei:
1 2 3
pentru len = 1, N execut Algoritmul este: iniializeaz opt i r

4 5 6 7 8 9 10 11 12

pentru i = 1, N - len + 1 execut j = i + len - 1; opt[i][j] = inf; pentru k = i, j execut dac opt[i][j] < a[k] + max(opt[i][k-1], opt[k+1][j]) atunci opt[i][j] = a[k] + max(opt[i][k-1], opt[k+1][j]); r[i][j] = k; sfrit_pentru; sfrit_pentru; sfrit_pentru;

13 14 15 16
return opt[1][N]; Sfrit.

Acum ca s obinem o complexitate mai bun, innd cont i de numele capitolului, vom ncerca s reducem complexitatea loop-ului interior (cel dup k). S ne uitm mai atent la acest loop. Fie un interval [i,j] fixat. Acum, opt[i][j] < opt[i][j'], pentru orice j < j'. Similar, opt[i][j] < opt[i'][j], pentru orice i' < i. Atunci, exist un indice k, astfel nct max(opt[i][o-1], opt[o+1][j]) = opt[o+1][j], pentru orice , i max(opt[i][o-1], opt[o+1][j]) = opt[i][o-1]pentru orice . Dup cum tim, trebuie s
21

Atestat informatic PROGRAMARE DINAMIC


aflm . S definim acum 3 matrici auxiliare: to_left[ i ][ j ] = ai + opt[ i + 1 ][ j ] to_right[ i ][ j ] = aj + opt[ i ][ j - 1 ] inter[ i ][ j ] = Se observ c inter[i][j] nu reprezint altceva dect indicele k tratat in paragraful precedent. De asemenea, inter poate fi calculat n O(N^2) observnd o proprietate simpl: . Aceasta reiese imediat din monotonia lui optdiscutat mai sus. Pseudocodul pentru calculul lui inter este: 1 2 3 4 5 6 7 8 9 10 11 iniializeaz opt, inter i r pentru len = 1, N execut pentru i = 1, N - len + 1 execut j = i + len - 1; calculeaz opt[i][j] pentru k = inter[i][j-1], inter[i+1][j] execut dac opt[i][k-1] <= opt[k+1][j] atunci inter[i][j] = k; sfrit_pentru; sfrit_pentru; sfrit_pentru;

12 Sfrit. 13 De menionat c trebuie avut grij la cazul cnd len este mai mic ca 3. Pseudocodul prezentat mai sus este orientativ. S vedem acum de ce aceast modalitate de calcul a lui inter duce la o complexitate de O(N^2). Alternativ, se poate cuta binar inter[i][j] pentru orice [i,j].

22

Atestat informatic PROGRAMARE DINAMIC


Acum vine ideea decisiv pentru a reduce complexitatea loop-ului: pentru a afla opt[i][j], este suficient s aflm minimul dintre 2 valori:

i

Mai exact, opt[ i ][ j ] = Diferena fa de prima recurent este evident: acum trebuie s aflm minimul pe un interval de linii, n cazul to_left, i minimul pe un interval de coloane, n cazul to_right. Att aflarea mnimului pe un interval, ct i update-ul acestei valori se poate face n O(logN), folosind arbori de intervale. Astfel, am redus complexitatea loopului interior de la O(N) la O(logN).

Programare dinamic folosind mti de bii i codificri

k-are

Unele probleme de programare dinamica au drept component a strii unei subprobleme o mulime de elemente care fac parte din subproblem. Astfel, subproblema nu este o reducere a problemei iniiale la un subset continuu de elemente (1..i sau i..j) ci la un subset oarecare. n acest caz, codificm submulimea curent n stare, ca vector sau ca numr ntreg. Dac dimensiunea submulimii este suficient de mic putem folosi un ntreg pentru a codifica aceast informaie astfel: Fie mulimea A = { x1, x2, ... xn }. Atunci masca de bii a unei partitii a lui A, MASK, va avea bitul i egal cu 1 dac i numai dac xi apartine partitiei. Desigur, aceast reprezentare duce la o complexitate direct proporional cu 2 card(A). Putem intui dac trebuie s folosim o astfel de rezolvare din limitele valorilor de intrare; pentru N cu valori cuprinse ntre 10-20, deducem c trebuie s cutm o astfel de soluie. Pentru multe probleme, fiecare element poate face parte din subproblem n mai mult de 2 feluri. De exemplu, dac starea reprezint o linie de maxim K elemente dintr-o matrice iar fiecare element de pe linie poate avea valorile 0, 1 sau 2 atunci exist 3^K variante distincte posibile pentru o astfel de linie. Un alt exemplu este o problem de optimizare n care fiecare element (participant) se poate afla n una din cteva stri (dac N persoane trebuie s treac un pod peste un ru, cele 3 stri pot fi: pe malul stng, pe pod i pe malul drept).
23

Atestat informatic PROGRAMARE DINAMIC Problema 1: Be a Smart Raftsman (SGU)


Suntei membri ai unui echipaj de rafting care const din N 10 participani. Putei naviga pe ru, i scopul vostru este s trecei de M 1000 cureni consecutivi i s ajungi de la punctul de nceput la punctul de sfrit n timp minim. Cel de-al i-lea curent se caracterizeaz prin greutatea critic ci, iar al j-lea participant este caracterizat greutatea sa wj. n cazul n care pluta trece prin al i-lea curent cu oameni la bord cu greutatea total mai mare de ci, ea se rstoarn. Aceast parte a rafting-ului este cea mai interesant, dar este nevoie de timp suplimentar pentru a te urca pe plut dup rsturnare. Deci, uneori este mai bine s se urce un grup de oameni cu greutate total care nu depete greutatea critic a plutei, n timp ce restul parcurg distana pe jos. Mai formal, vom considera M + 1 puncte P0, P1 ,..., PM, n cazul n care P0 este nceputul i PM este punctul final. Fiecare dintre punctele intermediare Pi (1 i M-1) este situat ntre i-lea i (i + 1)-lea curent. Dac pluta se rstoarn, sunt necesare Di minute pentru a ajunge de la Pi-1 la Pi, altfel sunt necesare di minute. Cel de-al j-lea participant poate merge pe jos de la Pi1

la Pi n tj minute, iar pentru a se urca sau a cobor din plut are nevoie

de sj minute. nainte de fiecare curent, grupul vostru este mprit n dou pri. Prima parte trece prin curent cu pluta, iar a doua parte merge pe mal spre urmtorul punct. Cei care ajung primii i ateapt pe toi ceilali. Apoi, unii participani care sunt pe plut se pot da jos, n timp ce ali participanti care sunt pe mal se pot urca pe plut. Aceast activitate ncepe cnd ajung pluta i toi cei de pe mal la punctul stabilit. Timpul total necesar pentru aceast aciune este egal cu suma valorilor sj pentru toate persoanele care schimb locul (persoanele urc i coboar pe rnd). Nimeni nu poate ncepe deplasarea la urmtorul punct, pn cnd nu s-au mutat toi membrii. ncepei de la punctul de P0 cu tot grupul pe mal i trebuie s terminai la punctul PM cu toi participanii i pluta pe mal. Nu avei voie s prsii pluta
24

Atestat informatic PROGRAMARE DINAMIC


la nceput sau ntr-un un punct intermediar i s mergei pe jos spre linia de sosire fr ea. Avei posibilitatea s urcai tot grupul de salvare n plut, dar nu putei lsa pluta goal n timpul cltoriei printr-un curent. Sarcina voastr este s calculai timpul minim necesar pentru a ajunge la linia de sosire.

Rezolvare:
Din enun intuim c o component a strii trebuie s fie submulimea participanilor aflai n plut n punctul curent, intuiie confirmat de numrul mic al participanilor (N 10). Exist 210 astfel de variante, din care vom exclude varianta n care pluta este goal. O stare a problemei, reprezentnd soluia unei subprobleme, va fi alctuit din poziia curent a plutei (punctul Pi unde se afl) i submulimea oamenilor care se afl n plut. Observm c de la punctul precedent pluta a traversat curentul, au urcat nite oameni apoi au cobort alii; aceti pai contribuie la timpul total i reprezint 2 pri ale recurenei soluiei. Dac notm cu Tm[i][S] timpul minim necesar pentru a ajunge n punctul icu submulimea S a oamenilor din plut, putem descrie problema prin relaiile:

unde Tu[S'][S] este timpul necesar pentru urcarea participanilor care se gsesc n mulimea S dar nu fac parte din mulimea S' (aceti participani alctuiesc mulimea Su),Tc[S'][S] este timpul necesar pentru coborrea participanilor din plut (participanii din mulimea Sc), iar Tt[i][S] este timpul necesar traversrii curentului de la punctul i-1 la punctul i dac n plut se afl participanii din S. Formulele pentru valorile Tu, Tc i Tt sunt:

Calcularea direct a soluiei pe baza recurenelor de mai sus are complexitatea O(M*N*2N + M*2N*2N). Primul termen al complexitii este dat de calcularea valorilor Tt, iar al doilea de calcularea matricii Tm. Valoarea rezultatului nu depinde de ordinea n care urc i coboar participanii din plut, deci o putem alege noi. Dac aranjm ordinea n doi pai astfel nct n primul pas doar s urce, apoi n al doilea pas doar s coboare, vom obine o mbuntire semnificativ a timpului de execuie. Se observ c dac am efectua doar adugri de elemente (daca n plut s-ar putea doar urca persoane, fr s poat cobor), atunci i deci reprezentarea n cod binar a mulimii S' ar avea o valoare mai mic dect
25

Atestat informatic PROGRAMARE DINAMIC


reprezentarea binar a mulimii S. Dac ar avea voie doar s coboare persoane, relaia ar fi invers. De aceea, vom ordona operaiile de urcare i coborre astfel nct s aib loc nti toate urcrile (astfel nct s putem parcurge cresctor valorile binare ale mulimilor) apoi toate coborrile (astfel nct s putem efectua a doua parcurgere, n ordine descresctoare). Introducem matricea auxiliar Tum[i][S] care reprezint timpul minim necesar la care se poate afla pluta n punctul i cu mulimea S de oameni, dac dup ultima traversare nu a cobort nc nici o persoan. Starea S a fost obinut prin una sau mai multe urcri de persoane n plut sau direct prin traversarea curentului. Recurenele pentru Tum[i][S]sunt: Am eliminat calcularea matricilor Tu, Tc. Toate matricile rmase au dimensiunea Mx2N iar calcularea fiecrui element necesit un timp O(N), deci soluia astfel obinut are complexitatea O(M*N*2N).
1 2 3 4
pentru fiecare S n ordine cresctoare a reprezentrii binare execut iniializeaz toate Tm[0]; pentru i = 1, M execut calculeaz toate Tt[i];

5 6 7 8 9 10 11 12 13

Tum[i][S] = Tm[i-1][S] + Tt[i][S]; pentru fiecare j din S execut Tum[i][S] = min(Tum[i][S], Tum[i][S - j] + s[j]); sfrit pentru; sfrit pentru; pentru fiecare S n ordine descresctoare a reprezentrii binare execut Tm[i][S] = Tum[i][S]; pentru fiecare j care nu exist n S execut Tm[i][S] = min(Tm[i][S], Tm[i][S + j] + s[j]); sfrit pentru;

14 15 16 17

sfrit pentru; sfrit pentru; returneaz Tm[M][0];

26

Atestat informatic PROGRAMARE DINAMIC


Mai exist dou optimizri de spaiu pe care le putem efectua n soluia prezentat. Putem elimina matricea Tum, calculnd toate valorile direct pe matricea Tm, deoarece aceasta va fi parcurs n ambii pai n cte o singur direcie. A doua optimizare se bazeaz pe observaia c nu avem niciodat nevoie de alte linii n afar de ultimele 2 (i si i-1), deci putem nlocui matricea cu 2 vectori de dimensiune 2N. Valorile Tt pot fi calculate n cadrul primei bucle, reducnd astfel spaiul necesar soluiei la O(2N).

Programare dinamica pe mai multe dimensiuni

Unele probleme nu pot fi mprite n subprobleme doar dup o submulime a datelor de intrare ale problemei curente, ci trebuie adugate criterii suplimentare. De multe ori, aceste valori sunt similare sumei din problema rucsacului, dar calculele se fac pe 2 sau chiar mai multe dimensiuni, obinnd o descriere destul de complex a unei subprobleme. De exemplu, o subproblem poate fi reprezentat printr-un vector de valori de dimensiuni variate.

Problema 1: Ugly numbers (Google Code Jam 2008, Round 1C)


Un numr se numete urt dac este divizibil prin oricare dintre numerele prime de o singur cifr, mai exact 2, 3, 5, 7. Se d un ir de N cifre. ntre fiecare 2 cifre consecutive se poate insera unul dintre semnele + sau -. Dac nu se insereaz un semn, cele 2 cifre sunt concatentate astfel nct s se obin un numr. Pentru un ir dat de cifre si o variant de a aduga semne, prin evaluarea expresiei matematice obinute prin inserarea semnelor se obine un numr, numit numrul generat. S se calculeze cte dintre cele 3N-1 variante de a insera semnele genereaz numere urte. Exemplu: Pentru cifrele 123456, expresia 1+234-5+6 = 236 care este numr urt, iar 123+4-56 = 71 nu este numr urt.

27

Atestat informatic PROGRAMARE DINAMIC


Rezolvare: Observm c un numr este urt dac d cel puin o dat restul 0 la mprirea la numerele 2, 3, 5, 7. O abordare a problemei este parcurgerea tuturor variantelor de inserare i calcularea celor 4 resturi, verificnd dac cel puin unul este 0. Vom folosi aritmetica modular pentru a obine o soluie mai eficient a problemei. Vom mpri cele 3N-1 variante n clase, fiecare clas reprezentnd toate numerele care dau resturile (r2, r3, r5, r7) la mprirea la cele 4 numere. Calculnd pentru fiecare clas numrul de numere generate, rezultatul final va fi suma valorilor calculate pentru toate clasele pentru care cel puin un rest este egal cu 0. Intuim c putem utiliza clasele de resturi ntr-o soluie cu programare dinamic. Vom utiliza irul auxiliar V[i][j] reprezentnd numrul format prin concatenarea cifrelor de pe poziiile consecutive i, i+1, ..., j din irul dat, fr inserarea unor semne ntre cifre. De exemplu, V[2][4] = 234. Dac notm cu C[i][r2][r3][r5][r7] numrul de variante formate cu primele i cifre pentru care clasa de resturi este (r2, r3, r5, r7). Observm c orice astfel de variant se obine dintr-o variant cu j < i cifre dup care adugam + sau - apoi concatenm restul cifrelor pn la i fr semne. Dac resturile pentru varianta cu j cifre erau (r2, r3, r5, r7) atunci resturile pentru varianta nou (r'2, r'3, r'5, r'7) sunt:

Atunci numrul total de variante cu i cifre este suma dup toate valorile j ale numerelor de variante cu j cifre:
?

1 2 3 4 5 6 7 8

pentru i = 1, N execut V[i][i] = A[i] pentru j = i + 1, N execut V[i][j] = V[i][j - 1] + A[i]; sfrit pentru; sfrit pentru;

C[0][0][0][0][0] = 1; S = 0;

28

Atestat informatic PROGRAMARE DINAMIC


9 10 11 12 13
C[i][r_2][r_3][r_5][r_7] = 0; pentru i = 1, N execut pentru r_2 = 0, 1 execut pentru r_3 = 0, 2 execut pentru r_5 = 0, 4 execut pentru r_7 = 0, 6 execut

14 15 16 17 18 19 20 21 22 23

pentru j = 0, i - 1 execut

C[i][r_2][r_3][r_5][r_7] += C[j][(r_2 + 2 - V[j+1][i])%2)][(

[(r_5 + 5 - V[j+1][i])%5)][( C[i][r_2][r_3][r_5][r_7] += C[j][(r_2 + V[j+1][i])%2)][(r_3 [(r_5 + V[j+1][i])%5)][(r_7 sfrit pentru;

dac i == N i (r_2 == 0 sau r_3 == 0 sau r_5 == 0 sau r_7 == 0) S += C[i][r_2][r_3][r_5][r_7] sfrit pentru; sfrit pentru;

24 25 26 27 28 29 30 31

sfrit pentru; sfrit pentru; sfrit pentru; sfrit pentru; returneaz S;

O problem n abordarea precedent este calcularea matricii V, ale crei valori devin foarte mari i ies din precizia tipurilor de date disponibile. O soluie posibil este calcularea cte unei matrici de sume modulo R pentru fiecare rest R, adica a 4 matrici V2, V3, V5 si V7 ale resturilor mpririi valorilor lui V la cele 4 numere prime. Folosind aritmetica modular, aceste matrici pot fi calculate direct, fr a calcula V. Alt variant este utilizarea
29

Atestat informatic PROGRAMARE DINAMIC


teoremei chineze a resturilor, prin care un sistem de ecuaii modulo cteva numere prime ntre ele poate fi redus la o ecuaie modulo produsul numerelor date. n cazul nostru, numerele sunt prime, deci putem aplica teorema. Astfel, putem reduce problema resturilor mpririi la (2, 3, 5, 7) la problema restului mpririi la 2*3*5*7=210, deci fiecare clasa (r2, r3, r5, r7) se reduce la un rest R < 210. Modificm pseudocodul astfel:
1 2 3 4 5 6
sfrit pentru; pentru i = 1, N execut V[i][i] = A[i] % 210 pentru j = i + 1, N execut V[i][j] = (V[i][j - 1] + A[i]) % 210; sfrit pentru;

7 8 9 10 11 12 13 14 15 16 17
dac i == N i (r % 2 == 0 sau r % 3 == 0 sau r % 5 == 0 sau r % 7 == 0) C[0][0] = 1; S = 0; pentru i = 1, N execut pentru r = 0, 209 execut C[i][r] = 0; pentru j = 0, i - 1 execut C[i][r] += C[j][(r + 210 - V[j+1][i]) % 210]; C[i][r] += C[j][(r + V[j+1][i]) % 210]; sfrit pentru;

18 19 20 21 22 23

S += C[i][r]; sfrit pentru; sfrit pentru; sfrit pentru; returneaz S;

30

Atestat informatic PROGRAMARE DINAMIC


Problema 2: Doipatru
Membrii Lotului Naional de Informatic sunt foarte mndri de noul joc inventat de ei, pe care l-au denumit asemntor cu o problem de la Olimpiada Internaional de Informatic din anul 2001, care le-a plcut foarte mult. Astfel, jocul se numete DoiPatru. Pentru acest joc se folosesc 3 N 30 grmezi, fiecare coninnd cel puin 0 i cel mult 4 bile. Numrul total de bile din toate grmezile este 2*N. Doi juctori mut alternativ. Atunci cnd ii vine rndul, fiecare juctor este obligat s efectueze o mutare valid. O mutare valid const din alegerea a dou grmezi, dintre care prima grmad are mai multe bile dect cea de a doua. Juctorul ia o bil din prima grmad i o mut n cealalt. Mutarea se consider valid, doar dac numrul de bile rezultat n a doua grmad dup mutarea bilei nu este mai mare dect numrul de bile rmas n prima grmad. Jocul se termin atunci cnd nu mai poate fi efectuat nici o mutare valid (dac v gndii puin, vei constata c acest lucru se ntmpl atunci cnd fiecare grmad conine dou bile). Ctigtorul jocului este desemnat cel care deine mai multe grmezi la sfritul jocului. Bineneles, dac cei doi juctori dein un numr egal de grmezi, jocul se consider a fi remiz. Un juctor deine o grmad dac grmada are dou bile, iar acest numr (de dou bile) a rezultat n urma unei mutri efectuate de juctorul respectiv. De exemplu, dac un juctor alege o grmad cu 4 bile i una cu o bil, n urma efecturii mutrii, el va deine cea de-a doua grmad (care va avea
31

Atestat informatic PROGRAMARE DINAMIC


dou bile), dar prima nu va aparine deocamdat nici unuia dintre juctori. Dac alege o grmad cu 3 bile i una cu 0 bile, juctorul va deveni proprietarul primei grmezi, deoarece, n urma mutrii efectuate, grmada respectiv va rmne cu dou bile. n cazul n care alege o grmad cu 3 bile i una cu o bil, dup efectuarea mutrii, el va deine ambele grmezi (amndou au acum doua bile). Dac un juctor este proprietarul unei grmezi la un moment dat n timpul jocului, nu nseamn c aceast grmad va rmne n posesia lui pn la sfrit. De exemplu, s presupunem c juctorul 1 deine o grmad cu dou bile i este rndul juctorului 2 s mute. Dac acesta alege o grmad cu 4 bile i grmada cu dou bile ce aparine juctorului 1, dup efectuarea mutrii, ambele grmezi vor avea 3 bile, iar numrul de grmezi aflate n posesia juctorului 1 va scdea cu 1 (grmada deinut de el anterior nu mai aparine nici unuia din cei doi juctori, cci nu mai are doua bile). Dac la nceputul jocului exist unele grmezi avnd dou bile, acestea sunt distribuite n mod egal celor doi juctori. Dac numrul de grmezi cu dou bile este impar, atunci juctorul 2 va primi cu o grmad mai mult dect jucatorul 1. Juctorul 1 este cel care efectueaz prima mutare. Scriei un program care, pentru un N dat i un set de configuraii iniiale ale jocului cu N grmezi, decide rezultatul fiecrei configuraii de joc.

Rezolvare:
ncepem rezolvarea cu observaia c exist 5 tipuri de grmezi (0, 1, 2, 3 i 4 bile), dar pentru fiecare grmad de 2 bile aparine doar unuia dintre juctori, informaie esenial pentru stabilirea ctigtorului. Vom separa deci toate grmezile de 2 bile n 2 categorii: 2A care reprezint grmezile de
32

Atestat informatic PROGRAMARE DINAMIC


2 bile deinute de primul juctor i 2B care sunt grmezile de 2 bile deinute de al doilea juctor. Orice moment al jocului poate fi reprezentat identificnd juctorul care urmeaz la mutare i numrul de bile din fiecare dintre cele N grmezi. O variant alternativ este de a reprezenta numerele de grmezi din fiecare tip, starea fiind reprezentat prin valorile (J, n0, n1, n2A, n2B, n3, n4), unde nkreprezint numrul de grmezi care au k bile (cu excepiile 2A i 2B, descrise nainte). Vom nota prin R[J, n0, n1, n2A, n2B, n3, n4] cel mai bun rezultat pe care l poate obine juctorul care urmeaz (J). Valorile posibile ale rezultatului vor fi alese ca numere ntregi cresctoare, astfel: 0 dac din configuraia curent juctorul nu are nici o ans s ctige, 1 dac juctorul poate obine o remiz i 2 dac juctorul are o strategie sigur de ctig. Fiecare juctor va alege mutarea potrivit astfel nct s maximizeze valoarea R, care este n acelai timp rezultatul cel mai prost pentru cellalt juctor. Vom nota juctorii prin 0 i 1 i putem calcula valoarea optim pentru R astfel: dac (n'0, n'1, n'2A, n'2B, n'3, n'4) reprezint o stare accesibil din starea curent printr-o singur mutare. Observm c rezultatul este cu att mai bun pentru unul dintre juctori cu ct este mai prost pentru cellalt, deci rezultatele sunt invers proporionale, 2pentru J reprezentnd 0 pentru juctorul 1 - J. Fiecare juctor alege mutarea care i va obine rezultatul maxim, acesta fiind corespunztor rezultatului defavorabil (de valoare minim) pentru cellalt. Strile pentru care nu mai exist dect grmezi de tipuri 2A i 2B sunt, conform enunului, stri finale.

Notnd cu S tuplul distribuiei bilelor n grmezi (J, n0, n1, n2A, n2B, n3, n4) atunci vom folosi o notaie echivalent dar mai scurt, R[J, S]. Vom iniializa toate valorile din acest tablou multidimensional la -1 i apoi vom calcula recursiv valorile, obinnd o complexitate polinomial prin memoizare.
1 2 3 4 5
calculR(J, S) dac R[J, S] != -1 atunci returneaz R[J, S]; dac S e stare final returneaz 0, 1 sau 2 n funcie de ctigtor;

33

Atestat informatic PROGRAMARE DINAMIC


6 7 8 9 10 11
R[J, S] = 0; pentru toate strile S' n care se poate ajunge din S printr-o mutare R[J, S] = max(R[J, S], 2 - calculR(1 - J, S')); sfrit pentru; returneaz R[J, S];

La prima vedere, numrul strilor este 2 * (2N)6 = 2 * 606 = 93,312 * 109, deci numrul strilor este prea mare pentru a intra n limite rezonabile de spaiu i timp. Observm totui c toate strile ndeplinesc condiia n0 + n1 + n2A + n2B + n3 + n4 = N. Numrul total al strilor este numrul tuplurilor care ndeplinesc egalitatea i condiiile nk 0. Vom numerota cele 6 tipuri de grmezi prin numere de la 0 la 5. Vom calcula valorile NS[g, b, v] care reprezint numrul de variante de a grupa b bile n g grmezi, astfel nct cea mai mic grmad s aib cel puin mrimea tipului de indice 0 v 5. Notm cu D irul dimensiunilor tipurilor, (0, 1, 2, 2, 3, 4) i observm c o stare descris prin (g, b, v) ori are o grmad de dimensiune Dv ori toate grmezile sunt mai mari dect aceast dimensiune. Atunci recurena de calcul pentru NS este: NS[g, b, v] = NS[g, b, v + 1] + NS[g - 1, b - Dv, v] cu cazul particular NS[0, 0, 5] = 1. Numrul total de stri este valoarea NS[N, 2*N, 0]. Pentru valoarea maxim a lui N din enun, am obinut NS(30) = NS[30, 60, 0] = 8266. Rezult deci c numrul total real al strilor este relativ mic, iar soluia noastr se ncadreaz n limitele de timp i spaiu date. Pentru o stare S dat, numrul strilor posibile S' n care putem ajunge este foarte mic. n cel mai ru caz, perechile de grmezi care reprezint mutarea din pasul curent sunt din mulimea {(0, 2A), (0, 2B), (0, 3), (0, 4), (1, 3), (1, 4), (2A, 4), (2B, 4)}, deci un juctor are maxim 8 mutri posibile de efectuat. Dup ce am ales tipurile de grmezi pe care efectum mutarea, este irelevant alegerea grmezilor cu dimensiunile date, deoarece toate alegerile duc la aceeai stare urmtoare. Un mod de a stoca tabloul R astfel nct spaiul necesar s fie minim este folosirea unui dicionar care s stocheze valorile R[J, n0, n1, n2A, n2B, n3, n4] pentru toate strile valide, folosind astfel doar O(NS(N)) spaiu. Dicionarul poate fi implementat printr-o tabel de dispersie sau arbori binari de cutare, n C++ folosind chiar map sau hash_map dinSTL. Alt opiune este codificarea fiecrei stri S printr-un numr ntreg cuprins ntre 1 i NS(N), caz n care tabloul R poate fi stocat ntr-o matrice de dimensiune 2xNS(N). Asocierea dintre descrierea unei stri (numrul de grmezi de fiecare tip) i numrul su de ordine poate fi precalculat i stocat ntr-un dicionar sau poate fi calculat n O(N)cu ajutorul unor
34

Atestat informatic PROGRAMARE DINAMIC


formule. n primul caz, vom genera prin backtracking toate variantele posibile de stri ntr-o ordine oarecare i vom aloca fiecrei stri cte un numr, stocnd izomorfismul ntr-un dicionar. Varianta mai complex, dar mai elegant presupune stabilirea unei ordini fixe a strilor i apoi folosirea unor tehnici combinatoriale pentru a calcula numrul corespunztor unei stri sau starea corespunztoare unui numr. Vom defini mai formal o stare ca un 6-tuplu (n0, n1, n2A, n2B, n3, n4) i vom ordona toate strile lexicografic (strile sunt ordonate dup n0; n caz de egalitate, sortarea se face dup n1 .a.m.d.). Atunci numrul de ordine al unei stri calculat de formula de mai sus este numrul de stri care au o valoare mai mic pentru n0 plus numrul de stri care au aceeai valoare pentru n0 dar o valoare mai mic pentru n1 .a.m.d.
1
IS = 1;

2 3 4 5 6 7 8 9 10 11

G = N; B = 2 * N; pentru k = 0, 4 execut dac n[k] > 0 atunci pentru i = 1, n[k] execut IS = IS + S[G][B][k + 1]; G = G - 1; B = B - D[k]; sfrit pentru; sfrit pentru; returneaz IS;

12
Operaia invers calculeaz o stare S pe baza valorii I(S) a indicelui strii. ?

1 2 3 4 5 6

G = N; B = 2 * N; pentru k = 0, 4 execut n[k] = 0; ct timp IS > S[G][B][k + 1] execut IS = IS - S[G][B][k + 1];

35

Atestat informatic PROGRAMARE DINAMIC


7 8 9 10 11
n[5] = G; G = G - 1; B = B - D[k]; n[k] = n[k] + 1; sfrit ct timp; sfrit pentru;

12 13

returneaz n;

Aceti algoritmi pot fi integrai n funcia recursiv de calcul a matricii R.

36