Sunteți pe pagina 1din 7

Prima problema: Se da un sir de numere.

Sa se gaseasca cel mai lung subsir care are


suma divizibila cu 3.
Am gasit cel mai lung subsir, dar conditia cu suma divizibila cu 3 imi da batai de cap. Relatia
de recurenta la care m-am gandit eu este:
m[i]=1 daca i=n si a[i] div 3 = 0
m[i]=0 daca i=n si a[i] div 3 = 0
m[i]=1+max(m[j]), unde m[i] este lungimea subsirului care incepe pe pe pozitia i si respecta
conditia, iar j incepe de la i+1 si merge pana la sfarsitul sirului. Problema este ca nu prea stiu
cum sa implementez chestia asta. Adica toate ideile care-mi vin duc mai degraba spre brute
force. (IMG:style_emoticons/default/wallbash.gif)
A doua problema: Sa se gaseasca cel mai mic numar cu exact k divizori.
Aici nu reusesc sa gasesc nici macar o relatie de recurenta. Stiu formula matematica pentru a
calcula numarul asta, insa nu prea e programare dinamica.
m[i]=1 daca i=n si a[i] div 3 = 0
m[i]=0 daca i=n si a[i] div 3 = 0

CODE
m[i]=1
if(i=n && a[i]%3==0)
.....
m[i]=0
if(i=n && a[i]%3==0)

Se considera 2 cuvinte alfa si beta,cu m si respectiv n litere.Sa se transforme cuvantul alfa in


cuvantul beta utilizand trei operatii:
A-adaugarea unei litere;
M-modificarea unei litere;
S-stergerea unei litere.
Transformarea se face prin utilizarea unui nr minim de operatii(cat mai putine
A,M,S).Se cere afisarea nr de operatii precum si a sirului intermediar.
EX:cuvantul DANIA va trece in cuvantul IOANA
Costul transformarii este 3
DANIA
A-IDANIA
M-IOANIA
S-IOANA
folosesti o matrice, in care m[i,j] are semnificatie de numarul minim de operatii pentru a
ajunge din primele i caractere ale primului cuvant, la primele j caractere ale celui de al doilea.
parcurgi matricea de la stanga la dreapta si de sus in jos si pentru fiecare

m[i,j] ai o recurenta de genu asta m[i,j] = min{m[i-1,j],m[i+1,j],m[i-1,j-1]}+1,


care corespund operatiilor {adaugare,stergere,modificare}, daca vrei afli si ce operatii s-au
facut, mai faci o matrice si la fiecare pas pui in ea ce mutare s-a ales (adica pentru care din
alea trei posibilitati a dat minimul
si mai trebuie la fiecare mutare sa verifici daca a[i]=b[j] ( ca nu mai avem nevoie de nici o
operatie) si mai trebuie putina atentie la marginile matrice

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

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++)
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:

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).

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;}
}
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;
}
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;
}

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:
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:
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]);
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)

S-ar putea să vă placă și