Sunteți pe pagina 1din 7

Note de laborator Specializare

Algoritmi si structuri de date 2 Info 1

Laborator 7
Descriere: Programare dinamica - 2

1. Dezvoltarea algoritmilor de programare dinamica

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.

2. Subsir comun maximal

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.

De exemplu, sirurile X='ABCDE' si Y='BDCEA' au ca subsiruri commune pe 'BC','DE','CE' si pe


'BCE', care este si subsirul comun maximal (LCS).

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

Comparand sirurile Xi si Yj constatam ca exista doua cazuri:

1. Daca Xi = Yj atunci LCS(Xi, Yj) = 1+LCS(Xi−1, Yj−1).


2. Daca Xi ≠ Yj atunci LCS(Xi, Yj) = max(LCS(Xi−1, Yj), LCS(Xj, Yi−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.

Relatia de recurenta se rezolva in mod bottom-up:

for (int i=1; i<=n; i++)


for (int j=1; j<=m; j++)
if (x[i]==y[j])
C[i][j]=1+C[i-1][j-1];
else
if (C[i-1][j]>C[i][j-1])
C[i][j]=C[i-1][j];
else
C[i][j]=C[i][j-1];

Metoda programarii dinamice va folosi rezultatele anterioare pentru a completa matricea de


costuri optime, incepand fie cu prima coloana, fie cu prima linie.

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.

3. Pentru calcularea solutiei optime se utilizeaza o structura de date suplimentara, notata P.


Elementele matricei P se completeaza astfel:

P[i][j] = 0 daca C[i][j]=C[i][j-1] (s-a mers pe orizontala)


P[i][j] = 1 daca C[i][j]=C[i-1][j] (s-a mers pe verticala)
P[i][j] = 2 daca C[i][j]=C[i-1][j-1]+1 (s-a mers pe diagonala)

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

In cazul concret X='ABCDE' si Y='BDCEA' matricile C si P arata astfel:

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

Costul optim, deci lungimea LCS este C[5][5]=3.

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.

Secventa de parcurgere a matricei P este urmatoarea:


1 2 3 4 5
P[5][5]=0 A
1 1 1 1 1 2
P[5][4]=2 (scrie 'E') B
2 2 0 0 0 1
P[4][3]=1 C
3 1 1 2 0 0
P[3][3]=2 (scrie 'C') D
4 1 2 1 1 1
P[2][2]=0 E
5 1 1 1 2 0
P[2][1]=2 (scrie 'B'). B D C E A

Se observa ca elementele LCS se obtin in ordine inversa, deci este necesara inversarea lor inainte de
afisare.

Regulile de miscare in matricea P sunt :

daca P[i][j]=0 se merge la stanga (pe orizontala)


daca P[i][j]=1 se merge in sus (pe verticala)
daca P[i][j]=2 se merge in stanga-sus (pe diagonala)

Rezolvare

import java.io.*;
Date de intrare
class SubsirComunMaximal{
ABCDE
static PrintWriter out; BDCEA
static int [][] c;

static char [][] p;


static String x,y;
static char[] z,zmin,zmax;
static int nsol=0,n,m;
static final char sus='1', stanga='0', diag='2';

public static void main(String[] args) throws IOException{

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("O solutie oarecare");


osol(n,m);// o solutie

System.out.println("\nToate solutiile");
toatesol(n,m,c[n][m]);// toate solutiile

out.println(nsol);

System.out.print("sol min = ");


afisv(zmin);

System.out.print("sol max = ");


afisv(zmax);

out.close();
System.out.println("\nGata!");
}

static void citire() throws IOException{


BufferedReader br=new BufferedReader(new FileReader("lcs.txt"));
x=br.readLine();
y=br.readLine();
}

static void matrad(){


int i,j;
c=new int[n+1][m+1];
p=new char[n+1][m+1];

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

static void osol(int lin, int col){


if((lin==0)||(col==0)) return;
if(p[lin][col]==diag)
osol(lin-1,col-1);
else if(p[lin][col]==sus)
osol(lin-1,col);
else osol(lin,col-1);

if(p[lin][col]==diag)
System.out.print(x.charAt(lin-1));
ASD

static void toatesol(int lin, int col,int k){


int i,j;

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
}

static void zminmax(){


if(nsol==1){
copiez(z,zmin);
copiez(z,zmax);
}
else
if(compar(z,zmin)<0)
copiez(z,zmin);
else if(compar(z,zmax)>0)
copiez(z,zmax);
}

//-1=<; 0=identice; 1=>


static int compar(char[] z1, char[] z2){
int i=1;

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

static void copiez(char[] z1, char[] z2){


int i;
for(i=1;i<z1.length;i++)
z2[i]=z1[i];
}

static int max(int a, int b){


if(a>b) return a;
else return b;
}

// pentru matrici cu elemente intregi


ASD

static void afism(int[][]a){


int n=a.length;
int i,j,m;
m=y.length();
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(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");
}

// pentru matrici cu elemente caractere


static void afism(char[][]d){
int n=d.length;
int i,j,m;
m=y.length();

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

static void afisv(char[]v){


int i;

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

1. Problema segmentarii vergelei

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

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