Documente Academic
Documente Profesional
Documente Cultură
1. Descriere
2. Probleme clasice
3. Probleme propuse
Descriere
Programare dinamica presupune rezolvarea unei probleme prin descompunerea ei in subprobleme si
rezolvarea acestora. Spre deosebire de divide-et-impera, subproblemele nu sunt disjuncte, ci se
suprapun.
Pentru a evita recalcularea portiunilor care se suprapun, rezolvarea se face pornind de la cele mai mici
subprobleme si folosindu-ne de rezultatul acestora calculam subproblema imediat mai mare. Cele mai
mici subprobleme sunt numite subprobleme unitare. Acestea pot fi rezolvate intr-o complexitate
constanta, ex: cea mai mare subsecventa dintr-o multime de un singur element.
Pentru a nu recalcula solutiile subproblemelor ce ar trebui rezolvate de mai multe ori, pe ramuri
diferite, se retine solutia subproblemelor folosind o tabela (matrice uni, bi sau multi- dimensionala
in functie de problema) cu rezultatul fiecarei subprobleme. Aceasta tehnica se numeste memorizare.
Aceasta tehnica afla valoarea solutiei optime pentru fiecare din subprobleme. Mergand de la
subprobleme mici la subprobleme din ce in ce mai mari ajungem sa gasim valoarea problemei intregi.
Motivul pentru care aceasta tehnica se numeste Programare Dinamica este datorat flexibilitatii ei,
valoarea schimbandu-si intelesul logic de la o problema la alta. In probleme de minimizarea costului,
valoarea este acest cost minim. In probleme de aflarea unei componente maxime,
valoarea este dimensiunea componentei.
Dupa calcularea valorii pentru toate subproblemele se pot afla efectiv elementele ce alcatuiesc solutia.
Reconstructia solutiei se face mergand din subproblema in subproblema incepand de la problema cu
valoarea optima si ajungand in subprobleme unitare. Metoda admite nuante in cazuri particulare, o sa
se inteleaga mai bine din exemple.
Aplicand aceasta tehnica determinam una din solutiile optime, problema putand avea mai multe solutii
optime. In cazul in care se doreste determinarea tuturor solutiilor optime, algoritmul trebuie combinat cu
unul de backtracking in reconstructia solutiilor.
Dupa cum probabil ati intuit deja, diferenta majora dintre cele doua metode prezentate este ca
algoritmul greedy mentine doar solutiile partiale de la pasul curent pentru a le folosi la pasul urmator, in
timp ce programarea dinamica poate utiliza la un pas subsolutii generate la oricare alt pas anterior.
Exemple de probleme:
Programarea Dinamica este cea mai flexibila tehnica din programare. Cel mai usor mod de a o intelege
este parcurgerea cat mai multor exemple.
O problema clasica de Programare Dinamica este determinarea celui mai lung subsir strict crescator
dintr-un sir de numere. Un subsir al unui sir este format din caractere (nu neaparat consecutive) ale sirului
respectiv, in ordinea in care acestea apar in sir.
Pentru 24 sau 12 nu exista nici un alt element in stanga lor strict mai mici decat ele, de aceea au best egal
cu 1. Pentru elementele 15 se poate gasi in stanga lor 12 strict mai mic decat ele. Pentru 19 se gaseste
elementul 15 strict mai mic decat el. Cum 15 deja este capat pentru un subsir-solutie de 2 elemente,
putem spune ca 19 este capatul pentru un subsir-solutie de 3 elemente.
Cum pentru fiecare element din multime trebuie sa gasim un element mai mic decat el si cu best maxim,
avem o complexitate O(N) pentru fiecare element. In total rezulta o complexitate O(N2). Se pot obtine si
rezolvari cu o complexitate mai mica folosind structuri de date avansate. Atat solutia in O(N2), cat si o
solutie in O(NlogN) poate fi gasita la [5]. Tot acolo se poate gasi si o lista de probleme mai dificile ce
folosesc tehnica Programarii Dinamice, adaptata in diferite forme.
Pentru a gasi care sunt elementele ce alcatuiesc subsirul strict crescator putem sa retinem si o cale de
intoarcere. Reconstructia astfel are complexitatea O(N). Exemplu: subproblema care se termina in
elementul 19 are subsirul de lungime maxima 3 si a fost calculata folosind subproblema care se
termina cu elementul 15 (oricare din ele). Subsirul de lungime maxima care se termina in 15 a fost
calculat folosindu-ne de elementul 12. 12 fiind cel mai mic element din subsir marcheaza sfarsitul
reconstructiei.
O alta problema cu o aplicare clasica a Programarii Dinamice este si determinarea celui mai lung subsir
comun a doua siruri de caractere. Descrierea problemei, indicatii de rezolvare si o modalitate de evaluare
a solutiilor voastre poate fi gasita la [6].
O problema care admite o varietate mare de soltii este cea a subsecventei de sume maxime. Enuntul poate
fi gasita la [7]. Daca se studiaza rezolvariile la aceasta problema se poate observa ca cea cu Programare
Dinamica este printe cele mai usoare de implementat.
Programarea Dinamica poate fi descompusa in urmatoarea secventa de pasi:
1. Descoperirea structurii si "masurii" pe care o are o solutie optima.
2. Determinarea unei metode de calcul recursive pentru a afla valoarea fiecarei subprobleme.
3. Calcularea "de jos in sus" a acestei valori (de la subproblemele cele mai mici la cele mai mari)
4. Reconstructia solutiei optime pornind de la rezultatele obtinute anterior
Programarea dinamic este o metod de elaborare a algoritmilor, care se aplic de regul problemelor n
care se cere determinarea unui optim referitor la un anumit criteriu (cel mai lung subir, cea mai mare
sum, cel mai scurt drum etc). Termenul a fost introdus n 1940 de ctre matematicianul american
Richard Bellman. 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:
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:
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 (top-down), 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).
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;
}
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:
i din {1,2,...,n}S[n][ i]=T[n][i ],
S[i ][j]=T[i ][j]+max{S[i+1][j], S[i+1][j+1]}
Secvena de program este:
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]);
else
S[i][j]=T[i][j]+S[i+1][j];}
Probleme propuse:
1. mprirea unei mulimi n 2 submulimi de sume ct mai apropiate
La sfarsitul noptii de Craciun, Mosul a poposit la bradul a doi frati, unde si-a golit sacul. Cand s-au trezit,
fratii au intrat intr-o mare dilema: cum isi vor imparti ei cadourile mosului? Stiind ca fiecare cadou are o
valoare (cuprinsa intre 1 si 100 inclusiv) si ca sunt maxim 100 de cadouri scrieti un program care sa
determine sumele cadourilor fratilor, precum si modul de impartire, astfel incat sumele obtinute sa fie
cele mai apropiate posibil.
Date de intrare:
In fisierul CADOURI.IN se gasesc informaiile referitoare la cadouri: pe prima linie numarul total de
cadouri, pe urmatoarea linie valorile lor.
Date de iesire: In fiierul CADOURI.OUT trebuie scrise doua sume care sunt cele mai apropiate
corespunztoare unei impartiri a cadourilor, pe a doua linie valorile corespunztoare cadourilor care
nsumeaz prima suma gsit, pe a treia linie, valorile corespunztoare cadourilor care nsumeaz a doua
suma gsit.
Exemplu:
CADOURI.IN CADOURI.OUT
7 48 49
28 7 11 8 9 7 27 28 11 9
7 8 7 27
2. Sum maxim
Se consider un ir de N (N1000) numere naturale cuprinse ntre 1 i 10.000. S se determine o sum
maxim de componente ale irului astfel nct n sum s nu intre dou numere care se afl pe poziii
consecutive n ir.
Exemplu: pentru irul 1 10 2 40 100, suma maxim este 110 (10+100)