Sunteți pe pagina 1din 5

Programare dinamic

Probleme clasice

1. Problema rucsacului varianta discret


n matematic i informatic programarea dinamic reprezint o metod de rezolvare a unor
probleme complexe prin descompunerea lor n subprobleme mai simple. Aceast metod se aplic
problemelor care:
- admit o formulare recursiv
- soluiile subproblemelor se suprapun (nu sunt independente ca la metoda Divide et impera)
- subproblemele admit o soluie obinut printr-un criteriu de optimizare (min sau max)
Ideea pe care se bazeaz programarea dinamic este asemntoare cu cea de la metoda divide et
impera: pentru a gsi soluia unei probleme generale, trebuie s rezolvm o serie de subprobleme
(pri), iar apoi s combinm rezultatele pariale (ale subproblemelor) pentru a ajunge la soluia
global. Subproblemele care trebuie rezolvate au aceai structur, adic pot fi formulate recursiv.
Abordarea folosit n programarea dinamic este aceea de a rezolva fiecare subproblem o
singur dat, reducnd astfel numrul de operaii necesare o dat ce un rezultat parial a fost
calculat, acesta este memorat ntr-o structur de date. Aceast tehnic poart denumirea de
memoizare. Dac acelai rezultat parial este folosit de mai multe ori n obinerea soluiei globale
este luat direct din structura de date n care a fost memorat, reducnd complexitatea calculelor din
algoritm. Reducerea complexitii de calcul se face prin folosirea n schimb a unui volum mai mare
de memorie, necesar pentru memorarea tuturor acestor rezultate pariale. Structurile de date folosite
n programarea dinamic sunt vectori, tablouri bidimensionale (matrici) sau tablouri multidimensionale.

Fiierul intrare/ieire:

rucsac.in, rucsac.out

Surs

Arhiva Educationala

Autor

Vlad Gavrila

Adugat de

Timp execuie pe test


Scorul tu

0.4 sec
N/A

Limit de memorie
Dificultate

Gavrila Vlad
8192 kbytes
N/A

GavrilaVlad

Pentru aceasta problema accesul la toate sursele trimise este liber!


De asemenea, poti vedea si testele accesand atasamentele.
Vezi solutiile trimise

Problema rucsacului
Se d o mulime format din N obiecte, fiecare fiind caracterizat de o greutate i un profit. S se gseasc
o submulime de obiecte astfel nct suma profiturilor lor s fie maxim, iar suma greutilor lor s nu
depeasc o valoare G.

Date de intrare
Pe prima linie a fiierului rucsac.in se vor gsi valorile N i G, cu semnificaia din enun. Pe
urmtoarele N linii se vor gsi perechile de valori Wi i Pi, reprezentnd greutatea, respectiv profitul
obiectului i.

Date de ieire
n fiierul de ieire rucsac.out se va afia o singur valoare Pmax, profitul maxim care poate fi obinut
respectnd condiia problemei.

Restricii

1 N 5000
1 G 10000
0 Wi, Pi 10000

Exemplu
rucsac.in
6
3
3
1
1
2
1

rucsac.out

10
7
4
2
9
4
5

29

Explicaie
Lum obiectele 1, 2, 4, 5 i 6, a cror greutate este 10, iar suma profiturilor este 29.
n exemplul dat avem n=6 obiecte, cu o greutate total G=10.

obiect
greutate
profit

1
3
7

2
3
4

3
1
2

4
1
9

5
2
4

6
1
5

Indicaii de rezolvare
Problema rucsacului reprezint un exemplu clasic al utilizrii programrii dinamice. Soluia pe care
urmeaz a o prezenta ruleaz n timp pseudo-polinomial O(N*G), i foloseste O(N*G) memorie. O
descriere mai complet a problemei i a variantelor ei este disponibil pe Wikipedia .
Vom ine un tablou bidimensional D, care va reine n celula de coordonate (i, cw) profitul maxim pe
care-l putem obine selectnd o submulime a primelor i elemente, a cror greutate nsumat este egal
cu cw. Presupunem c avem calculate soluiile pentru orice greutate mai mic sau egal ca G, selectnd o
submulime din primele i-1 elemente. Construim soluia pentru i elemente, folosindu-ne de urmatoarea
recuren: D[i][cw] = maximul dintre D[i-1][cw], situaie n care nu adugm elementul i la soluia
precedent, i D[i-1][cw - W[i]] + P[i], cnd adugm la cea mai bun soluie cu greutatea cw W[i] elementul i, obinnd greutatea W[i] i un profit mai mare cu P[i]. Rspunsul final se va afla n
starea D[N][G]. Aceast soluie obine 50 de puncte.
Pentru 100 de puncte este necesar s facem urmtoarea observaie: pentru o anumit linie din dinamic,
recurena nu se folosete dect de linia precedent, deci reinerea ntregului tablou este inutil. n
schimb, vom reine doar ultimele dou linii din matrice, reducnd memoria la O(N). De asemenea putem
s reinem tot timpul o singur linie, dac parcurgem linia n sensul invers al greutilor, cum putei
observa n aceast soluie.
Menionez c exist i algoritmi greedy care, dei ruleaza ntr-o complexitate a timpului cu mult sub cea a
soluiei prezentate, nu reuesc s ofere rezultatul corect, ci doar s-l aproximeze.

