Sunteți pe pagina 1din 9

Programare dinamică – Probleme

Problema 1. Se consideră un triunghi de numere naturale format din n linii. Prima linie
conține un număr, a doua linie conține două numere,.., ultima linie conține n numere
naturale. Cu ajutorul acestui triunghi se pot forma sume de numere naturale în felul
următor:
- Se pornește cu numărul de linia 1
- Succesorul unui număr se află pe linia următoare plasat sub el (aceeași coloană)
sau pe diagonală la dreapta (coloana crește cu 1).
- Care este cea mai mare sumă care se poate forma astfel și care sunt numerele
care o alcătuiesc?
Exemplu: pentru n=4
2 17 1
35 15 14 12
634 12 9 8 224
5614 5 6 1 4 0000
t[101][101] c[101][101] drum[101][101]
a) Vom forma un triunghi, de la bază către vârf, cu sumele maxime care se pot forma
cu fiecare număr. Dacă am citit triunghiul de numere într-o matrice t și calculăm
sumele într-o matrice c voma relațiile următoare:
Relațiile de recurență:
-c[n][j]=t[n][j], j=1,2,…,n
-c[i][j]=t[i][j]+maxim(c[i+1][j], c[i+1][j+1]), i=1,2,...,n-1 si j=1,2,...,i.
Tema: pentru n=5, să se construiască matricea c și matricea drumurilor t pt. exemplul:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
b) Pentru această problemă se poate defini o altă substructură optimală, dacă se
parcurge triunghiul de sus în jos. Se obține următoarea relație de recurență:
c[i][j]=t[i][j]+maxim(t[i-1][j-1], t[i-1][j]) i=2, 3,…,n si j=1, 2,…,i (de scris programul)
Exemplu: pentru n=4
2 2
3 5 5 7
6 3 4 11 10 11
5 6 1 4 16 17 12 15
Suma maximă cerută este smax=maxim(c[n][j]), j=1, 2,…,n.
1
Sursă P1.a):
///c[i][j]=t[i][j]+maxim(c[i+1][j],c[i+1][j+1])
///i=n-1, n-2,...,1 si j=1,2,...,i
#include <iostream>
#include <fstream>
using namespace std;
ifstream f("date.in");
int n, t[101][101], c[101][101], d[101][101], i, j;
int main()
{
f>>n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
f>>t[i][j];
for(j=1;j<=n;j++)
c[n][j]=t[n][j];
for(i=n-1;i>=1;i--)
for(j=1;j<=i;j++)
if(c[i+1][j]>c[i+1][j+1])
{
c[i][j]=t[i][j]+c[i+1][j];
d[i][j]=j;
}
else
{
c[i][j]=t[i][j]+c[i+1][j+1];
d[i][j]=j+1;
}
cout<<"Suma maxima="<<c[1][1]<<endl;
i=j=1;
while(i<=n)
{
cout<<t[i][j]<<" ";
j=d[i][j];
i++;
}
/*cout<<endl;
for(i=1;i<=n;i++)
{
for(j=1;j<=i;j++)
2
cout<<c[i][j]<<" ";
cout<<endl;
}
cout<<endl;
for(i=1;i<=n;i++)
{
for(j=1;j<=i;j++)
cout<<d[i][j]<<" ";
cout<<endl;
}*/
return 0;
}
Sursa P1. b):
#include <iostream>
#include <fstream>
using namespace std;
ifstream f("date.in");
int t[101][101], c[101][101],n,i,j,smax;
int main()
{
f>>n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
f>>t[i][j];
c[1][1]=t[1][1];
for(i=2;i<=n;i++)
{
c[i][1]=c[i-1][1]+t[i][1];
for(j=2;j<=i;j++)
if(c[i-1][j]>c[i-1][j-1])
c[i][j]=t[i][j]+c[i-1][j];
else
c[i][j]=t[i][j]+c[i-1][j-1];

}
for(j=1;j<=n;j++)
if(smax<c[n][j])
smax=c[n][j];
cout<<"Suma maxima="<<smax;
return 0;}
3
Problema 2. Subșir crescător de lungime maximă
Enunț. Se consideră un șir a ce conține n elemente întregi. Să se realizeze un program
care determină un subșir crescător de lungime maximă al acestuia. Un subșir crescător
al lui a se poate descrie astfel: ai1<=ai2<=…..<=aik si 1<=i1<i2<…<ik<=n.
Soluție:
Orice problemă care este abordată prin metoda programării dinamice, trebuie să aibă
o structură optimală, adică soluția optimă poate fi construită pe baza soluțiilor optime
ale subproblemelor sale. Pe de altă parte, subproblemele în care se descompune nu
sunt independente, ci se suprapun. Toate soluțiile subproblemelor sunt, în general,
salvate într-o structură de tip tablou.
Descompunerea problemei în subprobleme:
În cazul problemei de față, o subproblemă se referă la determinarea unui subșir
crescător de lungime maximă, care începe cu elementul a[i], 1<=i<=n.
Structuri de date utilizate:
-tabloul a[n] reține elementele șirului inițial
-tabloul l[n] va memora în l[i] lungimea celui mai lung subșir crescător care începe cu
a[i].
Relații de recurență
- l[n]=1
- l[i]=1+max(l[j]) | a[i]<=a[j], i<j<=n)
Lungimea celui mai lung subșir crescător care începe cu a[i] este mai mare cu 1 decât
lungimea maximă a unui subșir care începe cu un element a[j], mai mare decât a[i] ,
situat după poziția i (i<j<=n). Aceasta deoarece a[i] poate fi plasat în fața acestui subșir
maximal, determinat anterior.
Exemplu, pentru tabloul a=(2, 1, 3, 2, 4, 8, 6, 7), vectorul l=(5, 5, 4, 4, 3, 1, 2, 1)
Sursă P2:
#include <iostream>
#include <fstream>
using namespace std;
ifstream f("date.in");
int a[101], l[101], n, lm, p;
void citire()
{
f>>n;
for(int i=1;i<=n;i++)
f>>a[i];
}
4
void dinamica()
{
int i, j, maxi;
l[n]=1;
for(i=n-1;i>=1;i--)
{
maxi=0;
for(j=i+1;j<=n;j++)
if(l[j]>maxi && a[i]<=a[j])
maxi=l[j];
l[i]=maxi+1;
if(lm<l[i])
lm=l[i];
}
}
void drum()
{
int t;
p=1;
t=0;
do{
while(l[p]!=lm||a[t]>a[p])
p++;
cout<<a[p]<<" ";
t=p;
lm--;
}while(lm!=0);
}
int main()
{
citire();
dinamica();
for(int i=1;i<=n;i++)
cout<<l[i]<<" ";
cout<<endl;
drum();
return 0;
}

