Documente Academic
Documente Profesional
Documente Cultură
CODE
m[i]=1
if(i=n && a[i]%3==0)
.....
m[i]=0
if(i=n && a[i]%3==0)
Programare dinamica
Autor: Prof. Voinea Mirela, Colegiul
National Vasile Alecsandri Galati
Introducere n programarea dinamic
1. Descriere
2. Probleme clasice
3. Probleme propuse
Descriere
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
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++)
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 (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:
{cout<<v[i]<<' ';
max--;}
return 0;
}
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:
h din {1,2,..,m}k din {1,2,..,n}, lcs[k][0]=lcs[0][h]=0,
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[k-1][h];
else lcs[k][h]=lcs[k][h-1];
}
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;
}
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 ],
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)