Professional Documents
Culture Documents
PROGRAMARE DINAMIC
LUCRARE PENTRU ATESTAREA COMPETENELOR PROFESIONALE
Elev: Arhip Georgiana Profesor ndrumtor: Balacea Georgeta Colegiul Naional Vasile Alecsandri Galai Sesiunea Mai 2013
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
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.
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.
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++)
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).
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
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
14
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
16
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
18
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:
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
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 ] =
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
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
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
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).
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
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
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
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
26
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.
27
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
14 15 16 17 18 19 20 21 22 23
pentru j = 0, i - 1 execut
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
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
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
30
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
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
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
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
35
12 13
returneaz n;
36