5
Problema 3.
Se consideră o valoarea naturală x și un șir de numere a[n] crescător, ce conține n
elemente naturale. Să se realizeze un program care determină un subșir de lungime
maximă, în care diferența între oricare două elemente alăturate este mai mare sau
egală cu x: (aik+1-aik)>=x, 1<=ik<n
Exemplu: pentru n=6 și x=4
a=(5, 7, 9, 10, 14, 15)
Se va afișa: 5 9 14, 5 10 15 sau 5 10 14
Soluție:
Descompunerea problemei în subprobleme:
Pentru această problemă o subproblemă se referă la determinarea unui subșir de
lungime maximă care respectă condiția impusă și care se termină cu elementul a[i],
1<=i<=n.
Structuri de date utilizate:
- Tabloul a reține elementele șirului inițial
- Tabloul l[n] va memora în elemental l[i] lungimea celui mai lung subșir a cărui
ultim element este a[i].

Relațiile de recurență:
- l[n]=1;
- l[i]=1+maxim(l[j] | a[i]-a[j]>=x, i<j<=n)

Lungimea celui mai lung subșir care se termină cu a[i] este mai mare cu 1 decât
lungimea maximă a unui subșir care se termină cu un element a[j], mai mic decât a[i]
cu cel puțin valoarea x, situat înaintea poziției i (1<=j<i). Aceasta deoarece a[i] poate fi
plasat la finalul acestui subșir maximal, determinat anterior.
Pentru tabloul a=(5,7,9,10,14,15) și x=4, vectorul l=(1,1,2,2,3,3)
Programul:
#include <iostream>
#include <fstream>
using namespace std;
ifstream f("date.in");
ofstream g("date.out");
int n,x, i, a[101], l[101], lm, p;
void dinamica()
{
int i,j, maxi;
l[1]=1;
for(i=2;i<=n;i++)
{
6
maxi=0;
for(j=1;j<i;j++)
if(l[j]>maxi&&a[j]+x<=a[i])
maxi=l[j];
l[j]=maxi+1;
if(lm<l[i]){
lm=l[i];p=i;
}
}
}
void drum(int lm, int p, int urm)
{
if(p==0)
return;
if(l[p]==lm && urm==-1 || a[p]+x<=a[urm])
{
drum(lm-1, p-1, p);
cout<<a[p]<<" ";
}
else
if(lm>0)
drum(lm,p-1,urm);
}

