Documente Academic
Documente Profesional
Documente Cultură
Laborator 7
Descriere: Programare dinamica - 2
Programarea dinamica se aplica problemelor care au mai multe solutii, fiecare din ele cu cate o
valoare, si la care se doreste obtinerea unei solutii optime (minime sau maxime).
Algoritmul pentru rezolvarea unei probleme folosind programarea dinamica se dezvolta in 4 etape:
• caracterizarea unei solutii optime (identificarea unei modalitati optime de rezolvare, care
satisface principiului optimalitatii)
• definirea recursiva a valorii unei solutii optime
• calculul efectiv al valorii unei solutii optime folosind o structura de date (de obicei tablou)
• reconstituirea unei solutii pe baza informatiei calculate.
Fie X = (x1, x2, ..., xn) si Y = (y1, y2, ..., ym) doua siruri de n, respectiv m caractere. Determinati un
subsir comun de lungime maxima.
Un sir de lungime m are 2m subsiruri, in care se includ sirul initial si un sir vid (fara nici un
element). De exemplu, sirul ABCD are urmatoarele subsiruri: ABCD, ABC, ABD, ACD, BCD,
AB, AC, AD, BC, BD, CD, A, B, C, D, ∅ .
Problema propusa este determinarea subsirului comun maximal (longest common string – LCS) a
doua siruri X si Y dintre toate subsirurile comune (care apar ca subsiruri atat in X cat si in Y). De
observat ca pot exista mai multe subsiruri comune diferite, dar de lungime diferita.
Solutie
1. Notam cu Xi = (x1, x2, ..., xi) (prefixul lui X de lungime i) si cu Yj =(y1, y2, ..., yj) prefixul lui
Y de lungime j. O subproblema a problemei date consta ın determinarea celui mai lung
subsir comun al lui Xi, Yj. Notam cu LCS(Xi, Yj) lungimea celui mai lung subsir comun al lui
Xi, Yj. Utilizand aceste notatii, problema cere determinarea LCS(Xn, Ym), precum si un astfel
de subsir.
Sa analizam structura de subprobleme a problemei LCS dintre Xn si Ym, cu alte cuvinte daca
putem reduce problema LCS(Xn, Ym) la probleme mai mici LCS(Xi, Yj) cu i∈[1,n] si j∈[1,m].
ASD
2. Pentru a retine solutiile subproblemelor vom utiliza o matrice cu n+1 linii si m+1 coloane,
denumita C.
Costul problemei LCS (X,Y) este chiar lungimea subsirului comun maximal si se va nota cu
C(n,m). Vom cauta sa exprimam pe C(i,j) in functie de costurile optime ale sirurilor mai
scurte: C(i-1,j), C(i,j-1) si C(i-1,j-1).
Din relatia de recurenta de mai sus deducem ca subproblemele problemei date nu sunt
independente si ca problema are substructura optimala (LCS dintre Xi si Yj contine in el LCS
dintre alte doua siruri mai scurte: Xi-1 si Yj-1)
Problemele care nu se mai pot descompune sunt cele cu i=0 sau cu j=0 pentru care avem
C(i,0)=0 si C(0,j)=0, deoarece un sir de orice lungime si cu sirul vid nu pot avea in comun
decat 0 caractere.
Ordinea de calcul in matrice este astfel incat intr-un grup de 4 elemente vecine se calculeaza
elementul din dreapta-jos in functie de celelalte 3.
Pentru problema LCS matricea C contine n+1 linii si m+1 coloane, iar matricea P numai n
linii si m coloane. La matricea de lungimi maxime se adauga o linie zero si o coloana zero,
pentru problemele ireductibile cu costuri C(0,j) si C(i,0).
ASD
Exemplu
0 1 2 3 4 5
0 0 0 0 0 0 0 1 2 3 4 5
1 A 1 1 1 1 2 A
0 0 0 0 0 1 1
2 B 2 0 0 0 1 B
0 1 1 1 1 1 2
3 C 1 1 2 0 0 C
0 1 1 2 2 2 3
4 D 1 2 1 1 1 D
0 1 2 2 2 2 4
5 E 1 1 1 2 0 E
0 1 2 2 3 3 5
B D C E A B D C E A
Interpretarea valorilor din matricea P pentru afisarea LCS porneste din P[5][5] si cauta la stanga si
in sus valori egale cu 2, deoarece P[i][j]=2 inseamna X[i]==Y[j], deci un caracter din subsirul
comun maxim.
Se observa ca elementele LCS se obtin in ordine inversa, deci este necesara inversarea lor inainte de
afisare.
Rezolvare
import java.io.*;
Date de intrare
class SubsirComunMaximal{
ABCDE
static PrintWriter out; BDCEA
static int [][] c;
citire();
n=x.length();
ASD
m=y.length();
out=new PrintWriter(new BufferedWriter( new FileWriter("lcs.out")));
int i,j;
matrad();
out.println(c[n][m]);
afism(c);
afism(p);
z=new char[c[n][m]+1];
zmin=new char[z.length];
zmax=new char[z.length];
System.out.println("\nToate solutiile");
toatesol(n,m,c[n][m]);// toate solutiile
out.println(nsol);
out.close();
System.out.println("\nGata!");
}
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(x.charAt(i-1)==y.charAt(j-1)){
c[i][j]=1+c[i-1][j-1];
p[i][j]=diag;
}
else{
c[i][j]=max(c[i-1][j],c[i][j-1]);
if(c[i-1][j]>c[i][j-1]) p[i][j]=sus;
else p[i][j]=stanga;
}
}
if(p[lin][col]==diag)
System.out.print(x.charAt(lin-1));
ASD
if(k==0){
System.out.print(++nsol+" ");
afisv(z);
zminmax();
return;
}
i=lin+1;
//merg in sus
while(c[i-1][col]==k){
i--;
j=col+1;
while(c[i][j-1]==k)
j--;
if( (c[i][j-1]==k-1)&&(c[i-1][j-1]==k-1)&&(c[i-1][j]==k-1)){
z[k]=x.charAt(i-1);
toatesol(i-1,j-1,k-1);
}
}//while
}
// z1 si z2 au n componente 1..n
while(z1[i]==z2[i])
i++;
if(i>n) return 0;
else if(z1[i]<z2[i]) return -1;
else return 1;
}
for(j=0;j<m;j++)
System.out.print(y.charAt(j)+" ");
System.out.println();
System.out.print(" ");
for(j=0;j<=m;j++)
System.out.print(a[0][j]+" ");
System.out.println();
for(i=1;i<n;i++){
System.out.print(x.charAt(i-1)+" ");
m=a[i].length;
for(j=0;j<m;j++)
System.out.print(a[i][j]+" ");
System.out.println();
}
System.out.println("\n");
}
System.out.print(" ");
for(j=0;j<m;j++)
System.out.print(y.charAt(j)+" ");
System.out.println();
System.out.print(" ");
for(j=0;j<=m;j++)
System.out.print(d[0][j]+" ");
System.out.println();
for(i=1;i<n;i++){
System.out.print(x.charAt(i-1)+" ");
m=d[i].length;
for(j=0;j<m;j++)
System.out.print(d[i][j]+" ");
System.out.println();
}
System.out.println("\n");
}
for(i=1;i<=v.length-1;i++)
System.out.print(v[i]);
for(i=1;i<=v.length-1;i++)
out.print(v[i]);
System.out.println();
out.println();
}
}
ASD
Probleme propuse
Scopul problemei este de a realiza n taieturi, dea lungul unei vergele ın locuri pre-specificate, cu
efort minim (sau cost). Costul fiecarei operatii de taiere este proportional cu lungimea vergelei care
trebuie taiata. In viata reala, ne putem imagina costul ca fiind efortul depus pentru a plasa vergeaua
ın masina de taiat.
Consideram sirul de numere naturale 0 < x1 < x2 < ... < xn < xn+1 ın care xn+1 reprezinta lungimea
vergelei iar x1, x2, ..., xn reprezinta abscisele punctelor ın care se vor realiza taieturile (distantele
fata de capatul ”din stanga” al vergelei). Notam prin c[i][j] (i < j) costul minim necesar realizarii
tuturor taieturilor segmentului de vergea [xi..xj].
Evident c[i][i + 1] = 0 pentru ca nu este necesara nici o taietura. Pentru j > i sa presupunem ca
realizam prima taietura ın xk (i < k < j). Din vergeaua [xi...xj] obtinem doua bucati mai mici:
[xi...xk] si [xk...xj]. Costul pentru taierea vergelei [xi...xj] este format din costul transportului
acesteia la masina de taiat (xj − xi) + costul taierii vergelei [xi...xk] (adica c[i][k]) + costul taierii
vergelei [xk...xj ] (adica c[k][j]).
Dar, ce valoare are k? Evident, k trebuie sa aiba acea valoare care sa minimizeze expresia c[i][k] +
c[k][j]. Obtinem relatia de recurenta:
Date de intrare
n=5
x: 2 4 5 8 12 15
Date de iesire
1 : 0..6 --> 4
2 : 0..4 --> 2
3 : 0..2 --> 1
4 : 2..4 --> 3
5 : 4..6 --> 5