Documente Academic
Documente Profesional
Documente Cultură
nota h. Pentru a memora istoria (a pstra urma - tracking) mutrilor succesive vom folosi urmtorea
convenie:
h[x][y] = 0; dac cmpul (x,y) nu a fost vizitat
h[x][y] = i; dac cmpul (x,y) a fost vizitat la mutarea i, (1<= i <= n2)
S determinm i ceilali parametrii. Trebuie s determinm condiiile de start pentru
urmtoarea mutare i apoi s determinm dac s-a realizat cu succes sau nu. Condiiile de start se pot
preciza prin specificarea coordonatelor (x,y) de la care se realizeaza mutarea i, pentru a putea fi
memorat. Pentru a specifica succesul unei mutri sau nu vom utiliza o variabil ntreag q = 1 pentru
succes i q = 0, pentru insucces. Condiia c tabla nu este plin poate fi exprimat prin i < n2.
Vom intoduce dou noi variabile u i v pentru a reprezenta coordonatele destinaie ale
posibilelor mutri, n concordan cu regulile de mutare ale calului. n acest caz o mutare acceptabil
poate fi rafinat astfel: noul cmp (u,v) va trebui s satisfac condiiile 1<= u <= n i 1<= v <= n i
cmpul respectiv nu fost vizitat anterior, adic h[u][v] = 0.
Operaia de memorare a unei mutri permise se va face prin atribuirea h[u][v] = i, iar anularea
unei mutri prin h[u][v] = 0. Vom introduce o variabil local q1 pentru a fi utilizat ca parametru
rezultat n apelarea recursiv a acestui algoritm; deci q1 poate fi substituit pentru mutarea a fost cu
succes. Dup aceste specificri, nu copmlete, algoritmul devine:
incearca ( int i; int x; int y; int q )
{int u, v, q1;
iniializarea selecie de mutri;
do{
se aleg u, v coordonatele urmatoarei mutari,
conform regulilor sahului;
if (1<= u <= n && 1<= v <= n && h[u][v] == 0)
{
h[u][v] = i;
if (i < pow (n,2))
{
incearca ( i+1 , u , v , q1 );
if ( !q1 )
h[u][v] = 0;
}
else
q1 = 1;
}
} while ( !q1 && ( mai sunt candidai ) );
q = q1;
}
Mai este necesar rafinarea unei condiii pentru ca programul s fie exprimat n notaia de baz
a programrii. Trebuie specificate n limbajul de programare regulile dup care se poate muta calul.
Dintr-un punct dat prin coordonatele sale (x,y) sunt 8 mutri posibile (ca n figura urmtoare, n
care sunt numerotate de la 1 la 8) ale cror coordonate viitoare vor fi notate (u,v). Pentru a obine
coordonatele viitoarelor mutri posibile (u,v) din coordonatele iniiale (x,y) se pot aduna diferenele de
coordonate posibile, memorate ntr-unul sau doi vectori. Vom utiliza doi vectori notai a i b, iniializai
corespunztor.
3
2
1
Cal
8
6
Pentru a specifica candidatul urmtor se poate utiliza o variabil ntreag index. ntregul
program este prezentat n continuare. Funcia recursiv este apelat, iniial, cu coordonatele (x0, y0), ca
paramertii, de unde va ncepe turul calului.
// turcal1s.cpp - program "knight tour": "acoperirea" unei table de sah
// prin mutarile unui cal, astfel incat fiecare casuta a tablei sa fie
// parcursa (atinsa) o singura data; se afiseaza prima solutie gasita
#include <stdio.h>
#include <conio.h>
#define N 5
#define NSQ N*N
#define NrMaxMutCal 8
// N - dimensiunea tablei de sah; NSQ=N^2;
// NrMaxMutCal=8, nr. maxim mutari posibile ale calului, pt. o pozitie data
int i, j, q;
long int nr_incercari;
int h[N+1][N+1];
int a[NrMaxMutCal+1]={0, 1, 1, 2, 2, -1, -1, -2, -2};// poz. relative pe cele doua
int b[NrMaxMutCal+1]={0, 2, -2, 1, -1, 2, -2, 1, -1};// doua coordonate,
// ale celor 8 mutari posibile
void muta (int i, int x, int y, int *pq){// determina mutarea urmatoare
int k, u, v, l, c, q1;
k = 0;
do {
k = k + 1;
// selectie urmatoarea mutare
u = x + a[k]; // noile coordonate
v = y + b[k];
q1 = 0;
// initializare indicator 'succes mutare'
if ((u >= 1 && u <= N) && (v >= 1 && v <= N) && (h[u][v] == 0))
// daca mutarea este valida si casuta nu a fost vizitata
{h[u][v] = i; // memorare mutare 'i'
if (i < NSQ)
{// daca sol. nu e completa se incearca mutarea urm.
muta (i+1, u, v, &q1);
if (!q1) // daca nu este valida se sterge mutarea
{h[u][v] = 0;
nr_incercari++; // se numara ramurile taiate
}
// (infundate)
}
else
3
Condiia acceptabil va fi ndeplinit dac cmpul (i, j) se afl pe o linie i pe diagonale care sunt nc
libere (au valoarea 1), care, deci, poate fi exprimat prin condiia:
a[j] && b[i-j] && c[i-j]
Deoarece vectorul c[i-j] ar urma s aib indicii de la 7 la +7 i ntruct acest lucru n C nu este posibil
vom translata acest indice n intervalul 0-14, adunndu-i 7. Vectorul b[i+j] va avea indicii n intervalul
2-16. n concluzie ntreg programul este urmtorul:
/* regine1s.c - programul furnizeaza o solutie (prima) pentru problema
asezarii a 8 regine pe tabla de sah, astfel incat nici una dintre ele
sa nu o atace pe alta (prima regina este amplasata in pozitia (1,1) */
#include <stdio.h>
#include <conio.h>
int a[9] = {0, 1, 1, 1, 1, 1, 1, 1, 1}; // indice a = linia 'k'
// initializarea pozitiilor, pe linii, de la 1 la 8 (0 nu este utilizata)
int b[17] = {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
// initializarea pozitiilor pentru diagonala dr-st, de la 2 la 16,
// pozitiile 0 si 1 nu sunt utilizate
int c[15] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
// initializarea pozitiilor de la 0 la 14, pentru diagonala dr-st.
int x[9]; // vectorul pozitiilor pe coloane, indice x=linia, x[]=coloana
void incearca (int i, int *pq)
{int j;
j=0;
do
{
j++; *pq=0; // 0 inseamna ca nu este solutie buna
if (a[j] && b[i+j] && c[i-j+7]) // plasare corecta ?
{
x[i]=j; // memorare pozitie (linie, coloana), si
a[j]=0; b[i+j]=0; c[i-j+7]=0; // "activare" directii
if (i < 8)
// de "atac" ale reginei respective
{
incearca (i+1, pq); // plasare regina urmatoare
if ( !(*pq) )
{// daca pozitionarea nu a fost buna
a[j]=1; b[i+j]=1; c[i-j+7]=1;// se sterg
} //"directiile" atacate de regina respectiva
}
else
*pq=1; // solutie completa
}
}while (!(*pq) && j !=8); // se reia cat timp solutia nu este buna
}
// si nu s-au asezat 8 regine pe tabla de sah
void main (void)
{
int i, q;
clrscr();
incearca (1, &q);
for (i=1; i<= 8; i++)
6
printf("%4d", x[i]);
printf("\n");
}
Metoda prezentat furnizeaz o sigur soluie. S extindem algorimul, n termeni generali,
pentru a determina toate soluiile. Pentru aceasta va trebui s generm candidaii n mod progresiv, ntro manier sistematic care s garanteze c nici un candidat nu este generat dect o singur dat.
Aceast proprietate a algoritmului corespunde unei cutri n arborele candidailor ntr-un mod
sistematic n care fiecare nod este vizitat o singur dat. Aceasta pemite, odat soluia gasit i
memorat, s se treac la urmtorul candidat, furnizat de procesul sistematic de selecie. Schema
general, derivat din ultima, este urmtoarea:
ncearc ( i )
{
for ( k = 0; k<= m; k++)
{
selecteaz candidatul k;
dac (acceptabil)
{
memoreaz candidatul;
dac ( i < n )
ncearc ( i + 1 );
altfel
tiparete soluia;
terge candidatul memorat;
}
}
}
Procesul de cutare pentru toate soluiile este realizat de un program simplu, spre deosebire de
cutarea unei singure soluii.
Programul pentru problema celor 8 regine care furnizeaz toate soluiile (fr a recunoate
simetriile) este prezentat n continuare.
/* reginets.c - programul furnizeaza toate solutiile pentru problema
asezarii a 8 regine pe tabla de sah, astfel incat nici una dintre ele
sa nu o atace pe alta (prima regina este amplasata in pozitia (1,1) */
#include <stdio.h>
#include <conio.h>
int a[9] = {0, 1, 1, 1, 1, 1, 1, 1, 1}; // indice a = linia 'k'
// initializarea pozitiilor, pe linii, de la 1 la 8 (0 nu este utilizata)
int b[17] = {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
// initializarea pozitiilor pentru diagonala dr-st, de la 2 la 16,
// pozitiile 0 si 1 nu sunt utilizate
int c[15] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
// initializarea pozitiilor de la 0 la 14, pentru diagonala dr-st.
int x[9]; // vectorul pozitiilor pe coloane, indice x=linia, x[]=colaona
int ntotsol=0; // numarul total de solutii
void afiseaza (void)
{
7
int k;
printf("Sol. %2d: ", ntotsol+1);
for ( k = 1; k <= 8; k++)
printf("%4d", x[k]);
printf("\n");
ntotsol++; // contorizez numarul total de solutii
if (ntotsol%20 == 0){
printf("\nProgramul (si afisarea) continua! Apasati o tasta!\n");
getch();
}
}
void incearca (int i)
{
int j;
for (j=1; j<=8; j++)
if (a[j] && b[i+j] && c[i-j+7])
{x[i]=j; // memorare pozitie (linie, coloana), si
a[j]=0; b[i+j]=0; c[i-j+7]=0; // "activare" directii
if ( i < 8 )
// de "atac" ale reginei respective
incearca (i+1);
else
afiseaza (); // afiseaza solutia gasita
a[j]=1; b[i+j]=1; c[i-j+7]=1;// se sterg
} // "directiile" atacate de regina respectiva
}
void main (void)
{
clrscr();
incearca (1);
printf("\nNumarul tota de solutii gasite este %4d.\n", ntotsol);
printf("\nProgramul s-a terminat! Apasati o tasta!\n");
getch();
}
Iat i varianta care ofer toate soluiile pentru problema anterioar (acoperirea tablei de ah cu
mutrile calului).
// turcalts.cpp - program "knight tour" : "acoperirea" unei table de sah
// prin mutarile unui cal, astfel incat fiecare casuta a tablei sa fie
// parcursa (atinsa) o singura data, programul furmizeaza toate solutiile
// posibile, pornind din coltul din stanga sus, pozitia de coordonate (1,1)
#include <stdio.h>
#include <conio.h>
#define N 5
#define NSQ N*N
#define NrMaxMutCal 8
// N - dimensiunea tablei de sah; NSQ=N^2;
// NrMaxMutCal = 8, numarul maxim de mutari posibile ale calului,
// dintr-o pozitie data
8
nsl = 2;
// dupa care vor fi actualizate pentru N-ul curent
if (N<6)
{nsl = 4;
// nsl = numar de solutii afisate pe linie
nsv = 4;
// nsv = numar de solutii afisate pe verticala
}
else if (N<8)
{nsl = 3;
if (N == 6)
nsv = 3;
}
nse = nsl*nsv;
// numarul de solutii afisate pe ecran
for (il=1; il<=N; il++) // initializare tabla sah, care va memora mutarile
for (ic=1; ic<=N; ic++)
h[il][ic] = 0;
h[1][1] = 1;
muta( 2, 1, 1);
printf("\nNumar total de solutii: %li\n", nr_sol);
printf("\nNumarul total de incercari (ramuri arbore incercari): %li\n",
nr_ramuri);
}
O alt problem rezolvat cu ajutorul metodei backtracking este problema determinrii soluiei
pentru jocul-minune, Sudoku.
// sudoku1s.cpp - program pentru "acoperirea" unui careu de 81 casute (9*9)
// din care o parte sunt fixate, dupa urmatoarea regula:
// orice linie, coloana sau patrat de 3*3 casute sa contina o singura data
// fiecare cifra cuprinsa intre 1 si 9. Verificarea incrucisata a liniilor
// coloanelor si patratelor mici de 3*3 ofera indicii pentru gasirea solutiei
// se determina si afiseaza prima solutie gasita
#include <stdio.h>
#include <conio.h>
#define N 9
#define NSQ N*N
// N - dimensiunea careului de joc; NSQ=N^2;
int q;
long int nr_incercari;
int si[N][N]={{8,0,3,0,0,9,1,0,0},
{0,0,9,0,0,1,0,0,0},
{6,0,0,0,4,0,0,0,8},
{0,0,0,6,0,7,0,5,0},
{3,0,6,0,0,0,2,0,9},
{0,7,0,9,0,3,0,0,0},
{5,0,0,0,7,0,0,0,2},
{0,0,0,1,0,0,7,0,0},
{0,0,1,4,0,0,8,0,3}
};
int s[N][N];
10
for (j=1; j<=N; j++) // pentru a pune in prima coloana (0) din vp
if (vp[i][j]) // valoarea -1, care specifica ca nu avem
nr1++; // decat o singura solutie (alegere)
if (nr1==1)
vp[i][0]=-1;
}
for (i=0; i<N; i++) // se transfera matricea initiala in matricea finala
for (j=0; j<N; j++) // cea care va contine solutia jocului
s[i][j]=si[i][j];
/* clrscr(); // afisarea matricii de valori permise pentru fiecare casuta
printf("Matricea numerelor permise pentru fiecare pozitie din careu:\n");
for (i=0; i<NSQ; i++){
if (i%18==0) // se continua afisarea dupa un numar de linii afisate
{printf("\nContinua afisarea dupa apasarea unei taste\n");
getch();
clrscr();
}
printf("Pozitia [%d,%d] : ", i/N+1, i%N+1);
printf("% d ", vp[i][0]);
for (j=1; j<=N; j++)
if (vp[i][j])
printf("%d ", j);
else
printf("%d ", 0);
printf("\n");
} */
}
int valid(int ip, int k){// fct verifica daca valoarea k poate fi pe poz ip
int l, c, i, j;
l=ip/N; c=ip%N;
// linia 'l' si coloana 'c' a elementului 'ip' din careul 9*9
for (i=0; i<N; i++) // daca valoarea exista pe coloana respectiva (c)
if (k==s[i][c]) // nu mai poate fi incercata (valida) pentru casuta
return 0; // curenta (ip) si returneaza invalid (0)
for (j=0; j<N; j++) // aceeasi conditie se testeaza si pentru linia 'l'
if (k==s[l][j])
return 0; // valoarea k nu poate fi pusa in casuta 'ip'
l=(ip/N/3)*3; c=(ip%N/3)*3;// l si c elem 'ip' din careul 3*3 corespunzator
// in care este inclusa casuta 'ip', deci 'l' si 'c'
// reprezinta coord. stanga-sus al careului 3*3
for (i=l; i<l+3; i++) // se verifica conditia de validitate si pentru careul
for (j=c; j<c+3; j++) // 3*3 in care se afla casuta 'ip'
if(k==s[i][j])
return 0;
return 1; // daca toate conditiile (linie/coloana/careu 3*3) sunt
}
// indeplinite, functia returneaza 1 (valoare valida)
void incearca (int i, int *pq){// determina valoarea urmatoare
int k, x, y, q1=0;
// pentru casuta 'i'
k = 0;
12
printf("%2d", s[i][j]);
else
printf(" ");
printf("\n");
}
incearca(0, &q);// apel pt. i=0, in pozitia (0,0), q-succes joc (solutie)
if (!q)
// acoperire completa careul de joc
printf("Problema nu are solutie !!!\n");
else
{printf("\nSolutia finala este urmatoarea:\n");
for (i=0; i<N; i++)
{for (j=0; j<N; j++)
printf("%2d", s[i][j]);
printf("\n");
}
printf("\nNumarul total de incercari a fost de: %ld\n",nr_incercari);
}
printf("Pentru terminare apasati o tasta!\n");
getch();
}
3
9
9 1
1
6
7
5
2
7
1
1 4
14
8
9
2
7
8
n continuare se construiete, n funcia init_joc() matricea de valori permise (vp) pentru fiecare
csu din careul 9*9, pentru a reduce numrul de ncercri pentru fiecare celula din careu. Aceast
matrice are ca indice de linie (i) numrul csuei din careul iniial (de la 0 la 80, n loc de 1 la 81), iar
indicii coloanelor (j) reprezint valorile ce pot fi ncercate pentru csua (linia) respectiv; dac
valoarea din matricea vp este 1 nseamn c indicele coloanei respective (j) este o valoare ce poate fi
ncercat pentru csua respectiv (i), n caz contrar este o valoare nepermis. Valorile permise sunt n
conformitate cu regulile jocului, valori ce nu apar pe colona respectiv, pe linia respectiv, precum i
valori ce nu apar n careul 3*3 n care este inclus celula respectiv (careuri desenate ngroat n tabela
de mai sus).
Matricea valorilor permise vp are o coloan n plus (N+1) care este chiar prima, coloana indice
0, care va conine valoarea 1 dac csua respectiv are o valoare impus iniial (valorile specificate n
tabela dat mai sus), sau dac n urma verificrii condiiilor de completare a csuei respective rezult
c nu are dect o singur soluie. n celelalte linii de pe coloana 0 se va memora, pe durata ncercrii
(algoritmului), valoarea curent ncercat n celula i.
De exemplu pentru careul anterior, matricea vp (numai primele 20 de linii, din cele 81) are
urmtoarea configuraie:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0
-1
0
-1
0
0
-1
-1
0
0
0
0
-1
0
0
-1
0
0
0
-1
0
1 2 3 4 5 6 7 8 9
1
1
1 1
1
1
1
1
1
1 1
1
1
1
1
1 1
1 1 1 1
1
1
1
1
1 1
1
1 1
1
1 1
1 1
1 1
1
1
1 1 1 1
1 1 1
1 1
1 1 1 1
1
1
1 1
1
Un ultim exemplu de backtracking const n nsumarea unor valori ntregi dintr-un vector
pentru a obine o anumit valoare dat (programul furnizeaz toate soluiile posibile).
/* sumvalv.c - programul citeste un vector de valori intregi
si determina daca o anumita valoarea (suma dorita) se poate furniza
prin insumarea valorilor din vector, si ofera toate solutiile posibile
15
- exemplu de backtracking */
#include <stdio.h>
#include <conio.h>
#define MAXV 16
typedef struct
{int nval, val[MAXV]; /* numar valori disponibile in vector */
long rest;
/* si vectorul; rest (suma) de realizat */
int nrsol;
/* numar solutii */
unsigned sel; /* marcaj valori selectate, prin setarea
bitului corespunzator pozitiei valorii selectate */
} TDate;
int nle=0; /* numar de linii afisate pe ecran */
void AfiSolutie (TDate *a)
{int i;
unsigned x;
printf ("%2i:", ++a->nrsol);
for (x = a->sel, i = 0; i < a->nval; i++, x >>= 1)
if (x & 1)
printf ("%4i", a->val[i]);
else
printf(" -");
printf("\n");
nle++;
if(nle%23==0)
{printf("Se continua afisarea dupa apasarea unei taste!\n");
getch();
}
}
/* cauta variante de completare a restului din solutia curenta
prin backtracking */
void Cauta (TDate *a, int iv) /* iv - indicele valorii analizate */
{if (a->rest == 0)
/* solutie exacta */
{AfiSolutie (a); return;}
for (; iv < a->nval ; iv++) /* cat timp mai exista valori de analizat */
if (a->rest >= a->val[iv] ) /* valoarea iv este acceptabila */
{a->sel += (1 << iv);/* avans: marcheaza acceptare val[iv],
prin pozitionarea unui bit 1 pe pozitia iv */
a->rest -= a->val[iv]; /* si micsoreaza rest de completat */
Cauta (a, iv+1);
/* continua cautarea */
a->sel -= (1 << iv); /* revenire: renunta la val[iv],
prin stergerea bitului 1 de pe pozitia iv */
a->rest += a->val[iv]; /* si reface rest de completat */
}
}
void main ()
{int i;
long Suma;
16
TDate x;
clrscr();
printf ("Introduceti valori, terminand cu 0 sau caracter nenumeric:\n");
for (i = 0; i < MAXV; i++)
if (scanf("%i", &(x.val[i])) < 1 || x.val[i] == 0)
break;
if (!i)
{ printf ("Lipsa date!!!\n"); exit(1); }
x.nval = i;
printf("\nAti introdus %u valori:\n", x.nval);
for (i = 0; i < x.nval; i++)
printf ("%5i", x.val[i]);
printf ("\n");
for (;;)
{printf ("\nSuma dorita de construit (calculat) cu valori "
"din vector\n\t(oprire program orice caracter nenumeric): ");
fflush(stdin);
if (!scanf ("%ld", &Suma))
break;
x.sel = 0; x.rest = Suma; x.nrsol = 0;
Cauta (&x, 0);
/* Cauta solutii */
if (! x.nrsol)
printf ("Nu exista solutie !\n");
}
}
adugare, mulimea de candidai selectai este adecvat, ultimul candidat adugat va rmne de acum
ncolo n ea. De fiecare dat cnd lrgim mulimea candidailor selectai, verificm dac aceast
mulime nu constituie o soluie posibil a problemei noastre.
Metoda este urmtoarea:
- se alege un element din mulimea A;
- se testeaz dac acesta poate fi adugat unei mulimi B, iniial vid;
- se repet acest procedeu pn au fost alese toate elementele pentru mulimea B (i eventual
parcurse toate cele din mulimea A);
Dac algoritmul greedy funcioneaz corect, prima soluie gsit va fi totodat o soluie optim
a problemei. Soluia optim nu este n mod necesar unic; se poate ca funcia obiectiv s aib aceeai
valoare optim pentru mai multe soluii posibile.
Alegerea unui element din mulimea A, mai ales dac aceasta are un numr foarte mare de
elemente, pentru a determina dac acesta poate fi adugat mulimii B poate fi fcut astfel:
- fie dispunem de anumite criterii de alegere, care conduc la rezultatul dorit;
- fie nu dispunem de astfel de criterii, dar putem face o alegere care conduce la un rezultat
acceptabil (dar nu optim), situaie n care trebuie s apreciem distana fa de soluia
optim.
Descrierea formal a unui algoritm greedy este urmtoarea:
greedy (A)
/* A este mulimea candidailor */
B=0; /* B este mulimea n care construim soluia, iniial vid */
while ( ! soluie (B) && A!=0)
{
x = un element din A care maximizeaz/minimizeaz select(x);
A = A x;
if fezabil(B x})
B=Bx
}
if soluie(B)
return B
else
return nu exist soluie
Este de neles acum de ce un astfel de algoritm se numete lacom (greedy), deoarece la
fiecare pas, funcia alege cel mai bun candidat la momentul respectiv, fr s-i pese de viitor i fr s
revina asupra alegerii. Dac un candidat este inclus n soluie, el rmne n soluie, iar dac un
candidat este exclus din soluie, el nu va mai fi niciodata reconsiderat. O astfel de metod poate da
rezultate foarte bune datorit simplitii ei.
O problem clasic pentru aceast tehnic este problema rucsacului: un rucsac care poate
transporta o greutate G, trebuie ncrcat cu obiecte alese dintre n obiecte, fiecare avnd o anumit
greutate (g) i un anumit profit (importan) (p) sau ctig. Se cere s se determine obiectele ce trebuie
ncrcate n rucsac astfe nct profitul (ctigul) s fie maxim.
Exist dou variante ale acestei probleme, dup cum obiectele pot fi tiate sau nu pentru
includerea lor n rucsac: problema continu, respectiv problema discret a rucsacului. Bineneles c
cele dou probleme se rezolv n mod diferit.
Problema continu a rucsacului.
18
n acest caz exist posibilitatea ca obiectele s fie tiate. n acest fel se poate obine o ncrcare
mai eficient a rucsacului:
- se determin pentru fiecare obiect eficiena de transport rezultat prin mprirea profitului
la greutatea obiectului;
- se sorteaz obiectele n ordinea descresctoare a eficienei de transport i se iau n calcul n
aceast ordine;
- iniial profitul este 0, iar greutatea rmas de ncrcat este egal cu G;
- ct timp nu s-a ajuns la greutatea maxim (G) i nu au fost luate n considerare toate
obiectele se va selecta obiectul cel mai eficient pentru care avem dou variante:
- intr n ntregime n rucsac i atunci se va scdea greutatea lui din cea rmas de ncrcat;
- nu intr, n totalitate, n rucsac i atunci se determin ce procent din obiectul respectiv va
intra n rucsac; n acest caz se va cumula la ctigul total procentul de profit asociat prii
din obiect ncrcat n rucsac, iar greutatea rmas va fi zero.
Deci conform cu acest algoritm programul este urmtorul.
/* rucsacc.c - programul incarca un rucsac, utilizand metoda Greedy
(problema continua a rucsacului)*/
#include <stdio.h>
#include <conio.h>
#define DIM_MAX 20
void main()
{
float p[DIM_MAX], g[DIM_MAX], ef[DIM_MAX];
int n, i, j, inv, ordonat[DIM_MAX];
float gr, castig;
clrscr();
printf("Capacitate rucsac (greutate admisa): ");
scanf("%f", &gr);
printf("Numarul de obiecte ce trebuie incarcate in rucsac (disponibile): ");
scanf("%d", &n);
printf("Specificati pentru fiecare obiect greutatea/profitul:\n");
for(i=0;i<n;i++)
/* citirea elementelor sirului de sortat */
{
printf("Greutate(%d)= ",i+1);
scanf("%f", &g[i]);
printf("Profit(%d) = ",i+1);
scanf("%f", &p[i]);
ordonat[i]=i;
ef[i]=p[i]*g[i];
}
do
/* ordonez indici obiecte in vector ordonat, functie de eficienta */
{
/* utilizand metoda inversiunilor (bulelor)
*/
inv=0;
for (i=0; i < n-1; i++)
if (ef[ordonat[i]] < ef[ordonat[i+1]])
{
j=ordonat[i];
ordonat[i]=ordonat[i+1];
19
ordonat[i+1]=j;
inv=1;
}
} while (inv);
castig=0;
i=0;
printf("\nSe incarca in rucsac:\n");
while (gr > 0 && i < n)
{
if (gr >= g[ordonat[i]])
{
printf("\tObiect %d incarcat in intregime\n", ordonat[i]+1);
gr=gr - g[ordonat[i]];
castig=castig + p[ordonat[i]];
}
else
{printf("Obiect %d incarcat in proportie de"
" %.2f %\n", ordonat[i]+1, gr/g[ordonat[i]]*100);
castig=castig + p[ordonat[i]]*gr/g[ordonat[i]];
gr=0; // rucsacul este plin, greutate disponibila 0
}
i++;
}
printf("\nObiectele incarcate in rucsac sunt urmatoarele:\n");
for (j=0; j<i; j++)
printf("\tObiectul: %d, de greutate: %.2f si profit: %.2f\n",
ordonat[j]+1, g[ordonat[j]], p[ordonat[j]]);
if (gr){
printf("\nSe mai pot incarca obiecte de greutate: %.2f", gr);
printf("\n\t\t rucsacul nu este plin, dar nu mai sunt obiecte.\n");
}
printf("\nProfitul total obtinut pentru aceasta incarcare: %.2f\n", castig);
printf("\nApasati o tasta pentru a termina programul !!!\n");
getch();
}
Problema comis-voiajorului
Se cunosc disantele dintre mai multe orae. Un comis-voiajor pleac dintr-un ora i dorete s
se ntoarc n acelai ora, dup ce a vizitat fiecare din celelalte orae numai o dat. Problema este de a
minimiza lungimea drumului parcurs.
Problema poate fi reprezentat printr-un graf neorientat, n care oricare dou vrfuri diferite ale
grafului sunt unite ntre ele printr-o muchie, de lungime pozitiv. Cutm un ciclu de lungime minim,
care s se nchid n varful iniial i care s treaca prin toate vrfurile grafului.
Conform strategiei greedy, vom construi ciclul pas cu pas, adugnd la fiecare iteraie cea mai scurt
muchie disponibil cu urmtoarele proprieti:
- nu formeaz un ciclu cu muchiile deja selectate (exceptnd pentru ultima muchie aleas, care
completeaz ciclul)
20
- nu exist nc dou muchii deja selectate, astfel nct cele trei muchii s fie incidente n acelai
varf
Matricea distanelor pentru problema comis-voiajorului este urmtoarea (matricea este simetric fa de
diagonala principal, ntruct distana i-j este egal cu distana j-i):
La nodul 2
De la nodul:
1
3
2
3
4
5
10
6
11
12
9
7
8
4
5
25
26
20
15
18
De exemplu, pentru ase orae a caror matrice a disantelor este prezentat, muchiile se aleg n
ordinea: {1, 2}, {3, 5}, {4, 5}, {2, 3}, {4, 6}, {1, 6} i se obine ciclul (1, 2, 3, 5, 4, 6, 1) de lungime
58. Algoritmul greedy nu a gasit ciclul optim, deoarece ciclul (1, 2, 3, 6, 4, 5, 1) are lungimea 56.
21
Sursa
Dest. Aux
Sursa
Dest.
Aux
|
|
|
|
|
|
1
===
|
|
==>
|
===
|
2 =====
|
|
|
=====
|
3 =======
|
|
|
=======
|
------------------------------------------------------------------Problema mutrii celor n discuri poate fi descris recursiv astfel:
- dac n=1 , se mut un singur disc;
- dac n >1 se mut primele n-1 discuri n poziia mijloc, prin intermediul poziiei dreapta; se
mut apoi discul rmas n poziia dreapta, dup care se mut cele n-1 discuri din poziia mijloc n
poziia dreapta, prin intermediul poziiei stnga.
De exemplu deplasarea celor n discuri din poziia stnga n poziia dreapta, muta (n, stanga, dreapta)
este echivalent cu urmtoarea secven de sub-probleme :
muta ( n-1 , stanga , mijloc );
deplasez un disc din poziia "stnga" n "dreapta";
muta ( n-1 , mijloc , dreapta );
Am redus problema la a deplasa n-1 discuri, iar acest procedeu poate fi repetat pentru a rezolva
problema. De exemplu operaia muta ( n-1 , stanga , mijloc ) poate fi exprimat astfel:
muta ( n-2 , stanga , dreapta );
deplaseaz un disc din poziia stnga n mijloc;
muta ( n-2 , dreapta , mijloc );
Algoritmul acesta de descompunere a problemei mutrii a k discuri n subprobleme de mutare a
k-1 discuri poate continua pn cnd se ajunge la k-1=1, adic deplasarea unui singur disc. n
descompunerea anterioar funciile definite au ca parametrii poziiile efective ce difer de la un nivel la
altul (k-> k-1). ntruct este vorba de aceeai funcie i pentru a nu fi astfel particularizate vom defini
parametrii pentru cazul general, indiferent de nivelul mutrii (k). n algoritmul general de rezolvare a
acestei probleme va trebui s specificm, pentru mutarea discurilor de la poziia surs la cea destinaie,
i poziia intermediar care este utilizat pentru a realiza un turn provizoriu. Vom nota aceast funcie
astfel :
muta ( n , sursa , mijloc , destinatie );
care nseamn c se mut n discuri de la poziia "surs" n poziia "destinaie", utiliznd poziia
intermediar "mijloc" pentru un turn provizoriu.
Aceast mutare poate fi rezolvat prin descompunerea n subprobleme astfel:
muta ( n - 1 , stanga , dreapta , mijloc );
deplaseaz un disc de la stnga la dreapta;
muta ( n - 1 , mijloc , stanga , dreapta );
Acest algoritm este valabil ct timp n>=1. Se observ c aceast funcie este recursiv,
apelndu-se pe ea nsi ct timp n>=1. ntregul program pentru rezolvarea problemei "Turnurilor din
Hanoi" este urmtorul:
/*t_hanoi.c - Problema 'Turnurilor din Hanoi', rezolvata recursiv */
#include <stdio.h>
#include <conio.h>
enum pozitie { stanga , mijloc , dreapta};
void tipareste_pozitie ( enum pozitie poz )
{
switch ( poz )
{
22
else
if ( k )
printf ("1");
}
void afishexa ( int k ){
if ( k < 10 )
printf("%i",k);
else
printf("%c", k-10+'A');
}
void hexa ( int k ) {
if ( k > 15 ){
hexa ( k/16 );
afishexa ( k%16 );
}
else
if ( k )
afishexa ( k );
}
void main (void){
int n;
printf("numar de afisat in binar :");
scanf("%i",&n);
printf("reprezentarea binara este: \n");
binar ( n );
printf("\n");
getch();
printf("reprezentarea hexazecimala este :\n");
hexa ( n );
printf("\n");
getch();
}
Problema tieturilor
Se consider o form dreptunghiular cu lungimea l i limea h, care are pe suprafaa ei n
guri, ce au coordonate ntregi. Se cere s se separe (taie) din aceast form dreptunghiular o bucat,
tot de form dreptunghiular, de arie maxim care nu prezint guri.
Vom considrea c poziiile gurilor sunt reinute n doi vectori xg i yg. Dreptunghiurile iniiale,
precum i cele obinute n procesul tierii sunt memorate prin coordonatele colului din stnga jos i
cele dou dimensiuni (l i h). Pentru un dreptunghi dat, la un moment dat, se caut dac are vreo gaur.;
dac are, atunci problema se descompune n alte patru subprobleme, de acelai tip. n cazul cnd bucata
respectiv nu are guri, atunci se compar aria ei cu aria bucii maxime, gsite pn n acel moment.
Dreptunghiul de arie maxim este memorat tot prin coordonatele stnga jos i dimensiuni, transmise de
la o etap la alta. Dac dreptunghiul iniial avea n guri, am descompus problema n alte patru
subprobleme, cu cel mult n-1 guri.
Pentru ca o gaur de coordonate xg(i) i yg(i) s se afle n interiorul dreptunghiului trebuie ca:
xg(i) > x; xg(i) < x+l; yg(i) > y; yg(i) < y+h;
24
25
Problema labirintului
Se consider un labirint, specificat printr-o matrice cu n linii i m coloane, n care fiecare
element al matricei reprezint o camer din labirint. Pornind dintr-o camer oarecare, de coordonate i, j
se cere s se gseasc toate ieirile din labirint.
Un element al matricii L[i][j], ce are patru vecini (direciile spre partea de sus, dreapta, jos,
stnga ale poziiei curente, considerate n aceast ordine), poate lua valori ntre 0 i 15, dac
interpretm valoarea asociat camerei (elementului din matrice) n zecimal, codificnd (memornd)
pentru fiecare direcie cu ieire valoarea 1, iar n caz contrar cu valoarea 0. irul format cu aceste patru
cifre binare este transformat (interpretat) n zecimal i este memorat n elementul L[i][j]. De exemplu
dac camera (i, j) are ieiri spre partea de sus i spre stnga, se obine valoarea binar 1001, adic 9 n
zecimal, valoare ce va fi memorat n L[i][j].
Ieirile vor fi codificate cu valoarea 16, motiv pentru care vom ataa dou linii, sus i jos, i
dou coloane, la stnga i respectiv dreapta, ce vor conine aceast valoare.
Drumul parcurs spre ieire se va memora ntr-o matrice, d, cu dou linii i un numr de coloane
egal cu dimensiunea matricii (n*m), care va memora n cele dou linii ale unei coloane k, linia i
coloana n care s-a ajuns n drumul spre ieire.
Funcia iesire, care determin drumul spre ieire din labirint, va realiza urmtoarele operaii:
- testeaz dac nu s-a ieit din labirint (L[i][j]==16);
- dac s-a ajuns la ieire se tiprete drumul gsit;
- dac nu, se rein n matricea d[0][k]=i, d[1][k]=j), k fiind numrul curent al camerei vizitate;
- se testeaz dac aceast camer a mai fost vizitat, iar dac da se iese din funcie;
- se testeaz pe rnd cele patru direcii (sus, dreapta, jos, stnga) i acolo unde este gsit o
ieire se apeleaz funcia cu noile coordonate, i se decremeteaz valoarea k.
ntregul program este prezentat n continuare.
// ieslabir.c - problema "labirintului": iesirea dintr-un labirint
// specificat printr-o matrice L, in care se memoreaza valorile zecimale
// ale codificarilor binare ale drumului spre iesire, si anume:
// fiecare directie care duce spre iesire e codificata cu 1, iar
// restul cu 0; deci pentru o casuta oarecare se considera directiile:
// sus, dreapta, jos si stanga, in aceasta ordine; de exemplu daca o
// camera va avea directii spre iesire sus si stanga, adica 1001, in
// binar, se va memora valoarea 9; matricea va fi completata cu 2
// linii si 2 coloane care vor contine valorile 16 (iesire din labirint)
// Drumul spre iesire va fi memorat in matricea d[2][n*m], pe prima linie
// i, iar pe cea de-a doua j, pentru drumul catre iesire din pozitia k.
#include <stdio.h>
#include <conio.h>
#define N 10
#define M 15
#define DIM (N-1)*(M-1)
// N, M - dimensiuni matrice labirint, 2 linii si 2 coloane atasate;
// pentru a delimita labirintul
int L[N][M] = {{16, 16, 16, 16, 16, 16, 16},
{ 16, 0, 2, 1, 1, 0, 16},
{ 16, 0, 2, 0, 8, 0, 16},
{ 16, 0, 2, 4, 8, 0, 16},
{ 16, 1, 7, 4, 2, 0, 16},
26
{ 16, 0, 2, 0, 2, 0, 16},
{ 16, 16, 16, 16, 16, 16, 16},
};
int d[2][DIM];
int i, j, n=5, m=5, nr_sol=0, nse=4;
void tiparire (int k, int d[2][DIM])
{int h;
nr_sol++;
// contorizare numar de solutii
printf("\nSolutia %d:\n", nr_sol);
for (h=1; h<=k; h++){
printf("[%d,%d]-> ", d[0][h], d[1][h]);
if (h%8==0)
printf("\n");
}
printf("\n");
if (nr_sol % nse == 0) // continua afisarea urmatorului ecran de solutii
{printf("\n"); // dupa afisarea a 'nse' solutii, apasa tasta 'Enter'
printf("Apasati ENTER pentru a continua afisarea");
getch();
clrscr();
}
}
void iesire (int k, int i, int j, int L[N][M], int d[2][DIM]){
int gasit, h;
if (L[i][j] == 16)
tiparire(k, d);
else
{k++;
d[0][k]=i;
d[1][k]=j;
gasit=0;
for (h=1; h<=k-1; h++)
if (d[0][h] == d[0][k] && d[1][h] == d[1][k])
gasit=1;
if (!gasit)
for (h=1; h<=4; h++)
switch (h){
case 1: if (L[i][j] & 8)
iesire(k, i-1, j, L, d);
break;
case 2: if (L[i][j] & 4)
iesire(k, i, j+1, L, d);
break;
case 3: if (L[i][j] & 2)
iesire(k, i+1, j, L, d);
break;
case 4: if (L[i][j] & 1)
iesire(k, i, j-1, L, d);
27
}
k--;
}
}
void main (void) {
int il, ic;
clrscr();
/*
printf("Dimensiuni labirint (matrice), linii, coloane: ");
scanf("%d%d", &n, &m);
for (il=1; il<=n; il++)
for (ic=1; ic<=m; ic++){
printf("L[%d, %d]=", il, ic);
scanf("%d", &L[il][ic])
}
*/
printf("Punctul de plecare din labirint (i, j): ");
scanf("%d%d", &i, &j);
// matricea L, impreuna cu cele 2 linii si 2 coloane, care
// delimiteaza labirintul, si specifica iesirea din labirint
// are dimensiunile (n+2)*(m+2)
for (ic=1; ic<=m; ic++){
L[0][ic]=16;
L[n+1][ic]=16;
}
for (il=1; il<=m; il++){
L[il][0]=16;
L[il][m+1]=16;
}
printf("\nLabirintul are urmatoarea configuratie:\n\n");
for (il=0; il<n+2; il++){
for (ic=0; ic<m+2; ic++)
if (il==i && ic==j)
printf(" x");
else
printf("%3d", L[il][ic]);
printf("\n\n");
}
printf("Coordonate punct plecare L[%d,%d]=%d\n", i, j, L[i][j]);
iesire(0, i, j, L, d);
printf("\nNumar total de solutii: %i\n", nr_sol);
printf("Apasati ENTER pentru a termina programul !");
getch();
}
28
Problema fotografiei
Se consider o fotografie alb-negru, dat sub forma unei matrice binare, care reprezint mai
multe obiecte. Se consider c poriunile corespunztoare obiectului n matrice au valoarea 1, retsul
fiind 0. Considernd c obiectele sunt separate prin zerouri, se cere s se detrmine numrul de obiecte
dintr-o fotografie. Ca i n cazul problemei precedente, pentru a delimita matricea, vom ataa dou linii
(0 i n+1) i dou coloane (0 i m+1) cu valoarea 0. Algoritmul este acela;i cu cel din problema
precedent, cu deosebirea c acum cutarea se face pe opt direcii, n loc de patru. Funcia obiect va
nlocui toate unitile unui obiect cu zero, dup care programul principal va reapela aceast funcie
pn cnd nu mai sunt uniti.
// obiectpo.c - problema "fotografiei": determina numarul de obiecte
// dintr-o fotografie. Obiectele din fotografie sunt codificate cu 1,
// iar celelalte sunt 0. Pornind de la prima unitate determinata
// se cauta unitati adiacente pe cele 8 directii, care sunt puse pe 0
// eliminandu-se astfel obiectul identificat.
// Functia main() reapeleaza functia "obiect" pentru unitatile ramase
#include <stdio.h>
#include <conio.h>
#define N 10
#define M 15
// N, M - dimensiuni matrice fotografie, cu 2 linii si 2 coloane atasate;
// pentru a delimita fotografia
int F[N][M] = {{0, 0, 0, 0, 0, 0, 0},
{ 0, 1, 0, 1, 0, 1, 0},
{ 0, 0, 0, 0, 0, 0, 0},
{ 0, 1, 0, 0, 1, 0, 0},
{ 0, 0, 1, 0, 0, 1, 0},
{ 0, 0, 1, 1, 0, 1, 0},
{ 0, 0, 0, 0, 0, 0, 0},
};
int i, j, n=5, m=5;
void obiect (int i, int j, int F[N][M]){
if (F[i][j]==1){
F[i][j]=0;
// sterg 1 al obiectului curent
obiect(i-1, j, F);
obiect(i-1, j+1, F);
obiect(i, j+1, F);
obiect(i+1, j+1, F);
obiect(i+1, j, F);
obiect(i+1, j-1, F);
obiect(i, j-1, F);
obiect(i-1, j-1, F);
}
}
void main (void) {
int il, ic, nr_ob=0; // nr_ob - numar obiecte
clrscr();
/*
printf("Dimensiuni fotografie (matrice), linii, coloane: ");
29
30
nmulirea matricilor
Pentru nmulirea a dou matrici, C = A * B, de n x n elemente, algoritmul clasic provine direct
din regula de nmulire a dou matrici i necesit n3 nmuliri i (n-1) n3 adunri scalare. Timpul necesar
pentru calcularea matricii C este deci n Q(n3). ncercm s gsim un algoritm de nmulire matricial al
carui timp s fie ntr-un ordin mai mic dect n 3. Oricum limita inferioar pentru orice algoritm de
nmulire matricial este de Q(n2), deoarece trebuie s parcurgem cele n2 elemente ale matricii C.
Strategia divide et impera conduce la un alt mod de calcul al matricii C. Vom presupune n
continuare ca n este o putere a lui doi, sau cel puin un numr par. Partiionm pe A i B n cte patru
submatrici de n/2 * n/2 elemente fiecare. Matricea produs C se poate calcula conform formulei pentru
produsul matricilor de 2 * 2 elemente:
A11
A21
A12 B11
*
A22 B 21
B 22 C 21 C 22
unde
C11 = A11 * B11 + A12 * B21
C21 = A21 * B11 + A22 * B21
Pentru n = 2, nmulirile i adunrile anterioare sunt scalare; pentru n > 2, aceste operaii sunt
ntre matrici de n/2 * n/2 elemente. Pentru fiecare nmulire matricial, aplicm recursiv aceste
partiionri, pn cnd ajungem la submatrici de 2 * 2 elemente.
n timp ce nmulirea matricilor necesit un timp cubic, adunarea matricilor necesit doar un
timp ptratic. Este, deci, de dorit ca n formulele pentru calcularea submatricilor C s folosim mai
puine inmuliri, chiar dac trebuie s mrim numrul de adunri. n 1969, Strassen a descoperit o
metod de calculare a submatricilor C, care utilizeaz 7 nmuliri i 18 adunri i scderi. Pentru
nceput, se calculeaz apte matrici de n/2 * n/2 elemente:
P = ( A11 + A22 ) * ( B11 + B22 )
Q = ( A21 + A22 ) * B11
R = A11 * ( B12 - B22 )
S = A22 * ( B21 B11 )
T = ( A11 + A12 ) * B22
U = ( A21 A11 ) * ( B11 + B22 )
V = ( A12 A22 ) * ( B21 + B22 )
Este uor de verificat c matricea produs C, se obine astfel:
C11 = P + S T + V
C21 = Q + S
C12 = R + T
C22 = P + R Q + U
Algoritmul lui Strassen este mai eficient dect algoritmul clasic de nmulire matricial. Pe
calculator, s-a constatat c, pentru n >= 40, algoritmul lui Strassen este mai eficient dect metoda
clasic, dar se folosete memorie suplimentar.
nmulirea numerelor ntregi mari
Pentru anumite aplicaii, trebuie s considerm numere ntregi foarte mari. Operaiile aritmetice
cu numere ntregi foarte mari nu mai pot fi efectuate direct prin hardware, deci nu mai necesit un timp
31
constant. Reprezentarea numerelor n virgul mobil ar duce la aproximri nedorite, deci trebuie s
implementam prin software operaiile aritmetice respective.
Vom defini un algoritm de tip divide et impera pentru nmulirea ntregilor foarte mari
(operaiile de tip aditiv se realizeaz relativ uor).
Considerm u si v doi ntregi foarte mari, fiecare avnd n cifre zecimale. Daca s = [n/2], partea
ntreag a lui n/2, reprezentm pe u i v astfel:
u = 10s * w + x;
v = 10s * y + z;
unde w i x, respectiv y i z sunt cele dou jumti ale lui u i respectiv v.
vu
yw
zx
n/2
n/2
ntregii w i y, respectiv x i z au cte [n/2] cifre. Din relaia:
u*v = 102s * w*y + 10s * (w*z+x*y) + x*z
obinem urmtorul algoritm divide et impera pentru nmulirea a dou numere ntregi mari.
produs (u, v)
n = cel mai mic ntreg astfel nct u i v s aib fiecare n cifre
if n este mic then calculeaz in mod normal produsul u*v
return produsul uv astfel calculat
else
s=n/2
w = u / 10s ; x = u % 10s
y = v / 10s ; z = v % 10s
return produs (w, y) * 102s +
( produs (w, z) + produs (x, y)) * 10s + produs (x, z)
Pentru a avea i mai puine nmuliri, observnd produsul:
r = (w+x)(y+z) = wy + (wz+xy) + xz = p + (wz+xy) + q
se poate nlocui termenul cu ponderea 10s cu termenul r p q, n ultima linie a algoritmului:
r = produs (w+x, y+z);
p = produs (w, y);
q = produs t(x, z);
return 102s *p + 10s *(r-p-q) + q
void main(void){
long int c[DIM];
int n=15, i, k, d;
clrscr();
for (k=0; k<=n; k++){
// construire vector v, linia n, din triunghi Pascal
for (i=k; i>=0; i--) // construire linie k din tringhiul Pascal
if (i==k)
c[i]=1;
else if (i==0)
c[i]=1;
else
c[i] += c[i-1];
printf("\n\n%2i", k);
// afisare linie k
d=3; // spatiu initial rezervat pentru afisarea unei valori
for (i=0; i<=k; i++){
printf("%*li", d, c[i]);
if (i < (n+1)/2)// pentru valori mai mari ale lui n
{if(i%2)
// se incrementeaza la cresterea lui
d++; // i spatiul d (din 2 in 2, cu 1)
}
else if((i+1)%2)
// iar pentru partea a doua a
d--; // triunghiului se reduce spatiul
}
// cu acelasi pas; pentru n>20 trebuie
if ((k+1)%16 == 0){ // modificat 'd' cu un pas mai mare
printf("\n\nContinua afisarea dupa apasarea unei taste!\n");
getch();
}
}
printf("\n");
printf("\n\n\tApasati o tasta pentru a termina programul\t!!!\n");
getch();
}
Putem spune c metoda divide et impera opereaz de sus n jos (top-down), descompunnd un
caz n subcazuri din ce n ce mai mici, pe care le rezolv apoi separat. Al doilea principiu fundamental
al programrii dinamice este faptul ca ea opereaz de jos n sus (bottom-up). Se pornete de obicei de la
cele mai mici subcazuri. Combinnd soluiile lor, se obin soluii pentru subcazuri din ce n ce mai
mari, pn se ajunge, n final, la soluia cazului iniial.
Programarea dinamic este folosit de obicei n probleme de optimizare. n acest context,
conform celui de-al treilea principiu fundamental, programarea dinamic este utilizat pentru a
optimiza o problem care satisface principiul optimalitaii: ntr-o secven optim de decizii sau
alegeri, fiecare subsecven trebuie s fie de asemenea optim. Cu toate c pare evident, acest principiu
nu este ntotdeauna valabil i aceasta se ntampl atunci cnd subsecvenele nu sunt independente,
adic atunci cnd optimizarea unei secvene intr n conflict cu optimizarea celorlalte subsecvene.
Pe lng programarea dinamic, o posibil metod de rezolvare a unei probleme care satisface
principiul optimalitaii este i tehnica greedy. Ca i n cazul algoritmilor greedy, soluia optim nu este
n mod necesar unic. Dezvoltarea unui algoritm de programare dinamic poate fi descris de
urmtoarea succesiune de pai:
34
subirurile de lungime maxim formate anterior, prin parcurgerea, cu un indice q, a spaiului [p+1, n]
i determinarea valorilor maxime L[q]. Lungimea noului sub;ir determinat va fi L[p]=L[q]+1.
Deoarece relaia leag un termen cu ranf mai mic, p, (mai la stnga) de unul cu rang mai mare, q, (mai
la dreapta), rezult c vom parcurge irul de la dreapta la stnga.
Vom parcurge irul de la dreapta la stnga i vom calcula pentru fiecare valoare curent,
lungimea celui mai mare subir cresctor care ncepe de la elementul curent, determinnd pentru
elementele urmtoare, care are asociat cea mai mare lungime, cu condiia ca irul obinut s fie tot
cresctor.
La fiecare pas k (n-1, n-2, . . . , 1) se caut valoarea vi mai mare dect vk i actualizm lungimea
asociat acetsui element L[k]=L[i]+1, iar dac nu exist o astfel de valoare rmne L[k]=1.
Dup ce se construiete vectorul L, valoarea maxim din acest vector reprezint lungimea
subirului de lungime maxim, iar indexul acestei valori reprezint poziia de nceput a subirului.
ncepnd de la acest element se va afia elementul urmtor care este mai mare i a crui lungime
asociat este mai mic cu o unitate (altfel nseamn c nu face parte din acest subir).
/* sirmaxpd.c - programul determina cel mai mare subsir crescator,
dintr-un sir dat, utilizand "programarea dinamica" */
#include<stdio.h>
#include <conio.h>
#define DIM 100
void main(void){
int vmax, v[DIM]={1, 5, 2, 6, 7, 4, 10, 13, 9, 20, 14, 7, 15, 12, 17};
int n=15, i, k, max, imax, L[DIM], Lmax;
clrscr();
/*printf("Numar elemente:");
scanf("%d", &n);
for(i=0; i<n; i++) // citire sir unde caut cel mai mare subsir crescator
{
printf("v(%d)= ", i+1);
scanf("%d", &v[i]);
} */
L[n-1]=1;
/* calculez L[k]=L[i]+1, unde 'i' este indicele unde v[k]<=v[i] */
for (k=n-2; k>=0; k--){ // L[k] contine numarul max. de succesori pentru v[k]
L[k]=1; // determin pentru fiecare numar v[k]
imax=k; // care pot fi succesorii sai
Lmax=1; // selectand dintre acestia pe cei care ofera lungimea
for (i=k+1; i<n; i++) // maxima (L[k] va contine pentru pozitia k
if (v[k] <= v[i] && Lmax <= L[i]){ // numarul total
Lmax=L[i]; // de elemente din subsirul
imax=i;
// crescator, maxim, ce se
L[k]=L[imax]+1;
// poate forma
}
}
printf("Vectorul L este urmatorul:\n");
for (i=0; i<n; i++)
printf("%3i", L[i]);
printf("\n");
Lmax=L[0]; imax=0; // se determina inceputul subsirului crescator maxim
36
37