int main()
{
f>>n>>x;
for(i=1;i<=n;i++)
f>>a[i];
dinamica();
for(int i=1;i<=n;i++)
cout<<l[i]<<" ";
cout<<p<<endl;
drum(lm, p, -1);
return 0;
}

7
Problema 4. Cel mai lung subșir comun a două șiruri
Enunț. Un subșir al unui șir a1, a2, ..., an este un sir care se obține ștergând zero sau
mai multe elemente din șirul inițial. Elementele care se șterg nu trebuie să fie neapărat
pe poziții consecutive în șir. De exemplu: 2, 3, 2, 1 este un subșir al șirului 2, 4, 3, 1, 2, 1
el obținându-se prin ștergerea lui 4 și a primei apariții a lui 1 în șirul inițial. Dându-se
două șiruri a1, a2,...,an și b1, b2,...,bm un subșir comun a celor două șiruri este un șir
care este subșir și pentrul primul șir și pentru al doilea. Problema constă în a găsi un
subșir de lungime maximă a două șiruri date.
Rezolvare. Să presupunem că știm deja cel mai lung subșir comun al celor două șiruri
de lungime n și respectiv m. Să studiem ce se întâmplă cu aceasta în cazul în care se
mai adaugă elemente celor două șiruri. În cel mai simplu caz, dacă se adaugă același
element la sfârșitul ambelor șiruri, este evident că lungimea subșirului comun va crește
cu 1(elementul adăugat făcând parte din cele două șiruri va apărea și în subșirul
comun).
Dacă notăm cu c[i][j] lungimea celui mai lung subșir comun care se poate forma
folosind doar primele i elemente ale primului șir și primele j elemete ale celui de-al
doilea șir.

Relații de recurență:

c[i] [j]=1+c[i-1] [j-1], dacă a[i]= b[j]

= maxim(c[i-1] [j], c[i] [j-1], altfel

Programul:
#include <iostream>
#include <fstream>
using namespace std;
ifstream f("date.in");
int a[101], b[101], c[101][101], n, m, i, j;
void solve()
{
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(a[i]==b[j])
c[i][j]=c[i-1][j-1]+1;
else
if(c[i-1][j]>c[i-1][j-1])
c[i][j]=c[i-1][j];
else
8
c[i][j]=c[i][j-1];
}
void reconstituire(int n, int m)
{
if (n < 1 || m < 1)
return;
if (a[n] == b[m])
{
reconstituire(n-1,m-1);
cout<<a[n]<<" ";
}
else
if (c[n-1][m] > c[n][m-1])
reconstituire(n-1,m);
else
reconstituire(n,m-1);
}
void afisare()
{
//Solutia va fi c[n][n]
//Lungimea celui mai lung subsir comun al sirurilor a si b
cout<<c[n][m]<<endl;
reconstituire(n,m);
}

int main()
{
f>>n>>m;
for(i=1;i<=n;i++)
f>>a[i];
for(i=1;i<=m;i++)
f>>b[i];
solve();
afisare();
return 0;
}

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