Documente Academic
Documente Profesional
Documente Cultură
Curs 13
Curs 13
0
C(n, k) = 1
C(n 1, k) + C(n 1, k 1)
, daca n < k,
, daca k = 0 sau k = n,
, altfel.
Varianta de implementare a formulei folosind o functie recursiva nu este recomandata, fiind una
redundanta, aceeasi valoare fiind calculata de mai multe ori (complexitate exponentiala). Alegem
sa folosim metoda programarii dinamice, valorile coeficientilor binomiali calculandu-se progresiv.
Aceste valori vor fi salvate ntr-o matrice patratica de ordin n + 1, C. Doar partea inferioara
a matricei mpreuna cu diagonala sa principala vor fi ncarcate. Vom construi triunghiului lui
Pascal pana la o pereche data (n, k). Complexitatea algoritmului este de ordinul O(nk). Daca
se cere sa se calculeze doar valoarea C(n, k) se poate folosi ca spatiu de stocare un tablou
unidimensional de dimensiune k.
#include<i o s t r e a m >
#include<f s t r e a m >
#define dim 100
using namespace s t d ;
int main ( )
{
unsigned n , k ;
unsigned C[ dim ] [ dim ] ;
cout<<n : ;
c i n >>n ;
cout<<k , k <= n : ;
c i n >>k ;
while ( k > n )
{
2
Curs 13
cout<<k , k <= n : ;
c i n >>k ;
}
ofstream o u t f i l e ( combinari . txt ) ;
C[ 0 ] [ 0 ] = 1;
o u t f i l e <<C[ 0 ] [ 0] < < e n d l ;
for ( unsigned i = 1 ; i <= n ; i ++)
{
for ( unsigned j = 0 ; j <= i ; j ++)
{
if ( j = = 0 || j = = i )
C[ i ] [ j ] = 1 ;
else
C[ i ] [ j ] = C[ i 1 ] [ j 1] + C[ i 1 ] [ j ] ;
o u t f i l e <<C[ i ] [ j ]<< ;
}
o u t f i l e <<e n d l ;
}
o u t f i l e <<endl<<Am g e n e r a t t r i u n g h i u l l u i P a s c a l pana l a
<< p e r e c h e a data ( <<n<< , <<k<< ) <<e n d l ;
o u t f i l e <<C( <<n<< , <<k<< ) = <<C[ n ] [ k]<< e n d l ;
outfile . close ();
return 0 ;
}
Astfel, pentru n = 10 si k = 6, obtinem
combinari.txt
1
1
1
1
1
1
1
1
1
1
1
1
2 1
3 3 1
4 6 4 1
5 10 10 5 1
6 15 20 15 6 1
7 21 35 35 21 7 1
8 28 56 70 56 28 8 1
9 36 84 126 126 84 36 9 1
10 45 120 210 252 210 120 45 10 1
Curs 13
Pentru fiecare camera n care intra i se cere sa plateasca o taxa (numar natural). Determinati
suma minima pe care trebuie sa o plateasca cautatorul de comori pentru a ajunge la comoara
mult dorita.
Vom pastra ntr-o matrice A = (aij ), i = 1, m, j = 1, n, taxele care trebuie platite la trecerea
prin fiecare camera (i, j). In matricea S = (sij ), i = 1, m, j = 1, n, vom calcula sumele partiale
de plata prin parcurgerea drumului de la camera (1, 1) la camera (i, j). Astfel, obtinem formula
de recurenta:
ai,j
, daca i = 1 si j = 1,
s
, daca i 2 si j = 1,
i1,j + ai,j
si,j =
si,j1 + ai,j
, daca j 2 si i = 1,
Curs 13
{
i f s t r e a m i n f i l e ( comoara . t x t ) ;
if (! infile )
{
c e r r << Eroare l a d e s c h i d e r e a f i s i e r u l u i <<e n d l ;
exit (1);
}
int i , j ;
i n f i l e >>m>>n ;
for ( i = 0 ; i < m; i ++)
for ( j = 0 ; j < n ; j ++)
i n f i l e >>a [ i ] [ j ] ;
i n f i l e . close ();
comoara ( ) ;
outfile . close ();
return 0 ;
}
De exemplu,
4
5
7
2
3
3
7
9
6
2
4
3
1
8
Curs 13
n1 obiecte si greutate Ggn , cel rezultat prin transportul obiectului n. Ideea algoritmului este
deci urmatoarea: la fiecare pas, la solutia curenta ori nu adaugam deloc obiectul i, si ramanem
la castigul obtinut pentru i 1 obiecte, ori adaugam obiectul i, caz n care adaugam castigul
rezultat prin transportul lui la cel obtinut pentru primele i1 obiecte si greutate j gi . Obtinem
formula de recurenta:
, daca i = 0 sau j = 0,
0
D[i][j] = D[i 1][j]
, daca i > 0 si j < gi ,
Curs 13
{
S [ i ] [ j ] = 0;
return S [ i ] [ j ] ;
}
else
i f ( S [ i ] [ j ] != 1)
return S [ i ] [ j ] ;
else
{
if ( j < g[ i ])
S [ i ] [ j ] = r u c s a c 2 ( i 1, j ) ;
else
S [ i ] [ j ] = max( r u c s a c 2 ( i 1, j ) ,
r u c s a c 2 ( i 1, jg [ i ] ) + c [ i ] ) ;
return S [ i ] [ j ] ;
}
}
void c o n s t r u c t S o l ( )
{
int i = n , j = G;
while (D[ i ] [ j ] >0)
{
i f (D[ i ] [ j ] = = D[ i 1 ] [ j ] )
i = i 1;
else
{
s o l [ i 1] = 1 ;
j = j g [ i 1];
i = i 1;
}
}
}
int main ( )
{
ifstream f i n ( rucsac . txt ) ;
if (! fin )
{
cout<< Eroare l a d e s c h i d e r e a f i s i e r u l u i . <<e n d l ;
exit (1);
}
f i n >>n ;
f i n >>G ;
for ( int i = 0 ; i < n ; i++ )
f i n >>g [ i ] ;
7
Curs 13
Curs 13
Curs 13
}
f o u t <<Numarul minim de bancnote : <<C[ S]<< e n d l ;
}
void c o n s t r u c t S o l ( int j )
{
i f ( j > 0)
{
constructSol ( j s [ j ] ) ;
f o u t <<s [ j ]<< ;
}
}
int main ( )
{
ifstream f i n ( rest . txt ) ;
if (! fin )
{
cout<< Eroare l a d e s c h i d e r e a f i s i e r u l u i . <<e n d l ;
exit (1);
}
f i n >>n ;
f i n >>S ;
for ( int i = 0 ; i < n ; i++ )
f i n >>b [ i ] ;
fin . close ();
plataS ( ) ;
f o u t << B a n c n o t e l e s e l e c t a t e : ;
constructSol (S ) ;
fout . close ( ) ;
return 0 ;
}
Daca vrem sa platim suma S = 129, cu un numar minim de bancnote de tipul b1 = 12, b2 = 5,
b3 = 1 si b4 = 25, algoritmul Greedy corespunzator nu ne furnizeaza solutia optima. In schimb,
algoritmul proiectat folosind programarea dinamica construieste solutia optima a problemei.
rest.txt
4 129
12 5 1 25
Numarul minim de bancnote: 7
Bancnotele selectate: 25 25 25 25 5 12 12
Aplicatia 5. Cel mai lung subsir ordonat crescator (CMLSC) al unui sir oarecare. Se considera
un sir v oarecare de n elemente numere ntregi, v1 , v2 , ..., vn . Se cere sa se determine CMLSC al
10
Curs 13
sirului.
Pentru a calcula lungimea CMLSC vom calcula mai ntai lungimile celor mai lungi subsiruri
crescatoare care ncep cu fiecare element al vectorului. Vom memora n L[k] lungimea CMLSC
care ncepe de la pozitia k si pana la sfarsitul sirului initial. Tabloul unidimensional L are n
elemente. Pentru ultimul element, L[n 1] = 1. Calculam apoi pe rand L[n 2], ..., L[1], L[0].
Lungimea CMLSC va fi data de cea mai mare valoare a vectorului L.
Vrem sa calculam L[k]. Pentru aceasta, trebuie mai ntai sa calculam lungimea CMLSC din
dreapta lui k, subsir al carui prim element este mai mare sau egal cu elementul de pe pozitia
k. Dar aceasta lungime o putem determina n acelasi mod. Obtinem urmatoarea formula de
recurenta:
(
1
, k = n 1,
L[k] =
1 + maximi:k<i<n,v[k]v[i] L[i] , k = 0, 1, ..., n 2.
Pentru a afisa si elementele CMLSC, folosim un vector suplimentar, next, n care retinem pe
pozitia k indexul primului element al subsirului folosit pentru calcularea lui L[k].
#include<i o s t r e a m >
using namespace s t d ;
void CMLSC( int v , int n )
{
int Lmax , s t a r t , i , k ;
int L = new int [ n ] ;
int next = new int [ n ] ;
L [ n1] = 1 ;
Lmax = 1 ;
s t a r t = n1;
next [ n1] = n1;
for ( k = n 2 ; k >= 0 ; k)
{
L[ k ] = 1;
next [ k ] = k ;
for ( i = k + 1 ; i < n ; i ++)
i f ( v [ k ] <= v [ i ] && L [ k ] <= L [ i ] )
{
L[ k ] = L[ i ] + 1;
next [ k ] = i ;
}
i f (L [ k ] > Lmax)
{
Lmax = L [ k ] ;
s t a r t = k ; // p o z i t i a de i n c e p u t a s u b s i r u l u i
}
}
cout<< Lungimea CMLSC: <<Lmax<<e n d l ;
cout<< Acesta e s t e : <<v [ s t a r t ]<< ;
11
Curs 13
int main ( )
{
int n , v ;
cout<< Lungimea s i r u l u i : ;
c i n >>n ;
v = new int [ n ] ;
cout<< E l e m e n t e l e s i r u l u i : ;
for ( int i = 0 ; i < n ; i++ )
c i n >>v [ i ] ;
CMLSC( v , n ) ;
return 0 ;
}
De exemplu, pentru n = 15 si v[ ] = {34, 1, 3, 5, 45, 23, 0, 5, 39, 40, 234, 7, 10, 56, 45}, obtinem
Lmax = 7 si CMLSC: {1, 3, 5, 23, 39, 40, 234}.
12