Probleme propuse:

Triunghi
Energii
Lapte
Calatorie interplanetara
Jocul
Ghiozdan
Intensitate

Soluii:
Pentru nceput, o s memorm soluia problemei sub forma unei matrice. Notm cu D[n][g]
profitul maxim prin selectarea unei submulimi de obiecte din cele n disponibile, care nu depesc
greutatea total care poate fi transportat n rucsac g.
Relaia de recuren este:
D[n][g]=max ( D[n-1][g], D[n-1][g-w[n]]+p[n] )
fr obiectul n

cu obiectul n

- n este numrul total de obiecte


- g este greutatea maxim a obiectelor din rucsac
- w[i] greutatea obiectului i
- p[i] profitul asociat obiectului i
Din formula general obinem relaia prin care se calculeaz D[i][j]:
[ ][ ]

[
[

][ ]

][ ]
][

[ ]]

[]

[]
[]

1. Evaluarea funciei recursive n manier Top-Down (de la cazul general spre cazul iniial)
folosind tehnica memoizrii obine 50p:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.

#include <fstream>
using namespace std;
#define MXN 5001
#define MXG 5001
ifstream fin("rucsac.in");
ofstream fout("rucsac.out");
int D[MXN][MXG];
int n,g;
int w[MXN],p[MXN];
int max(int a,int b)
{
if(a>b) return a;
else return b;
}
int rucsac(int i,int j)
{
if(i==0) return 0;
if(D[i][j]!=0) return D[i][j];
if(j<w[i]) return D[i-1][j]=rucsac(i-1,j);
return D[i][j]=max(rucsac(i-1,j),
rucsac(i-1,j-w[i])+p[i]);
}
int main()
{
int i;
fin>>n>>g;
for(i=1;i<=n;i++)
fin>>w[i]>>p[i];
fout<<rucsac(n,g);
fin.close();
fout.close();
return 0;
}

2. Evaluarea cu o funcie iterativ Top-Down echivalent obine tot 50p, din cauza pstrrii

rezultatelor intermediare n matricea D. Codul funciei este:


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

int rucsac()
{
int i,j;
for(i=0;i<n;i++)
for(j=0;j<=g;j++)
if(j<w[i+1])
D[i+1][j]=D[i][j];
else
D[i+1][j]=max( D[i][j],
D[i][j-w[i+1]]+p[i+1] );
return D[n][g];
}

3. O prim optimizare a memoriei utilizate se obine dac reinem succesiv doar ultimele dou

linii din matricea D. Dei acest algoritm determin rezultatul corect, nu se ncadreaz n
timp pentru toate testele.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

long pV[MXG],V[MXG];
int rucsac()
{
int i,j,a,b;
for(i=1;i<=n;i++)
{ for(j=0;j<=g;j++)
{ if(j<w[i])
V[j]=pV[j];
else
{
a=pV[j];
b=pV[j-w[i]]+p[i];
V[j]=max(a,b);
}
}
for(j=0;j<=g;j++) //copiem V in pV
pV[j]=V[j];
}
return V[g];
}

4. Soluia care obine 100p foloesete urmtoarea observaie: valoarea optim se calculeaz n

ordinea descresctoare a greutilor, de la g la 0 n acelai vector. Aceast abordare se


ncadreaz n timpul cerut i ocup un spaiu minim de memorie.
Funcia iterativ Bottom-Up, obine rezultatul pornind de la valori cunoscute spre valorile
necunoscute, are codul:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

long V[MXG];
int n,g;
int w[MXN],p[MXN];
int rucsac()
{
int i,j;
for(i=1;i<=n;i++)
for(j=g;j>=w[i];j--)
V[j]=max(V[j],V[j-w[i]]+p[i]);
return V[g];
}