Documente Academic
Documente Profesional
Documente Cultură
Aceasta tehnică determina ”valoarea” soluției pentru fiecare din subprobleme. Mergând
de la subprobleme mici la subprobleme din ce în ce mai mari ajungem la soluția optimă,
la nivelul întregii probleme. Motivul pentru care aceasta tehnica se numește Programare
Dinamică este datorată flexibilității ei, ”valoarea” schimbându-și înțelesul logic de la o
problema la alta. În probleme de minimizarea costului, ”valoarea” este reprezentata de
costul minim. In probleme care presupun identificarea unei componente maxime,
”valoarea” este caracterizată de dimensiunea componentei.
După calcularea valorii pentru toate subproblemele se poate determina efectiv mulțimea
de elemente care compun soluția. „Reconstrucția” soluţiei se face mergând din
subproblemă în subproblemă, începând de la problema cu valoarea optimă și ajungând în
subprobleme unitare. Metoda și recurența variază de la problemă la problemă, dar în
urma unor exerciții practice va deveni din ce în ce mai facil să le identificați.
Aplicând aceasta tehnică determinăm una din soluțiile optime, problema putând avea mai
multe soluții optime. În cazul în care se dorește determinarea tuturor soluțiilor optime,
algoritmul trebuie combinat cu unul de backtracking în vederea construcției soluțiilor.
Exemple de probleme
Programarea Dinamică este cea mai flexibilă tehnica din programare. Cel mai ușor mod
de a o înțelege presupune parcurgerea cât mai multor exemple.
O problema clasică de Programare Dinamică este determinarea celui mai lung subșir
strict crescător dintr-un șir de numere. Un subșir al unui șir este format din caractere (nu
neapărat consecutive) ale șirului respectiv, în ordinea în care acestea apar în șir.
Se observa că dacă încercăm o abordare greedy nu putem stabili nici măcar elementul de
început într-un mod corect. Totuși, problema se poate rezolva ”muncitorește” folosind un
algoritm care alege toate combinațiile de numere din șir, validează că șirul obținut este
strict crescător şi îl reține pe cel de lungime maximă, dar aceasta abordare are
complexitatea temporală O(2N). Cu optimizări este posibil sa se ajungă la O(N!).
elem 24 12 15 15 8 19
best 1 1 2 2 1 3
Pentru 24 sau 12 nu există nici un alt element în stânga lor strict mai mici decât ele, de
aceea au best egal cu 1. Pentru elementele 15 se poate găsi în stânga lor 12 strict mai mic
decât ele. Pentru 19 se găsește elementul 15 strict mai mic decât el. Cum 15 deja este
capăt pentru un subșir soluție de 2 elemente, putem spune că 19 este capătul pentru un
subșir soluție de 3 elemente.
Cum pentru fiecare element din mulțime trebuie să găsim un element mai mic decât el şi
cu best maxim, avem o complexitate O(N) pentru fiecare element. În total rezultă o
complexitate O(N2). Se pot obține şi rezolvări cu o complexitate mai mica folosind
structuri de date avansate. Atât soluția în O(N2), cât si o soluție în O(N log N) poate fi
găsită la [5]. Tot acolo se poate găsi și o lista de probleme mai dificile ce folosesc tehnica
Programării Dinamice, adaptată în diferite forme.
Pentru a găsi care sunt elementele ce alcătuiesc subșirul strict crescător putem să reținem
și o „cale de întoarcere”. Reconstrucția astfel obținută are complexitatea O(N). Exemplu:
subproblema care se termina în elementul 19 are subșirul de lungime maximă 3 şi a fost
calculată folosind subproblema care se termină cu elementul 15 (oricare din ele). Subșirul
de lungime maximă care se termină în 15 a fost calculat folosindu-ne de elementul 12. 12
marchează sfârșitul reconstrucției fiind cel mai mic element din subșir.
O altă problemă cu o aplicare clasică a Programării Dinamice este şi determinarea celui
mai lung subșir comun a două șiruri de caractere. Descrierea problemei, indicații de
rezolvare şi o modalitate de evaluare a soluțiilor voastre poate fi găsită la [6].
O problema care admite o varietate mare de soluții este cea a subsecvenței de sume
maxime. Enunțul poate fi găsit la [7]
1. O tabla de sah se citeste ca o matrice n*n in care pozitiile libere au valoarea 0, iar
piesele sunt marcate prin valoarea 1.
Pe prima linie pe coloana js se afla un pion. Sa se determine drumul pe care poate
ajunge pionul pe ultima linie luand un numar maxim de piese.
Pozitia initiala a pionului se considera libera.
Pionul aflat in pozitia i,j se poate deplasa astfel:
- in pozitia i+1,j daca e libera
- in pozitia i+1, j-1 daca e piesa in aceasta pozitie
- in pozitia i+1, j+1 daca e piesa in aceasta pozitie
Exemplu:
53
00000
01010
01111
00011
01011
int main()
{ citire();
pd();
afis();
fin.close();
fout.close();
return 0;
}
2. In fisierul m.in se afla pe prima linie un numar natural n cel mult 20000,iar pe
liniile urmatoare o matrice patratica de dimensiune n care contine doar elemente 0
si 1.
Sa se determine cel mai mare patrat care contine doar valori 1. Se for afisa in
fisierul text m.out urmatoarele valori separate prin spatiu: latura patratului, linia si
coloana coltului stanga sus al patratului. Daca exista mai mult astfel de patrate se
va afisa doar cel mai de sus.
Exemplu:
5
11000
11101
11111
11100
10111
Se afiseaza 3 2 1
#include<fstream>
using namespace std;
int n, a[3][20001],maxx,im,jm;
ifstream fin("m.in");
ofstream fout("m.out");
void Read();
void Write();
int main()
{
Read();
Write();
fin.close();
fout.close();
return 0;
}
void Read()
{ int i,j,m,x;
fin>>n;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{ fin>>x;
if(i==1) a[i][j]=x;
else
{ if(x==0) a[2][j]=0;
else if(j==1) a[2][j]=x;
else
{
m=a[1][j-1];
if(a[1][j]<m) m=a[1][j];
if(a[2][j-1]<m) m=a[2][j-1];
a[2][j]=m+1;
if(a[2][j]>maxx) { maxx=a[2][j];
im=i;
jm=j;
}
}
}
a[1][j-1]=a[2][j-1];
}
a[1][n]=a[2][n];
}
}
void Write()
{ fout<<maxx<<" "<<im-maxx+1<<" "<<jm-maxx+1;
}
3. Se dau n(n+1)/2 numere naturale aranjate intr-un triunghi format din elementele
de sub si de pe diagonala unei matrici patratice de ordin n.
Se calculeaza sume pornind din elementul de pe prima linie prin deplasari in
vecinii de sub si din dreapta. Gasiti suma maxima care se poate calcula astfel si
care sunt valorile din care se obtine suma maxima.
Exemplu:
n=4
2
35
634
5614
suma maxima este 17
si se obtine din valorile 2 3 6 6
#include <fstream>
using namespace std;
ifstream fin ("date.in");
ofstream fout ("date.out");
int main()
{
int i,j,a[100][100],n,s[100][100],maxx;
citire(n,a);
s[1][1]=a[1][1];
for(i=2;i<=n;i++)
for(j=1;j<=i;j++)
if(j==1) s[i][j]=s[i-1][j]+a[i][j];
else if(j==i) s[i][j]=s[i-1][j-1]+a[i][j];
else if(s[i-1][j]<s[i-1][j-1]) s[i][j]=a[i][j]+s[i-1][j-
1];
else s[i][j]=a[i][j]+s[i-1][j];
maxx=0;
int mj;
for(j=1;j<=n;j++) if(s[n][j]>maxx) { maxx=s[n][j]; mj=j;}
fout<<maxx<<endl;
drum(n,a,s,n,mj);
fin.close();
fout.close();
return 0;
}
4. Se citeste o matrice patratica de ordin n formata din numere naturale.
Se calculeaza sume pornind de pe prima linie prin deplasari in vecinii de sub si
din dreapta. Gasiti suma maxima care se poate calcula astfel si care sunt valorile
din care se obtine suma maxima.
Exemplu:
n=4
7158
3561
6348
5614
suma maxima este 23
si se obtine din valorile 5 6 8 4
#include <fstream>
using namespace std;
ifstream fin ("date.in");
ofstream fout ("date.out");
int main()
{
int i,j,a[100][100],n,s[100][100],maxx;
citire(n,a);
for(j=1;j<=n;j++) s[1][j]=a[1][j];
for(i=2;i<=n;i++)
for(j=1;j<=n;j++)
if(j==1) s[i][j]=s[i-1][j]+a[i][j];
else if(s[i-1][j]<s[i-1][j-1]) s[i][j]=a[i][j]+s[i-1][j-
1];
else s[i][j]=a[i][j]+s[i-1][j];
maxx=0;
int mj;
for(j=1;j<=n;j++) if(s[n][j]>maxx) { maxx=s[n][j]; mj=j;}
fout<<maxx<<endl;
drum(n,a,s,n,mj);
fin.close();
fout.close();
return 0;
}
int main()
{
int i,j,a[100][100]={0},n,s[100][100]={0},maxx;
citire(n,a);
for(j=1;j<=n;j++) s[1][j]=a[1][j];
for(i=2;i<=n;i++)
for(j=1;j<=n;j++)
if(s[i-1][j]<=s[i-1][j-1] && s[i-1][j+1]<=s[i-1][j-1]) s[i]
[j]=a[i][j]+s[i-1][j-1];
else if(s[i-1][j]>=s[i-1][j-1] && s[i-1][j]>=s[i-1]
[j+1]) s[i][j]=a[i][j]+s[i-1][j];
else s[i][j]=a[i][j]+s[i-1][j+1];
maxx=0;
int mj;
for(j=1;j<=n;j++) if(s[n][j]>maxx) { maxx=s[n][j]; mj=j;}
fout<<maxx<<endl;
drum(n,a,s,n,mj);
fin.close();
fout.close();
return 0;
}
int main()
{
unsigned long int a[500][500]={0};
int i,j,k,n;
fin>>n>>k;
a[0][0]=1;
for(i=1;i<=n;i++)
for(j=0;j<=i;j++)
if(j==0 || j==i) a[i][j]=1;
else a[i][j]=a[i-1][j]+a[i-1][j-1];
fout<<a[n][k]<<endl;
fin.close();
fout.close();
return 0;
}
#include<fstream>
using namespace std;
ifstream fin ("date.in");
ofstream fout ("date.out");
int main()
{
unsigned long int a[500][500]={0};
int i,j,m,n;
fin>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(i==1 || j==1) a[i][j]=1;
else a[i][j]=a[i-1][j]+a[i][j-1];
fout<<a[n][m]<<endl;
fin.close();
fout.close();
return 0;
}
int main()
{
int n,i,a[10000],s[10000]={0},maxx,im,jm;
fin>>n;
for(i=1;i<=n;i++)
fin>>a[i];
maxx = a[1];
for(i=1;i<=n;i++)
{
s[i]=a[i];
if(s[i]<s[i-1]+a[i]) s[i]=s[i-1]+a[i];
if(s[i]>maxx)
{ maxx=s[i];
jm=i;
}
}
fout<<maxx<<endl;
im=jm;
while(im>0 && s[im]>=0) im--;
im++;
for(i=im;i<=jm;i++) fout<<a[i]<<" ";
return 0;
}
int main()
{
int i,j,maxx;
fin>>n;
for(i=1;i<=n;i++) fin>>A[i];
L[1]=1;
for(i=2;i<=n;i++)
{
maxx=0;
for(j=1;j<i;j++)
if(A[j]<=A[i] && L[j]>maxx) maxx=L[j];
L[i]=maxx+1;
if(L[i]>maxt)
{
maxt=L[i];
pm=i;
}
}
afis(pm,maxt);
return 0;
}
10. Se citeste un numar n si apoi 2 siruri formate din cate n cuvinte fiecare. Primul sir
de cuvinte stabileste ordinea initiala, iar al doilea este o permutare a primului
(aceleasi cuvinte, dar in alta ordine).
Gasiti cel mai lung subsir de cuvinte din cel de-al doilea sir care are proprietatea
ca are cuvintele in ordinea din primul sir de cuvinte. Se va afisa numarul maxim
de cuvinte si apoi cuvintele.
Daca exista mai multe subsiruri de lungime maxima se va afisa unul dintre ele.
Exemplu:
date.in
5
platon kant marx stalin havel
marx stalin kant platon havel
date.out
3
marx stalin havel
#include <fstream>
#include <cstring>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int n, A[10000], L[10000], maxt, pm;
char O[10000][50], S[10000][50];
void citire()
{
int i;
fin>>n;
for(i=1;i<=n;i++) fin>>O[i];
for(i=1;i<=n;i++) fin>>S[i];
}
int poz(int k)
{
int i;
for(i=1;i<=n;i++)
if(strcmp(O[i],S[k])==0) return i;
return 0;
}
int main()
{
int i,j,maxx;
citire();
for(i=1;i<=n;i++) A[i]=poz(i);
L[1]=1;
for(i=2;i<=n;i++)
{
maxx=0;
for(j=1;j<i;j++)
if(A[j]<=A[i] && L[j]>maxx) maxx=L[j];
L[i]=maxx+1;
if(L[i]>maxt)
{
maxt=L[i];
pm=i;
}
}
fout<<maxt<<endl;
afis(pm,maxt);
return 0;
}
11. O tabla de sah se citeste ca o matrice n*n in care pozitiile libere au valoarea 0, iar
piesele sunt marcate prin valoarea 1.
Sa se determine drumul pe care poate ajunge un pion de pe prima linie pe ultima
linie luand un numar maxim de piese. Pe prima linie nu sunt piese si pionul poate
porni din orice pozitie de pe prima linie
Pozitia initiala a pionului se considera libera.
Pionul aflat in pozitia i,j se poate deplasa astfel:
- in pozitia i+1,j daca e libera
- in pozitia i+1, j-1 daca e piesa in aceasta pozitie
- in pozitia i+1, j+1 daca e piesa in aceasta pozitie
Exemplu:
5
00000
01010
01111
00011
01011
}
void drum(int i, int j)
{
if(i==1) fout<<i<<" "<<j<<endl;
else { if(a[i][j]==0) drum(i-1,j);
else if(c[i-1][j-1]+1==c[i][j])
drum(i-1,j-1);
else drum(i-1,j+1);
fout<<i<<" "<<j<<endl;
}
}
void afis()
{
int max=0,jm;
for(j=1;j<=n;j++) if(c[n][j]>max) { max=c[n][j]; jm=j; }
fout<<max-1<<endl;
drum(n,jm);
}
int main()
{ citire();
pd();
afis();
fin.close();
fout.close();
return 0;
}
12. Cladirea Finantelor publice este formata din birouri dispuse intr-un dreptunghi cu
nXm elemente. Intre doua birouri se poate trece daca sunt alaturate pe linie sau pe
coloana.
Pentru fiecare birou se cunoaste valoare taxei care trebuie platita in acel birou
(valoare naturala). Un contribuabil intra in cladire prin biroul 1,1 si trebuie sa o
parareasca prin biroul n,m. Calculati suma minima a taxelor pe care le poate plati
contribuabilul de la intrare pana la iesirea din cladire.
Exemplu:
n=4, m=3, dispunerea birourilor si taxa din fiecare:
372
643
631
622
Valoarea minima pe care o poate plati contribuabilul este 18 (corespunde
parcurgerii birourilor cu taxele: 3 7 2 3 1 2)
#include<fstream>
using namespace std;
ifstream fin ("date.in");
ofstream fout ("date.out");
int main()
{
int a[100][100],s[100][100]={0};
int i,j,m,n;
fin>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
fin>>a[i][j];
if(i==1 && j==1) s[i][j]=a[i][j];
else if(i==1) s[i][j]=s[i][j-1]+a[i][j];
else if(j==1) s[i][j]=s[i-1][j]+a[i][j];
else if(s[i-1][j]<s[i][j-1]) s[i][j]=s[i-1][j]
+a[i][j];
else s[i][j]=s[i][j-1]+a[i][j];
}
fout<<s[n][m]<<endl;
fin.close();
fout.close();
return 0;
}
13. Se da o matrice patratica de ordin n care contine numere naturale si care are liniile
si coloanele numerotate de la 1 la n. Se citeste apoi un numar natural m si n
perechi de pozitii din matrice de forma (i1, j1) si (i2,j2) astfel incat i1 sa fie mai
mic decat i2 si j1 sa fie mai mic decat j2.
Calculati si afisati pentru fiecare pereche de pozitii suma elementelor din matrice
aflate in submatricea care are coltul stanga-sus in (i1,j1) si coltul dreapta-jos in
(i2,j2).
Exemplu:
date.in
3
121
361
136
3
2233
2133
1113
date.out
16 20 4
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int b[500][500];
int main()
{
int n,m,i,j,k,l,c;
fin>>n;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{ fin>>b[i][j];
b[i][j]=b[i][j]+b[i-1][j]+b[i][j-1]-b[i-1][j-1];
}
fin>>m;
for(k=0;k<m;k++)
{
fin>>i>>j>>l>>c;
fout<<b[l][c]-b[i-1][c]-b[l][j-1]+b[i-1][j-1]<<" ";
}
fin.close();
fout.close();
return 0;
}