Sunteți pe pagina 1din 60

UNIVERSITATEA OVIDIUS DIN CONSTANTA

FACULTEA DE MATEMATICA SI INFORMATICA

Algoritmi si structuri de date

Note de laborator

Ioana Pomparau

Versiunea: 12.01.2016
Cuprins

Prefata i

1 Introducere n studiul algoritmilor 1

2 Pseudocod. Instructiuni de intrare-iesire, atribuire si struc-


tura decizionala 2
2.1 Specificarea tipului de data al variabilelor . . . . . . . . . . . 2
2.2 Citirea datelor de intrare de la tastatura . . . . . . . . . . . . 3
2.3 Scriere la consola . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.4 Atribuire de valori variabilelor . . . . . . . . . . . . . . . . . . 4
2.5 Structura decizionala IF . . . . . . . . . . . . . . . . . . . . . 4
2.6 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

3 Structuri repetitive 7
3.1 Structura repetitiva cu numar necunoscut de pasi si test initial
- WHILE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.2 Structura repetitiva cu numar necunoscut de pasi si test final
- DO ... WHILE . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.3 Structura repetitiva cu numar cunoscut de pasi - FOR . . . . 9
3.4 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

4 Tablouri de date. Vectori 15


4.1 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

5 Tablouri bidimensionale (Matrice) 19


5.1 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

6 Complexitatea algoritmilor 22
6.1 Timpul de executie si ordinul de crestere al unui algoritm . . . 22
6.2 Aplicatii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
6.3 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

7 Sortari simple 27
7.1 Sortarea prin insertie . . . . . . . . . . . . . . . . . . . . . . . 27
7.2 Sortarea prin selectia minimului . . . . . . . . . . . . . . . . . 28
7.3 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

8 Tehnici de programare. Recursivitate si Divide et Impera 31


8.1 Recursivitate . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
8.2 Divide et Impera . . . . . . . . . . . . . . . . . . . . . . . . . 33
8.3 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

9 Mergesort si Quicksort 37
9.1 Sortare prin interclasare (Mergesort) . . . . . . . . . . . . . . 37
9.2 Sortarea rapida (Quicksort) . . . . . . . . . . . . . . . . . . . 39

10 Tehnici de programare. Greedy 42


10.1 Problema fractionara a rucsacului . . . . . . . . . . . . . . . . 42
10.2 Colorarea unei harti . . . . . . . . . . . . . . . . . . . . . . . 44
10.3 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

11 Tehnici de programare. Backtracking 46


11.1 Generarea tuturor permutarilor unei
multimi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
11.2 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

12 Tehnici de programare. Programare dinamica 50


12.1 Subsir strict crescator maximal . . . . . . . . . . . . . . . . . 51
12.2 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

A Generarea de numere aleatoare 55

2
Prefata

Aceste note de laborator se adreseaza studentilor de anul I, specializarea In-


formatica, ai Facultatii de Matematica si Informatica a Universitatii Ovidius
din Constanta.
La sfarsitul fiecarui laborator se recomanda un set de probleme. Prob-
lemele din tema sunt: exercitii simple (nemarcate), exercitii de dificultate
medie (marcate cu un asterisc), exercitii de dificultate ridicata sau care con-
situtie algoritmi elementari foarte importanti (marcate cu doua asteriscuri)
si probleme foarte dificile (trei asteriscuri).

i
Laboratorul 1

Introducere n studiul
algoritmilor

Sa consideram pentru nceput urmatoarea problema: fiind data o retea de


orase si drumuri care le unesc, se doreste calcularea unui traseu optim (de
distanta minima) de la un oras de pornire A la un oras destinatie B, daca
acesta exista.
Pentru a rezolva astfel de probleme ne intereseaza sa construim un algo-
ritm (o succesiune de pasi) care, pornind de la datele de intrare (reteaua
de orase si drumuri, punctul de plecare si cel de oprire) gaseste o varianta de
date de iesire (un traseu optim sau concluzia ca nu exista nici un traseu).
Indiferent de valorile introduse pentru datele de intrare, algoritmul trebuie
sa gaseasca corect valori pentru datele de iesire.
Atunci cand ncercam sa implementam, ntr-un limbaj de programare, o
solutie a unei probleme, trebuie sa avem n vedere

- structurarea datelor problemei

- elaborarea unui algoritm de rezolvare

- o analiza care sa asigure corectitudinea si eficienta algoritmului folosit.

1
Laboratorul 2

Pseudocod. Instructiuni de
intrare-iesire, atribuire si
structura decizionala

Pentru a defini algoritmii pe care i vom scrie n continuare, vom utiliza


un limbaj pseudocod. Limbajul pseudocod este utilizat n descrierea algo-
ritmilor fara a specifica detaliile de sintaxa corespunzatoare unui limbaj de
programare(de exemplu, C/C++, Java, PHP).

2.1 Specificarea tipului de data al variabilelor


Inainte de a utiliza o variabila n cadrul unui program, este recomandat/nece-
sar sa se specifice tipul de data si identificatorul acesteia, n forma:
tip data identificator

Exemplul 2.1. Declarare de variabile n pseudocod si cod C.

Pseudocod Cod C
real a, b
float a , b ;
integer n
int n ;
char c
char c ;

2
2.2 Citirea datelor de intrare de la tastatura
Pentru a descrie instructiunea prin care utilizatorul introduce valori de la
tastatura vom folosi secventa
read var1 , var2 , ...

Exemplul 2.2. Sa se citeasca de la tastatura valori pentru variabilele a, b


si n declarate n exemplul anterior.

Pseudocod Cod C
read a, b, n
s c a n f ( "%f %f %d" , &a , &b , &n ) ;

2.3 Scriere la consola


Instructiunea de mai jos permite afisarea la consola a valorilor unor expresii.
write expr1 , expr2 , ...
Expresiile pot fi mesaje catre utilizator, expresii aritmetice etc.

Exemplul 2.3. Afisarea unui mesaj la consola.

Pseudocod Cod C
write Hello World!
p r i n t f ( " Hello World !" ) ;

Exemplul 2.4. Calculul sumei a doua numere ntregi ale caror valori sunt
citite de la tastatura.

Pseudocod Cod C
integer a, b
int a , b ;
read a, b
s c a n f ( "%d %d" , &a , &b ) ;
write Suma este: , a + b
p r i n t f ( " Suma : %d" , $a + b$ ;

3
2.4 Atribuire de valori variabilelor
Putem atribui unei variabile valoarea unei expresii folosind instructiunea
var expresie

Exemplul 2.5. Sa se interschimbe valorile a doua variabile ntregi a si b


folosind o variabila auxiliara.
Comentariu. Algoritmul consta n trei pasi: se va salva valoarea vari-
abilei a n aux, a va primi valoarea lui b si, n final, variabila b va primi
valoarea lui aux, i.e., valoarea initiala a lui a.

Pseudocod Cod C
integer a, b, aux
i n t a , b , aux ;
read a, b
s c a n f ( "%d %d" , &a , &b ) ;
aux a
aux = a ;
ab
a = b;
b aux
b = aux ;

2.5 Structura decizionala IF


In cadrul instructiunii If se evalueaza o expresie logica. Daca expresia este
adevarata se va executa o secventa de instructiuni data. Optional, se poate
specifica un set de instructiuni de executat si n situatia n care expresia este
falsa.
if expresie then
secventa de instructiuni
end if
si varianta
if expresie then
secventa de instructiuni
else
alta secventa de instructiuni
end if

Exemplul 2.6. Sa se citeasca de la tastatura un numar ntreg si sa se verifice


daca este par.

4
Pseudocod Cod C
integer n
int n ;
read n
s c a n f ( "%d" , &n ) ;
if n%2 == 0 then
i f ( n % 2 == 0 )
write Numarul , n, este par
p r i n t f ( " Nr . %d este par " , n ) ;
else
else
write Numarul , n, este impar
p r i n t f ( " Nr . %d este par " , n ) ;
end if

Exemplul 2.7. Sa se citeasca de la tastura doua numere ntregi si sa se


calculeze maximul dintre ele.

Pseudocod Cod C
integer a, b
int a , b ;
read a, b
s c a n f ( "%d %d" , &a , &b ) ;
if a >= b then
i f ( a >= b )
write Maximul: , a
p r i n t f ( " Maximul : %d" , a ) ;
else
else
write Maximul: , b
p r i n t f ( " Maximul : %d" , a ) ;
end if

Exemplul 2.8. Sa se citeasca de la tastura trei numere ntregi si sa se


calculeze maximul dintre ele.
Comentariu. Daca am utiliza o strategie bazata pe compararea valorilor
doua cate doua, ar rezulta un numar mare de comparatii. Acest numar de
comparatii creste odata cu numarul de numere de evaluat. Din acest motiv,
se recomanda introducerea unei variabile auxiliare, max, care initial va primi
valoarea primului numar. Valoarea acestei variabile se va compara cu fiecare
dintre celelalte numere si de fiecare data cand se va gasi un numar mai mare,
max se va actualiza cu valoarea acestuia. La finalul comparatiilor, max va
ramane cu valoarea maxima gasita.

5
Pseudocod Cod C
integer a, b, c, max
i n t a , b , c , max ;
read a, b, c
s c a n f ( "%d %d %d" , &a , &b , &c ) ;
max a
max = a ;
if max < b then
i f ( max < b ) max = b ;
max b
i f ( max < c ) max = c ;
end if
p r i n t f ( " Maximul : %d" , max ) ;
if max < c then
max c
end if
write Maximul: , max

2.6 Tema
Exercitiul 2.1. Cititi de la tastatura un numar ntreg si verificati daca este
divizibil cu 5.

Exercitiul 2.2 . Introduceti o data exprimata prin trei numere ntregi, repre-
zentand anul, luna si ziua. Afisati data corespunzatoare urmatoarei zile.
De exemplu, daca introducem 2014 5 10, se va afisa 2014 5 11. Ce se
ntampla daca introducem 2014 10 31? Dar 2014 12 31?

Exercitiul 2.3. Introduceti ziua de nastere a unei persoane si data curenta,


fiecare reprezentata de trei numere: anul, luna si ziua. Calculati varsta per-
soanei n ani mpliniti.
Indicatie. In general varsta unei persoane este data de diferenta ntre
anul curent si anul nasterii. Ce se ntampla daca persoana respectiva si
aniverseaza ziua maine? Dar luna viitoare? Nu uitati sa i transmiteti La
multi ani! daca ziua sa este azi.

Exercitiul 2.4 . Data valoarea unui numar natural mai mic sau egal cu 99,
se cere sa se tipareasca n scriere romana.

6
Laboratorul 3

Structuri repetitive

3.1 Structura repetitiva cu numar necunos-


cut de pasi si test initial - WHILE
Aceasta instructiune se poate traduce prin sintagma: cat timp o conditie data
este adevarata, se repeta un set de instructiuni. La fiecare pas (iteratie) a bu-
clei, daca expresia este adevarata se executa o data secventa de instructiuni.
while expresie do
secventa de instructiuni
end while

Exemplul 3.1. Sa se calculeze suma S = 1 + 2 + 3 + + n, unde n este


un numar natural a carei valoare este citita de la tastatura.
Comentariu. Vom utiliza un contor, i, care va lua succesiv valorile
termenilor sumei, i.e., 1, 2, 3, . . . , n. La fiecare pas, vom adauga la suma s,
initializata cu 0, valoarea termenului curent, adica valoarea variabilei i.

Pseudocod Cod C
integer n, s, i int n , s , i ;
read n s c a n f ( "%d" , &n ) ;
s0 s = 0;
i1 i = 1;
while i <= n do while ( i <= n ) {
ss+i s = s + i;
ii+1 i ++;
end while }
write s p r i n t f ( "%d" , s ) ;

7
Exemplul 3.2. Sa se afiseze suma cifrelor unui numar ntreg n.
Comentariu. Pentru a calcula suma cifrelor unui numar ntreg, va
trebui sa obtinem individual fiecare cifra. Deoarece nu stim cate cifre are
numarul introdus de utilizator, vom extrage pe rand cate o cifra din numar
si o vom adauga la o suma s, initializata cu 0. Vom folosi formula n%10,
care este egala cu ultima cifra a lui n. Dupa adaugarea ultimei cifre la
suma, aceasta poate fi eliminata din numar prin utilizarea formulei n/10,
care, pentru n ntreg, reprezinta numarul n fara ultima cifra.

Pseudocod Cod C
integer n, s
int n , s ;
read n
s c a n f ( "%d" , &n ) ;
s0
s = 0;
while n! = 0 do
while ( n != 0 ) {
s s + n%10
s = s + n % 10 ;
n n/10
n = n / 10 ;
end while
}
write s
p r i n t f ( "%d" , s ) ;

Exemplul 3.3. Sa se introduca de la tastatura numere ntregi pana la ntalnirea


lui 0.

Pseudocod Cod C
integer x
int x ;
read x
s c a n f ( "%d" , &x ) ;
while x! = 0 do
while ( x != 0 ) {
read x
s c a n f ( "%d" , &x ) ;
end while
}

3.2 Structura repetitiva cu numar necunos-


cut de pasi si test final - DO ... WHILE
In cadrul acestei instructiuni conditia este plasata la sfarsitul iteratiei.
do
secventa de instructiuni
while expresie
Vom rescrie Exemplul 3.3, utilizand instructiunea do ... while.

8
Pseudocod Cod C
integer x
int x ;
do
do{
read x
s c a n f ( "%d" , &x ) ;
while x! = 0
}
while ( x != 0 ) ;

3.3 Structura repetitiva cu numar cunoscut


de pasi - FOR
In pseudocod, aceasta instructiune are urmatoarea forma.
for contor val initiala, val f inala[, pas] do
secventa de instructiuni
end for
Vom rescrie Exemplul 3.1, nlocuind structura repetitiva while cu for.

Pseudocod Cod C
integer n, s, i
int n , s , i ;
read n
s c a n f ( "%d" , &n ) ;
s0
s = 0;
for i 1, n do
f o r ( i = 1 ; i <= n ; i ++){
ss+i
s = s + i;
end for
}
write s
p r i n t f ( "%d" , s ) ;

Observatia 3.1. Oricare doua structuri repetitive se pot nlocui reciproc


ntr-un algoritm (vezi exemplele de mai sus).

9
Exemplul 3.4. Sa se calculeze numarul de cifre ale unui numar ntreg n.

Pseudocod Cod C
integer n, c
int n , c ;
read n
s c a n f ( "%d" , &n ) ;
c0
c = 0;
while n! = 0 do
while ( n != 0 ) {
cc+1
c++;
n n/10
n = n/ 1 0 ;
end while
}
write c
p r i n t f ( "%d" , c ) ;

Exemplul 3.5. Sa se calculeze rasturnatul(oglinditul) unui numar ntreg n.

Pseudocod Cod C
integer n, r
int n , r ;
read n
s c a n f ( "%d" , &n ) ;
r0
r = 0;
while n! = 0 do
while ( n != 0 ) {
r r 10 + n%10
r = r 10 + n % 10 ;
n n/10
n = n/ 1 0 ;
end while
}
write r
p r i n t f ( "%d" , r ) ;

Exemplul 3.6. Sa se introduca de la tastatura numere ntregi pana la ntalnirea


lui 0. Calculati valoarea maxima introdusa de catre utilizator.

Pseudocod Cod C
integer x, max
read x i n t x , max ;
max x s c a n f ( "%d" , &x ) ;
while x! = 0 do max = x ;
read x while ( x != 0 ) {
if max < x then s c a n f ( "%d" , &x ) ;
max x i f ( max < x ) max = x ;
end if }
end while

Exemplul 3.7. Sa se calculeze cel mai mare divizor comun a doua numere
ntregi a si b.

10
Comentariu. Vom utiliza algoritmul lui Euclid care se bazeaza pe mpar-
tiri succesive astfel: se calculeaza restul martirii lui a la b, iar daca acesta
este nenul, se face o noua martire, de data aceasta a vechiului mpartitor
la rest, obtinandu-se un al doilea rest. Se continua algoritmul pana cand se
ajunge la un rest egal cu 0. Ultimul rest nenul reprezinta cel mai mare divizor
comun al numerelor initiale:

a : b => r0 =6 0
b : r0 => r1 = 6 0
r0 : r1 => r2 6= 0

rn2 : rn1 => rn 6= 0


rn1 : rn => 0 ST OP
cmmdc(a, b) = rn
(3.1)

Pseudocod Cod C
integer a, b, r
int a , b , r ;
read a, b
s c a n f ( "%d %d" , &a , &b ) ;
do
do{
r a%b
r = a % b;
ab
a = b;
br
b = r;
while r! = 0
while ( r != 0 )
write a
p r i n t f ( "%d" , a ) ;

3.4 Tema
Exercitiul 3.1. Sa se verifice daca un numar este palindrom (un numar este
palindrom daca este egal cu rasturnatul sau, de exemplu, 131, 2552, 6, 303
sunt palindroame).
Exercitiul 3.2. Sa se citeasca de la tastatura un numar natural n si o cifra
k. Eliminati din numar cifra k. De exemplu, daca se introduc n = 12147 si
k = 1, trebuie sa se construiasca numarul 247.
Exercitiul 3.3. Sa se afiseze toate numerele impare cuprinse ntre 23 si 50.

11
Exercitiul 3.4. Sa se afiseze toate numerele de forma 1xx1 care sunt diviz-
ibile cu 3.
Exercitiul 3.5. Sa se afiseze toate perechile de numere x si y cu proprietatea
ca x + y = 150, x este divizibil cu 7 si y cu 11.
Exercitiul 3.6. Sa se afiseze toti divizorii naturali ai unui numar natural
n.
Exercitiul 3.7. Sa se verifice daca un numar natural n este prim (un
numar este prim daca are exact doi divizori, pe 1 si pe el nsusi).
Exercitiul 3.8 . Sa afiseze primele n numere prime.
Exercitiul 3.9. Sa se citeasca de la tastatura un numar natural n si un
numar prim k. Sa se calculeze la ce putere apare k n descompunerea n
factori primi a numarului n.
Exercitiul 3.10. Sa se afiseze descompunerea n factori primi a unui numar
natural n.
Exercitiul 3.11 . Sa se citeasca de la tastatura n numere ntregi si sa se
calculeze media arimetica a acestora.
Exercitiul 3.12 . Sa se citeasca de la tastatura n numere ntregi si sa se
calculeze valoarea minima introdusa.
Exercitiul 3.13. Pentru n dat, sa se afiseze la consola urmatoarele n linii,
unde pe ultima linie sunt n caractere .
*
**
***
...
*** . . . *
Exercitiul 3.14. Pentru n dat, sa se afiseze la consola urmatoarele n linii,
unde pe prima linie sunt n caractere .
*** . . . *
...
***
**
*

12
Exercitiul 3.15. Sa se citeasca de la tastatura doua numere ntregi n si
x. Sa se introduca de la tastatura alte n numere ntregi si sa se calculeze de
cate ori apare valoarea lui x n sirul de numere (frecventa de aparitie a unui
numar ntr-un sir).
Exercitiul 3.16 . Sa se citeasca de la tastatura numere ntregi pana la
ntal-nirea lui 0 si sa se calculeze suma acestora.
Exercitiul 3.17 . Sa se citeasca de la tastatura numere ntregi pana la
ntal-nirea lui 0 si sa se calculeze valoarea maxima introdusa.
Exercitiul 3.18 . Sa se simuleze aruncarea unui zar de n ori (vezi Anexa
A), afisandu-se valoarea fetei si sa se afiseze de cate ori aparut valoarea 6.
Exercitiul 3.19 . Se arunca doua zaruri (vezi Anexa A) pana la obtinerea
unei duble. Sa se afiseze suma punctelor.
Exercitiul 3.20. Scrieti un program prin care i permiteti utilizatorului sa
ghiceasca un numar generat aleator astfel: programul va genera aleator un
numar ntreg cuprins ntre 1 si 100 (vezi Anexa A; numarul de ncercari
de care dispune utilizatorul este nelimitat; utilizatorul poate opri jocul prin
apasarea tastei 0. Scenariul va fi urmatorul:
1. utilizatorul introduce un numar

2. cat timp (while) numarul introdus este diferit de cel ghicit sau de 0 se
executa urmatoarele

(a) n cazul n care se introduce un numar mai mic se afiseaza mesajul


Prea mic
(b) n cazul n care se introduce un numar mai mare se afiseaza mesajul
Prea mare
(c) se permite introducerea unui nou numar

3. dupa ncheierea buclei while, fie numarul introdus este chiar cel care
trebuia ghicit, caz n care se afiseaza mesajul Ati ghicit din ... ncercari,
fie utilizatorul a introdus 0 pentru a parasi jocul, caz n care afisati
numarul care trebuia ghicit.
Exercitiul 3.21
. Scrieti un program care afiseaza la consola un meniu dupa
urmatorul scenariu:

13
1. la rularea programului, utilizatorului i se va afisa meniul

(a) apasati tasta 1 pentru a introduce un numar de la tastatura


(b) apasati tasta 2 pentru a verifica daca ultimul numar introdus este
par
(c) apasati tasta 3 pentru a verifica daca ultimul numar introdus este
palindrom
(d) apasati tasta 0 pentru iesire

2. n cazul apasarii tastei 1, utilizatorului i se va permite sa introduca un


numar si i se va afisa din nou meniul de mai sus

3. n cazul apasarii tastelor 2 sau 3, se vor face verificarile cerute si se va


afisa un mesaj corespunzator; daca nu a fost introdus nici un numar
pana la apasarea tastelor 2 sau 3 se va afisa un mesaj corespunzator;
n ambele cazuri se va afisa din nou meniul de mai sus

4. programul se opreste la apasarea tastei 0.

Indicatie. Programul poate fi structurat astfel: se afiseaza meniul si i se


permite utilizatorului sa introduca o optiune; cat timp optiunea introdusa
de utilizator este diferita de 0 (while), se verifica valoarea introdusa si se
aplica unul dintre pasii descrisi mai sus; nainte de a se ncheia iteratia se
mai afiseaza nca o data meniul si utilizatorul introduce o optiune noua.

14
Laboratorul 4

Tablouri de date. Vectori

In C, tablourile de date reprezinta colectii de elemente de acelasi tip care


pot fi identificate prin unul sau mai multi indecsi (chei). Tablourile ocupa
un spatiu contiguu de memorie.
Tablourile unidimensionale, denumite si vectori, au proprietatea ca
elementele acestora sunt identificate printr-un singur index, numit si pozitie
sau cheie. Vectorii au urmatoarele caracteristici:

1. identificatorul vectorului (denumirea variabilei de tip vector)

2. tipul de data al elementelor

3. dimensiunea vectorului (numarul maxim de elemente).

Cele trei aspecte de mai sus sunt definite la declararea vectorilor


t i p d a t a i d e n t i f i c a t o r [ dimensiune ] ;

Indecsii sunt numere naturale consecutive de la 0 la (dimensiune vector - 1).


De exemplu, n urma declararii de mai jos
float v [ 100 ] ;

se rezerva n memorie 100 de spatii consecutive de memorie de marimea


sizeof(float). Blocul de memorie se asociaza variabilei v. Elementele vec-
torului v sunt v[0], v[1], . . . , v[99].

Exemplul 4.1. Sa se citeasca de la tastatura n numere ntregi, cu 1 < n <


100 si sa se memoreze ntr-un vector. Calculati suma elementelor din vector.

15
#i n c l u d e < s t d i o . h>
#i n c l u d e < s t d l i b . h>
i n t main ( ) {
int v [ 100 ] , n , i , s ;
p r i n t f ( "n = " ) ;
s c a n f ( "%d" , &n ) ;
for ( i = 0 ; i < n ; i ++){
p r i n t f ( "v [% d] = " , i ) ;
s c a n f ( "%d" , &v [ i ] ) ;
}
s = 0;
for ( i = 0 ; i < n ; i ++)
s = s + v[ i ];
p r i n t f ( "S = %d" , s ) ;
s y s t e m ( " pause " ) ;
return 0 ;
}

4.1 Tema
Exercitiul 4.1. Sa se citeasca de la tastatura n numere ntregi, cu 1 < n <
50 si sa se memoreze ntr-un vector. Calculati produsul elementelor aflate pe
pozitii impare in vector.
Exercitiul 4.2. Sa se citeasca de la tastatura n numere ntregi, cu 1 < n <
50 si sa se memoreze ntr-un vector. Calculati numarul de elemente nule din
vector.
Exercitiul 4.3. Sa se citeasca de la tastatura n numere ntregi, cu 1 < n <
50 si sa se memoreze ntr-un vector. Calculati valoarea maxima din vector.
Exercitiul 4.4. Sa se citeasca de la tastatura n numere ntregi, cu 1 <
n < 50 si sa se memoreze ntr-un vector. Afisati pozitiile pe care se gaseste
valoarea minima din vector.
Exercitiul 4.5. Sa se citeasca de la tastatura n numere ntregi, cu 1 < n <
100 si sa se memoreze ntr-un vector. Afisati numerele n ordinea inversa
citirii.
Exercitiul 4.6. Se introduc succesiv numere pana la ntalnirea lui 0, dar nu
mai multe de 1000. Sa se memoreze numerele introduse ntr-un vector si sa
se calculeze media lor aritmetica.

16
Exercitiul 4.7. Se introduc succesiv numere pana la ntalnirea lui 0, dar nu
mai multe de 100. Sa se memoreze numerele introduse ntr-un vector si sa
se calculeze numarul de elemente pare.

Exercitiul 4.8 . Sa se verifice daca un vector cu n elemente, numere ntregi


citite de la tastatura, este monoton crescator.

Exercitiul 4.9 . Sa se verifice daca cele n elemente ale unui vector formeaza
o progresie aritmetica (n ordinea n care se gasesc n vector).

Exercitiul 4.10. Fiind date doua siruri de numere ntregi v si u cu n,


respectiv m elemente (m < n), sa se verifice daca u este subsir al lui v.

Exercitiul 4.11. Sa se citeasca de la tastatura n numere ntregi, cu 1 <


n < 50 si se memoreze ntr-un vector. Introduceti de la tastatura un numar
ntreg x si verificati daca acesta se gaseste printre elementele vectorului
(cautare secventiala).

Exercitiul 4.12. Sa se citeasca de la tastatura n numere ntregi, cu 1 <


n < 50 si se memoreze ntr-un vector. Introduceti de la tastatura un numar
ntreg x si calculati frecventa de aparitie a acestuia n vector. Afisati pozitiile
pe care a fost gasit.

Exercitiul 4.13 . Sa se insereze numarul 0 pe prima pozitie a unui vector


dat cu n elemente. Elementele initiale vor fi mutate cu o pozitie la dreapta.

Exercitiul 4.14. Se citesc de la tastatura n numere ntregi n ordine crescatoare


si un numar k. Sa se insereze k n sirul dat astfel ncat sa ramana sortat
crescator.

Exercitiul 4.15. Fiind dat un vector cu n numere reale diferite, mutati


maximul din vector pe ultima pozitie a vectorului (prin interschimbarea max-
imului cu ultimul element).

Exercitiul 4.16. Sa mute toate elementele dintr-un sir de n numere ntregi


egale cu un numar k la sfarsitul sirului.

Exercitiul 4.17. Compactati elementele unui vector cu n numere ntregi.


Prin compactare ntelegem eliminarea elementelor nule din vector.

17
Exercitiul 4.18 . Fiind dat un vector cu n elemente, numere ntregi citite de
la tastatura, mutati primul element pe pozitia pe care ar trebui sa se gaseasca
daca vectorul ar fi sortat crescator. Mutati de asemenea, toate elemente mai
mici n stanga acestuia, nu neaparat n ordine crescatoare, si toate numerele
mai mari n dreapta. Folositi o singura parcurgere a vectorului.

Exercitiul 4.19 . Sa se citeasca de la tastatura elementele a doua multimi


A si B si sa se calculeze reuniunea, intersectia si diferenta A B acestora.

Exercitiul 4.20. Introduceti de la tastatura doua numere mari (de cel putin
15 cifre), cifra cu cifra. Memorati ciferele celor doua numere n cate un
vector. Calculati suma si produsul acestora.

Exercitiul 4.21 . Pentru un numar natural n, citit de la tastatura, calculati


scrierea sa n baza 2.

Exercitiul 4.22. Pentru doua numere naturale x si n, citite de la tastatura,


calculati xn ntr-un mod eficient.

18
Laboratorul 5

Tablouri bidimensionale
(Matrice)

Tablourile de date bidimensionale, numite si matrice, au proprietatea


ca fiecare element este identificat prin doua chei. Aceste chei determina linia,
respectiv coloana, pe care care se gaseste elementul n matrice. Numerotarea
liniilor si a coloanelor se face ncepand de la 0. La declararea unei matrice
se va specifica numarul maxim de linii si coloane.
Declararea statica a unei matrice a cu maxim 10 linii si maxim 50 de
coloane, cu elemente numere ntregi se poate realiza astfel
int a [ 1 0 ] [ 5 0 ] ;

Pentru doua variabile ntregi m si n (1 m 10 si 1 n 50), ale


caror valori au fost citite de la tastatura, vom construi o matrice cu m linii
si n coloane, corespunzatoare declararii de mai sus. Valorile matricei vor fi
citite de la tastatura.
f o r ( i = 0 ; i < m; i ++)
f o r ( j = 0 ; j < n ; j ++){
p r i n t f ( "a[%d][%d] = " , i , j ) ;
s c a n f ( "%d" , &a [ i ] [ j ] ) ;
}

Codul de mai jos afiseaza la consola valorile matricei a.


f o r ( i = 0 ; i < m; i ++){
f o r ( j = 0 ; j < n ; j ++)
p r i n t f ( "%d " , a [ i ] [ j ] ) ;
p r i n t f ( "\n" ) ;
}

19
Exemplul 5.1. Sa se citeasca de la tastatura doua valori ntregi m si n
(1 m 100 si 1 n 100) si elementele unei matrice de numere reale
cu m linii si n coloane. Calculati suma elementelor matricei.
#i n c l u d e < s t d i o . h>
#i n c l u d e < s t d l i b . h>
i n t main ( ) {
i n t n , m, i , j ;
f l o a t a [ 10 ] [ 10 ] , s = 0 ;
p r i n t f ( "m = " ) ;
s c a n f ( "%d" , &m) ;
p r i n t f ( "n = " ) ;
s c a n f ( "%d" , &n ) ;
for ( i = 0 ; i < m; i ++)
for ( j = 0 ; j < n ; j ++){
p r i n t f ( "a [% d ][% d] = " , i , j ) ;
s c a n f ( "%f" , &a [ i ] [ j ] ) ;
s = s + a[ i ][ j ];
}
p r i n t f ( "s = %f" , s ) ;
s y s t e m ( " pause " ) ;
return 0 ;
}

5.1 Tema
Exercitiul 5.1. Pentru o matrice patratica de ordin n ( 1 n 100) sa se
afiseze elementele de pe diagonala principala.

Exercitiul 5.2. Pentru o matrice patratica de ordin n ( 1 n 100)


calculati suma elementelor de pe diagonala secundara.

Exercitiul 5.3. Pentru o matrice patratica de ordin n ( 1 n 100)


numarati elementele nule de sub diagonala secundara.

Exercitiul 5.4. Pentru o matrice patratica de ordin n ( 1 n 100)


calculati media aritmetica a elementelor de deasupra diagonalei principale.

Exercitiul 5.5 . Ducand cele doua diagonale ntr-o matrice patratica se


obtin patru zone triunghiulare. Afisati suma componentelor din interiorul
fiecarei zone.

20
Exercitiul 5.6 . Fiind data o matrice patratica de ordin n ( 1 n 100),
stabiliti daca este un patrat magic. O matrice este patrat magic daca suma
elementelor de pe fiecare linie este egala cu suma elementelor de pe fiecare
coloana si cu suma elementelor de pe diagonale.

Exercitiul 5.7. Sa se citeasca de la tastatura doua valori ntregi m si n


(1 m 100 si 1 n 100) si elementele unei matrice de numere ntregi
cu m linii si n coloane. Sa se afiseze indicii coloanelor pentru care suma
elementelor este maxima.

Exercitiul 5.8. Pentru un n a carei valoare este introdusa de la tastatura


(1 n 10) creati un joc de X (calculatorul) si 0 (utilizatorul). Atunci cand
este randul utilizatorului, acesta introduce coordonatele (linia si coloana)
unde doreste sa plaseze un 0. Implementati 3 nivele de dificultate pentru
acest joc: usor - calculatorul alege aleator pozitiile pe care va plasa X (vezi
Anexa A; mediu - calculatorul urmareste sa completeze linia/coloana/diago-
nala cu cele mai mari sanse de victorie (trebuie sa aiba cat mai multe valori
de X deja completate si nici un 0); dificil - ncercati sa gasiti o strategie cat
mai buna.

21
Laboratorul 6

Complexitatea algoritmilor

6.1 Timpul de executie si ordinul de crestere


al unui algoritm
Pentru a estima eficienta de timp a unui algoritm se poate analiza
numarul de operatii care vor fi efectuate, relativ la dimensiunea datelor de
intrare.
Considerand ca instructiunile sunt executate secvential, fara calcul par-
alel, si ca operatiile elementare (adunare, scadere, nmultire, atribuire, eval-
uarea unei expresii logice etc.) au costul o unitate de timp, putem aproxima
timpul de executie al unui algoritm.
In exemplul de mai jos, pentru un n dat, se calculeaza suma S = 1 2 +
2 3 + ... + n (n + 1) ntr-un mod iterativ.

Op. Algoritm Cost Nr. rep.


1 S0 1 1
2 for i 1, n do 2*(n + 1) 1
3 S S + i (i + 1) 1 n
end for

- Operatiile 1 si 3 sunt elementare si au costul 1. In particular, putem


considera ca operatia 3 este formata dintr-o adunare, o nmultire, o
adunare si o atribuire, caz n care costul poate fi evaluat la 4 unitati de
timp. Vom vedea, nsa ca valorile constante nu participa semnificativ
la estimarea complexitatii de timp.

22
- Operatia 2 are costul 2 (n + 1), deoarece se constituie din atribuirea
succesiva a valorilor 1, 2, . . . , n + 1, variabilei i si verificarea conditiei
i <= n pentru fiecare din aceste valori.

- Atunci, timpul de executie se calculeaza n functie de n astfel

T(n) = 1 1 + 2 (n + 1) 1 + 1 n = 3 n + 3.

Considerand valori mari ale datelor de intrare, ordinul de crestere al


timpului de executie al unui algoritm este dat de termenul dominant al aces-
tuia. In cazul exemplului de mai sus, termenul dominant este n.
Pentru a compara diferiti algoritmi din punct de vedere al complexitatii
de timp se folosesc urmatoarele instrumente:

(g(n)) = f (n)| c1 , c2 R+ si n0 N a..




0 c1 g(n) f (n) c2 g(n), n n0 }


O(g(n)) = f (n)| c R+ si n0 N a..


0 f (n) c g(n), n n0 }

In exemplul de mai sus avem ca T(n) (n) si spunem ca algoritmul are


complexitate liniara. Evident, daca T(n) (n), atunci T(n) O(n).
Daca expresia timpului de executie depinde de datele de intrare, asa cum
vom vedea mai jos, n cateva exemple, se pot explicita, n functie de numarul
de operatii executate, cazurile cel mai favorabil, mediu sau cel mai nefavora-
bil. Cel mai util pentru a stabili o limita de timp a unui algoritm este cazul
cel mai nefavorabil. Acest caz descrie numarul maxim de operatii care s-ar
putea efectua la o rulare.
Se poate scrie urmatoarea ierarhie:

O(1) O(log log n) O(log n) O(n) O(n log n) O(n2 ) O(an ).

6.2 Aplicatii
Sa se calculeze timpul de executie pentru urmatorii algoritmi:

Exemplul 6.1. Produsul a doua matrice cu m linii si n coloane, respec-


tiv n linii si p coloane. Matricea c va fi matricea rezultat a nmultirii.

23
Op. Algoritm Cost Nr. rep.
1 for i 0, m 1 do 2*(m + 1) 1
2 for j 0, p 1 do 2*(p + 1) m
3 c[i][j] 0 1 m*p
4 for k 0, n 1 do 2*(n + 1) m*p
5 c[i][j] c[i][j] + a[i][k] 1 m*p*n
b[k][j]
end for
end for
end for

Obtinem T(m, n, p) = 2 (m + 1) + 2 m (p + 1) + m p + 2 m p
(n + 1) + m p n si termenul dominant este m n p.

Exemplul 6.2. Calcularea valorii maxime dintr-un sir de n numere.


Op. Algoritm Cost Nr. rep.
1 max v[0] 1 1
2 for i 1, n 1 do 2*n 1
3 if max < v[i] then 1 n-1
4 max v[i] 1 (n)
end if
end for

Obtinem T(n) = 1 + 2 n + n 1 + (n). Vom evalua expresia lui (n)


n cazurile cel mai favorabil, respectiv cel mai nefavorabil.
Cazul cel mai favorabil: maximul se gaseste pe prima pozitie, atunci
conditia de pe linia 3 este falsa de fiecare data, deci (n) = 0, de unde
rezulta T(n) = 3 n.
Cazul cel mai nefavorabil: sirul este ordonat strict crescator, prin urmare
conditia este de fiecare data adevarata si (n) = n 1, iar T(n) = 4 n 1.
In ambele cazuri termenul dominant este n.

Exemplul 6.3. Cautarea secventiala a unui element x ntr-un sir de n


numere.

24
Op. Algoritm Cost Nr. rep.
1 gasit 0 1 1
2 i0 1 1
3 while i < n AND gasit == 0 do 1 1 (n) + 1
4 if x == v[i] then 1 1 (n)
5 gasit 1 1 2 (n)
else
6 ii+1 1 1 (n) 2 (n)
end if
end while

Obtinem T(n) = 1 + 1 + (1 (n) + 1) + 1 (n) + 2 (n) + (1 (n) 2 (n)) =


3 1 (n) + 3. Si n aceasta situatie vom calcula T(n) pentru cele doua cazuri
extreme.
Cazul cel mai favorabil: elementul se gaseste pe prima pozitie din sir si
dupa o singura iteratie se iese din ciclul while, atunci 1 (n) = 1, de unde
rezulta T(n) = 6.
Cazul cel mai nefavorabil: elementul nu apartine sirului, de unde rezulta
ca n cadrul ciclului while vor fi n iteratii si 1 (n) = n. Atunci, T(n) = 2n+3.

Exemplul 6.4. Cautarea binara a unui element x ntr-un sir ordonat


crescator de n numere.
Op. Algoritm Cost Nr. rep.
1 gasit 0 1 1
2 p0 1 1
3 un1 1 1
4 while p <= u AND gasit == 0 do 1 1 (n) + 1
5 m (p + u)/2 1 1 (n)
6 if x == v[m] then 1 1 (n)
7 gasit 1 1 2 (n)
else
8 if x > v[m] then 1 1 (n) 2 (n)
9 pm+1 1 3 (n)
else
10 um1 1 1 (n) 2 (n) - 3 (n)
end if
end if
end while

Obtinem T(n) = 5 1 (n) 2 (n) + 4.


Cazul cel mai favorabil: elementul cautat se gaseste la mijlocul sirului
initial, caz n care 1 (n) = 1 si 2 (n) = 1. Rezulta T(n) = 8.

25
Cazul cel mai nefavorabil: elementul nu apartine sirului, atunci, T(n) se
poate scrie ca un sir recursiv astfel

T(1) =1
T(n) =a T(n/2) + b, n > 1,

unde a si b sunt constante reale. Prin urmare, timpul de executie are termenul
dominant log n.

6.3 Tema
Calculati timpul de executie si determinati termenul dominant al acestuia
pentru fiecare din algoritmii de mai jos.

Exercitiul 6.1.
x0
for i 0, n 1 do
for j 0, i 1 do
xx+1
end for
end for

Exercitiul 6.2 .
x0
for i 0, n 1 do
j2
while j <= n do
xx+1
j j2
end while
end for

26
Laboratorul 7

Sortari simple

7.1 Sortarea prin insertie


Metoda sortarii unui sir prin insertie reprezinta un algoritm simplu de sortare
prin comparare. Sirul initial, de lungime n, este ordonat n n 1 pasi,
adaugand succesiv, cate un element la un subsir deja sortat. Pentru siruri
aproape ordonate sau de dimensiune mica, acest algoritm este eficient din
punct de vedere al timpului de executie.

Algoritm. Dorim sa sortam crescator cele n elemente v[0], v[1], . . . , v[n1]


ale unui vector v.

1. Consideram subsirul v[0], care este deja ordonat.

2. Pentru fiecare i de la 1 la n 1, subsirul v[0], v[1], . . . , v[i 1] este


sortat si se cauta pozitia corespunzatoare a elementului v[i]: acel k
{0, 1, . . . , i 1} cu proprietatea ca v[k 1] v[i] < v[k]; elementele
v[k], . . . , v[i 1] se muta cu o pozitie la dreapta, iar elementul curent,
v[i], se insereaza pe pozitia k.

27
Op. Algoritm Cost Nr. rep.
1 Insertie(v[], n)
for i 1, n 1 do 2*n 1
2 aux v[i] 1 n1
3 j i1 1 n1
4 while aux < v[j] AND j >= 0 do 1 i (n)
5 v[j + 1] v[j] 1 i (n) 1
6 j j1 1 i (n) 1
end while
7 v[j + 1] aux 1 n1
end for

Pn1 Pn1
Pn1 Obtinem T(n) = 2 n + (n 1) + (n 1) P+n1 i=1 i (n) + i=1 i (n) 1 +
i=1 i (n) 1 + (n 1) = 3 n 1 + 3 i=1 i (n).
Cazul cel mai favorabil: elementele sunt n ordine crescatoare, caz n care
conditia de la operatia 4 se evalueaza de fiecare data ca fiind falsa, atunci
i (n) = 1, oricare ar fi i = 1, n 1 si T(n) = 6 n 4.
Cazul cel mai nefavorabil: elementele sunt n ordine strict descrescatoare;
n aceasta situatie i (n) = i + 1, oricare ar fi i = 1, n 1 si T(n) = 3 n
1 + 3 (2 + 3 + + n) = 32 n (n + 1) + 3 n 4.
La adresa http://www.sorting-algorithms.com/insertion-sort este
studiat mecanismul sortarii prin insertie aplicat pe diferite clase de date de
intrare.

7.2 Sortarea prin selectia minimului


Aceasta sortare face parte, de asemenea, din categoria sortarilor prin comparatie.

Algoritm. Dorim sa sortam crescator cele n elemente v[0], v[1], v[n 1] ale
unui vector v. Pentru fiecare i de la 0 la n 2,

1. Se cauta valoarea minima din sirul v[i], . . . , v[n 1], ramas neordonat.

2. Valoarea minimului se interschimba cu elementul de la pozitia i.

28
Op. Algoritm Cost Nr. rep.
1 Selectie(v[], n)
for i 0, n 2 do 2*n 1
2 poz min i 1 n1
3 for j i + 1, n 1 do 2*(n - i) 1
4 if v[j] < v[poz min] then 1 ni
5 poz min j 1 1i (n)
end if
end for
6 if poz min! = i then 1 n1
7 v[poz min] v[i] 1 2i (n)
end if
end for

Obtinem T(n) = 4 n 2 + n2
P
i=0 {3 n 3 i + 1i (n) + 3 2i (n)}.
Cazul cel mai favorabil: elementele sunt n ordine crescatoare, caz n care
conditiile de la operatiile 4, respectiv 6, se evalueaza de fiecare data ca fiind
false. Prin urmare, 1i (n) = 0 si 2i (n) = 0, oricare ar fi i = 0, n 2.
Cazul cel mai nefavorabil: elementele sunt n ordine strict descrescatoare;
n aceasta situatie 1i (n) = n i 1 si 2i (n) = 1, oricare ar fi i = 0, [ n2 ];
1i (n) = 0 si 2i (n) = 0, oricare ar fi i = [ n2 ] + 1, n 2
In ambele cazuri T(n) este o functie polinomiala de grad 2.
La adresa http://www.sorting-algorithms.com/selection-sort este
studiat mecanismul sortarii prin selectie aplicat pe diferite clase de date de
intrare.

7.3 Tema
Exercitiul 7.1. Implementati algoritmul de sortare prin selectia maximului.

Exercitiul 7.2. Pentru cei doi algoritmi de mai sus, scrieti variantele care
ordoneaza descrescator sirurile.

Exercitiul 7.3 . Sortare prin numarare. Se da un sir de n numere


naturale ale carui elemente fac parte din multimea {0, 1, 2, . . . , m 1}, unde
m este cel mult egal cu n. Sa se sorteze crescator elementele sirului astfel
ncat complexitatea algoritmului folosit sa fie liniara.

29
Idee: se construieste un vector c cu m elemente in care c[i] reprezinta
frecventa de aparitie a lui i n sirul dat, oricare ar fi 0 i < m; se con-
struieste sirul sortat crescator folosind informatia din c. La implementare se
tine cont de timpul de executie astfel ncat complexitatea sa fie O(n + m).

Exercitiul 7.4. Se da un sir de n numere naturale ale carui elemente fac


parte din multimea {0, 1, 2, . . . , m 1}, unde m este cel mult egal cu n. Sa
se modifice vectorul construit la problema anterioara, folosind orice algoritm,
astfel ncat, daca se doreste raspunsul la ntrebarea: cate elemente sunt n
intervalul [a, b], cu 0 a b < m?, sa se obtina n timp constant (aceasta
problema este relevanta atunci cand sirul se modifica foarte rar n timp, nsa
interogari ca cea de mai sus se produc frecvent).

Exercitiul 7.5
. Implementati Heapsort. Vezi documentul Heapsort.pdf.

30
Laboratorul 8

Tehnici de programare.
Recursivitate si Divide et
Impera

8.1 Recursivitate
Un algoritm recursiv este caracterizat de
- apeluri recursive prin care algoritmul se apeleaza pe el nsusi cel putin
o data; valorile parametrilor trebuie sa convearga n urma apelurilor
recursive spre valorile parametrilor dintr-o conditie de oprire
- conditii de oprire care specifica situatiile n care rezultatul poate fi
calculat direct, fara a fi necesar un apel recursiv.
Mai jos este descris un exemplu de algoritm recursiv generic.
alg rec(n)
if n == n0 then
R0
else
alg rec(h(n))
end if
Functia h are proprietatea ca exista k 0 astfel ncat h(k) (n) = h h
h(n) = n0
Exemplul 8.1. Functia de mai jos afiseaza toate numerele mai mici decat
o valoare data n ordine descrescatoare.

31
void p r i n t ( i n t n ) {
i f ( n == 0 ) p r i n t f ( "%d " , n ) ;
else {
p r i n t f ( "%d " , n ) ;
print (n 1 ) ;
}
}

Putem rescrie functia astfel ncat conditia de oprire sa fie nlocuita cu o


conditie de continuare.
void p r i n t ( i n t n ) {
i f ( n >= 0 ) {
p r i n t f ( "%d " , n ) ;
print (n 1 ) ;
}
}

Daca se doreste afisarea numerelor n ordine crescatoare, vom face urmatoarea


modificare.
void p r i n t ( i n t n ) {
i f ( n >= 0 ) {
print (n 1 ) ;
p r i n t f ( "%d " , n ) ;
}
}

O clasa mare de probleme care se pot rezolva cu ajutorul algoritmilor


recursivi o reprezinta implementarea sirurilor recursive.
Exemplul 8.2. Scrieti o functie recursiva care calculeaza valoarea celui de-al
n-lea termen al sirului (xk )k0 dat de formula recursiva:

x0 = 1
1
xk = xk1 + , k > 0.
xk1
Vom defini o functie x() recursiva cu proprietatea ca pentru orice k 0,
x(k) returneaza valoarea termenului xk .
float x ( int k ){
i f ( k == 0 ) return 1 ;
e l s e return x ( k 1 ) + 1 / x ( k 1 ) ;
}

32
Pentru a rezolva si alte tipuri de probleme n mod recursiv este necesar
sa se gaseasca relatii de recursivitate ntre pasii algoritmului iterativ.
Exemplul 8.3. Sa se scrie un subprogram recursiv care sa primeasca ca
parametru un numar natural si sa calculeze suma cifrelor acestuia.
Pentru a rezolva aceasta problema vom defini functia suma cif re : N
N. Urmatoarele proprietati ale functiei se deduc usor:

suma cif re(0) = 0


suma cif re(k) = suma cif re(k/10) + k%2,

si avem implementarea
int s u m a c i f r e ( int k ){
i f ( k == 0 ) return 0 ;
e l s e return s u m a c i f r e ( k / 1 0 ) + k%1 0 ;
}

Exemplul 8.4. Sa se scrie un subprogram recursiv care sa primeasca ca


parametru doua numere ntregi si sa calculeze cel mai mare divizor comun al
acestora.
Daca a = 0, atunci cmmdc(0, b) = b. In caz contrar observam ca au loc
relatiile

cmmdc(a, 0) = a
cmmdc(a, b) = cmmdc(b, a%b).

Putem scrie urmatorul subprogram recursiv.


i n t cmmdc ( i n t a , i n t b ) {
i f ( a == 0 ) return b ;
e l s e i f ( b == 0 ) return a ;
e l s e return cmmdc ( b , a%b ) ;
}

Observatia 8.1. Se observa din exemplele de mai sus ca un algoritm recursiv


poate nlocui o structura repetitiva.

8.2 Divide et Impera


Aceasta tehnica de programare se bazeaza pe urmatoarea strategie

33
- divide: problema initiala este mpartita n subprobleme; fiecare sub-
problema este rezolvata recursiv n aceeasi maniera; atunci cand di-
mensiunea problemei curente se reduce la o valoare suficient de mica,
atunci aceast este rezolvata direct;

- impera: rezultatele obtinute prin rezolvarea subproblemelor sunt com-


binate n asa fel ncat sa se calculeze solutia problemei de dimensiune
mai mare; astfel ca, la revenirea din recursivitate, problema initiala
este rezolvata.

Studiu de caz. Pentru a calcula suma elementelor unui sir format din n
elemente, se poate aplica divide et impera astfel: suma tuturor elementelor
se poate scrie ca suma dintre suma elementelor din prima jumatate si suma
elementelor din a doua jumatate a sirului; fiecare subsir este impartit n
mod recursiv n doua subsiruri; atunci cand un subsir este suficient de mic
(de exemplu, cand este format dintr-un singur element) rezultatul poate fi
calculat direct.
Fie sirul x1 , x2 , x3 , . . . , xn . Se poate construi o functie suma(p, u) =
xp + xp+1 + + xu dupa principiul divide et impera astfel:
int suma ( int x [ ] , int p , int u ) {
i f ( p==u ) return x [ p ] ;
else {
int m = ( p+u ) / 2 ;
return ( suma ( p , m) + suma (m+1 , u ) ) ;
}
}

Exemplul 8.5. Scrieti un algoritm recursiv care sa implementeze cautarea


binara.
Sa presupunem ca avem un sir crescator de numere ntregi memorate
ntr-un vector v si un numar intreg x. Se doreste implementarea unei functii
recursive cb(int v[], int x, int p, int u), care, folosind tehnica divide et impera,
sa returneze 1 daca x apartine sirului v[p], v[p+1], . . . , v[u] si 0 n caz contrar.
Apelul initial va fi cb(v, x, 0, n - 1).

cb ( int v [ ] , int x , int p , int u ) {


i f ( p > u ) return 0 ;
else {
int m = ( p+u ) / 2 ;
i f ( x == v [m] ) return 1 ;

34
e l s e i f ( x < v [m] ) return cb ( v , x , p , m 1 ) ;
e l s e return cb ( v , x , m + 1 , u ) ;
}
}

8.3 Tema
Exercitiul 8.1. Sa se calculeze al n-lea termen al sirului lui Fibonacci:

f0 = 1
f1 = 1
fk = fk1 + fk2 , k > 1.

Exercitiul 8.2. Sa se scrie un subprogram recursiv care sa primeasca ca


parametru un numar natural si sa calculeze n!.

Exercitiul 8.3 . Din numarul 4 se poate obtine orice numar natural n prin
aplicarea urmatoare-lor operatii:

- se adauga la sfarsit cifra 4;

- se adauga la sfarsit cifra 0;

- daca numarul este par, se mparte la 2.

Sa se scrie un program care produce un sir de numere construit conform


regulilor precedente, sir n care primul numar este 4 iar ultimul este n.

Exercitiul 8.4. Metoda coardei. Fie o functie f : [a, b] R de doua


ori derivabila pe [a, b] avand urmatoarele proprietati:

- f (a)f (b) < 0;

- f 0 (x)f 00 (x) 6= 0, oricare ar fi x [a, b];

- f (a)f 0 (a) < 0.


n1 b)
Atunci sirul x0 = a, xn = xn1 f (xn1 ) f (x(xn1 )f (b)
, oricare ar fi n N
converge strict crescator la unica solutie a ecuatiei f (x) = 0.

35
Pentru o functie f cu proprietatile de mai sus sa se calculeze o aproximatie
a solutiei ecuatiei f (x) = 0 cu o eroare  > 0. Functia f si intervalul [a, b]
vor fi alese de catre student. Eroarea va fi introdusa de utilizator (de obicei
mai mica decat 104 ).
Indicatie. Se creeaza o functie care primeste ca parametru un numar
real x si returneaza valoarea reala f (x); la fiecare pas se verifica daca |f (xn )| <
, caz n care xn reprezinta aproximatia solutiei; n caz contrar se calculeaza
urmatorul element din sir si se continua (recursiv).
Exemple de functii care verifica toate conditiile de mai sus

f : [1, 3] R; f (x) = x3 8,

f : [1, 1] R; f (x) = ex 1, unde e 2, 718281.

Exercitiul 8.5. Sa se implementeze un subprogram recursiv care sa calculeze


cu ajutorul tehnicii divide et impera maximul dintre elementele unui sir.

Exercitiul 8.6. Sa se implementeze un subprogram recursiv care sa calculeze


cu ajutorul tehnicii divide et impera cel mai mare divizor comun dintre ele-
mentele unui sir.

Exercitiul 8.7 . Sa se implemente cautarea ternara (o extindere a algorit-


mului de cautare binara, n care, la fiecare pas, sirul curent este mpaartit n
trei subsiruri de dimensiuni aproximativ egale).

Exercitiul 8.8. Problema turnurilor din Hanoi. Se considera n dis-


curi avand raze de dimensiuni distincte si 3 tije identice. Initial toate cele n
discuri sunt asezate pe prima tija n ordine crescatoare dupa raza de sus n
jos. Se cere mutarea tuturor discurilor pe ultima tija n aceeasi configuratie
respectand urmatoarele reguli: la fiecare mutare un singur disc se poate de-
plasa; un disc se poate aseza fie pe o tija libera, fie pe un disc de raza mai
mare.
Indiciu: folosind tehnica divide et impera problema de dimensiune n
(numar de discuri) se poate descompune n mai multe probleme de dimensi-
une maxim n-1.

36
Laboratorul 9

Mergesort si Quicksort

9.1 Sortare prin interclasare (Mergesort)


Aplicand tehnica de programare Divide et Impera, algoritmul de Sortare prin
interclasare se realizeaza astfel: daca sirul curent este netrivial (are cel putin
doua elemente), se mparte n doua subsiruri care urmeaza a fi sortate n
urma procesului recursiv; la pasul de Impera, cele doua subsiruri ordonate
sunt interclasate.
Interclasarea a doua siruri ordonate este un proces prin care se con-
struieste un al treilea sir sortat cu elementele celor doua siruri initiale. In-
terclasarea a doua siruri a si b ordonate crescator ntr-un sir c, de asemenea
ordonat crescator, presupune

- cele doua siruri se parcurg simultan de la stanga la dreapta

- cat timp mai sunt elemente de parcurs si n a si n b: se compara


elementele curente; cel mai mic dintre ele este copiat n c si n sirul
respectiv se trece la elementul urmator

- atunci cand unul dintre siruri a fost parcurs complet, elementele ramase
n celalalt sir sunt copiate n c.

In imaginea de mai jos este exemplificat algoritmul de Sortare prin


interclasare (Mergesort) pentru sirul 10, 4, 2, 7, 1, 9, 13.

37
Se poate scrie urmatoarea functie recursiva.
mergesort(v[], p, u)
if p < u then
m (p + u)/2
mergesort(v, p, m)
mergesort(v, m + 1, u)
interclasare(v, p, m, u)
end if
Functia interclasare() realizeaza interclasarea sirurilor ordonate v[p], v[p +
1], . . . , v[m] si v[m + 1], . . . , v[u] n sirul c[0], c[1], . . . c[u p]. La final, valorile
din vectorul c (sortat) se copiaza napoi n v[p], v[p + 1], . . . v[u].
interclasare(v[], p, m, u)
ip
j m+1
k0
while i <= m AND j <= u do

38
if v[i] <= v[j] then
c[k] v[i]
ii+1
else
c[k] v[j]
j j+1
end if
k k+1
end while
while i <= m do
c[k] v[i]
ii+1
k k+1
end while
while j <= u do
c[k] v[j]
j j+1
k k+1
end while
for i 0, k 1 do
v[i + p] c[i]
end for

9.2 Sortarea rapida (Quicksort)


Ideea acestui algoritm este ca n sirul curent sa se aleaga un element pivot
(primul sau ultimul element, elementul de la mijloc, un element ales aleator
etc.). In urma unei operatii de pivotare, pivotul va fi mutat pe pozitia pe
care ar trebui sa se gaseasca daca sirul ar fi ordonat. Toate elementele mai
mici decat pivotul se vor muta n stanga sa, iar cele mai mari n dreapta. Se
obtin astfel doua subsiruri, separate de un pivot. Elementele fiecarui subsir
se gasesc n zona corespunzatoare n ordinea finala, nsa nu sunt neaparat
sortate. Algoritmul se va aplica n mod recursiv pe fiecare din cele doua
subsiruri.

39
Pivotare. Daca pivotul este ales ca fiind primul element al sirului curent,
pentru a i gasi pozitia n ordinea finala se procedeaza n felul urmator: se
parcurge simultan sirul de la stanga la dreapta ncepand cu cea de-a doua
pozitie si de la dreapta la stanga (cu prioritate) pana la ntalnire; elementele
mai mici decat pivotul, aflate la dreapta se vor interschimba cu cele mai mari
aflate la stanga. In final se interschimba valorea pivotului cu valorea pe care
s-a oprit parcurgerea.
Exemplul 9.1. Vom considera sirul 47, 34, 91, 75, 5, 65, 1, 23 si vom
exemplifica operatia de pivotare.
- se alege 47 ca pivot si se ncepe parcurgerea simultana: 47, 34, 91 75,
5, 65, 1, 23

- cum 34 < 47, rezulta ca 34 trebuie sa ramana pe loc, si se trece la 91:


47, 34, 91, 75, 5, 65, 1, 23

- cum 91 > 47 si 23 < 47, se interschimba 91 cu 23 si se continua


parcurgerea: 47, 34, 23, 75, 5, 65, 1, 91

- are loc o noua interschimbare, a lui 75 cu 1: 47, 34, 23, 1, 5, 65, 75,
91

- cum 65 > 47, se continua parcurgerea spre stanga: 47, 34, 23, 1, 5,
65, 75, 91.

- n acest moment cele doua parcurgeri s-au ntalnit si se face interschim-


barea lui 47 cu 5, obtinand: 5, 34, 23, 1, 47, 65, 75, 91.
Pivotul este asezat pe pozitia finala n sirul ordonat si ramane sa sortam
separat subsirurile 5, 34, 23, 1 si 65, 75, 91. Se va folosi n mod recursiv
aceeasi tehnica pentru fiecare subsir.
Propunem urmatoarea implementare.
quicksort(v[], p, u)
if p < u then
poz pivotare(v, p, u)
quicksort(v, p, poz 1)
quicksort(v, poz + 1, u)
end if
pivotare(v[], p, u)

40
pivot v[p]
sp+1
du
while s <= d do
while v[d] > pivot do
dd1
end while
while v[s] < pivot AND s <= d do
ss+1
end while
if s <= d then
v[s] v[d]
dd1
ss+1
end if
end while
v[p] v[d]
return d

41
Laboratorul 10

Tehnici de programare. Greedy

Algoritmi de tip Greedy pot fi utilizati atunci cand se doreste rezolvarea


unei probleme de optim, a carei solutii se poate construi n etape. Acestia
se caracterizeaza prin faptul ca la fiecare pas se alege mereu cel mai bun
candidat posibil, i.e., optimul local. In general, algoritmii de tip Greedy sunt
eficienti din punct de vedere al complexitatii de timp. Insa nu ntotdeauna,
solutia construita este cea optima.
In cele ce urmeaza vom studia cateva probleme n rezolvarea carora vom
aplica tehnica Greedy. Pentru unele dintre acestea, solutia obtinuta nu este
optima.

10.1 Problema fractionara a rucsacului


Se considera un rucsac avand capacitatea (in kilograme) M si n obiecte
definite prin greutate si valoare (valori strict pozitive). Sa se determine o
modalitate de umplere a rucsacului astfel ncat valoarea totala a obiectelor
introduse sa fie maximala. Obiectele se pot alege si partial.
Pentru a rezolva aceasta problema se pot sorta obiectele descrescator dupa
raportul valoare/greutate. Obiectele se vor introduce n rucsac pe rand, in
ordinea obtinuta. Daca ncap toate obiectele n rucsac, se vor adauga toate.
In caz contrar, se vor adauga obiecte ntregi pe rand, astfel ncat sa nu se
depaseasca M . Ultimul obiect se poate introduce partial, pentru a umple
spatiul ramas n rucsac.
Propunem o implementare n care obiectele sunt memorate ntr-un vector
de structuri, ce poate fi definit astfel.

42
struct o b i e c t {
float greutate ;
float valoare ;
int i n d e x ;
} ob [ 1 0 0 ] ;

Cu ajutorul campului index obiectele pot fi numerotate atunci cand sunt


citite, deoarece, n urma sortarii, se pierde ordinea initiala a acestora.
Solutia n pseudocod este prezentata mai jos, unde sort desc() este o
functie care realizeaza sortarea descrescatoare a vectorului ob dupa raportul
valoare/greutate.
rucsac(ob[], n, M )
sort desc(ob, n)
s0
i0
while s + ob[i].greutate <= M AND i < n do
s s + ob[i].greutate
write Obiectul , i, a fost introdus complet - greutatea , ob[i].greutate,
- valoarea , ob[i].valoare
ii+1
end while
if s < M AND i < n then
write Obiectul , i, a fost introdus partial - greutatea , M - s,
- valoarea , ob[i].valoare*(M - s)/ob[i].greutate
end if

Observatia 10.1. Este usor de demonstrat faptul ca n aceasta implementare


solutia calculata este optima.
Abordarea greedy prezentata mai sus mai garanteaza obtinerea unei solutii
optime n cazul n care problema se modifica astfel ncat obiectele sa nu poata
fi introduse si partial n rucsac? (Raspuns: Nu)

43
10.2 Colorarea unei harti
Se considera o harta cu n tari, numerotate de la 0 la n 1. Pentru oricare
doua tari se nregistreaza relatia de vecinatate. Acest lucru se realizeaza cu
ajutorul unei matrice a, astfel: daca tarile i si j sunt vecine, atunci a[i][j] = 1;
n caz contrar, a[i][j] = 0. Gasiti o modalitate de a colora harta, utilizand
cat mai putine culori, astfel ncat doua tari vecine sa nu aiba aceeasi culoare.
Dispunem de un numar nelimitat de culori c0 , c1 , c2 , . . . .
Aplicand strategia Greedy, vom colora prima tara utilizand culoarea c0 .
La fiecare pas vom ncerca sa coloram urmatoarea tara folosind c0 ; daca
tara are un vecin colorat cu c0 , testam posibilitatea de a folosi culoarea c1 ;
continuam pana cand gasim o culoare valida.

Exemplul 10.1. Consideram o harta cu 7 tari, pentru care relatiile de


vecinatate sunt reprezentate n graficul din imagine. Daca tara i si tara
j sunt vecine, acest lucru se marcheaza printr-o legatura (muchie) n graf.

Cu metoda Greedy descrisa mai sus se obtine urmatoarea colorare:

Nod (tara) 0 1 2 3 4 5 6
Culoare c0 c1 c2 c3 c0 c1 c5

Prin urmare s-au folosit 5 culori. Se poate gasi o solutie mai buna (o
modalitate de a colora harta cu mai putin de 5 culori)? (Raspuns: Da)

44
10.3 Tema
Exercitiul 10.1. Gasiti un exemplu pentru problema nefractionara a ruc-
sacului n care tehnica Greedy nu genereaza o solutie optima.

Exercitiul 10.2. Selectia activitatilor. Fie o multime de activitati care


partajeaza o aceeasi resursa. La un anumit moment de timp, o singura ac-
tivitate poate folosi respectiva resursa (de exemplu activitatile pot fi examene
si resursa poate fi sala de examinare). Pentru fiecare activitate se cunosc
timpul initial si timpul final de desfasurare. Doua activitati sunt compatibile
daca intervalele de timp corespunzatoare sunt disjuncte. Problema este de a
se gasi o submultime maximala de activitati compatibile.
Indiciu: se foloseste tehnica greedy cu oricare din urmatoarele criterii de
selectie: i) cea mai mica durata a unei activitati; ii) timpul cel mai mic de
start al unei activitati; iii) cel mai mic timp de sfarsit; iv) activitatea pentru
care numarul de activitati compatibile este maximal.

Exercitiul 10.3 . Sa se descompuna un sir de n numere ntregi n subsiruri


strict crescatoare astfel ncat numerele lor de ordine din sirul initial sa fie or-
donate crescator n subsirurile formate si numarul subsirurilor sa fie minim.
Idee de rezolvare: se parcurg succesiv elementele sirului initial si pentru
fiecare element xk se cauta un subsir existent la care xk se poate adauga la
sfarsit; daca nu exista un astfel de subsir se creeaza un subsir nou care va
contine ca prim element pe xk ; daca exista mai multe subsiruri la care xk se
poate adauga, se va alege acela care are cel mai mare ultim element.
Observatie: Ultimile elemente din fiecare subsir (care sunt, n particular,
si elemente maxime n aceste subsiruri) formeaza un sir descrescator. Acest
fapt permite utilizarea cautarii binare pentru determinarea subsirului n care
se va plasa elementul xk .

45
Laboratorul 11

Tehnici de programare.
Backtracking

Metoda de programare Backtracking se poate aplica n rezolvarea prob-


lemelor pentru care solutiile se pot scrie pe componente si pentru fiecare
componenta xk se cunoaste multimea Ak de valori posibile. Multimile Ak
trebuie sa fie finite si usor de generat.
La fiecare pas, algoritmul cauta o valoare valida pentru componenta
curenta xk . In cazul n care exista o astfel de valoare n Ak , se contruieste
solutia partiala x1 x2 . . . xk si se continua cu xk+1 . Daca din multimea Ak au
fost ncercate toate valorile, algoritmul se ntoarce la xk1 pentru a cauta
o alta valoare valida din Ak1 . Procedura se ncheie atunci cand au fost
ncercate toate valorile posibile din A1 .
Cu ajutorul metodei Backtracking se genereaza toate solutiile unei prob-
leme. Daca problema este de optim, se pot evalua solutiile gasite pentru a
determina solutiile optime.

11.1 Generarea tuturor permutarilor unei


multimi
Se considera multimea {1, 2, 3, . . . , n}. Ne intereseaza sa determinam toate
permutarile posibile ale acesteia. Pentru a construi o solutie (permutare)
vom folosi un vector x cu n elemente, numerotate de la 0 la n 1. Orice
componenta necompletata a unei solutii partiale va avea valoarea 0. Mai jos
este descrisa o varianta de rezolvare iterativa.

46
backtrack permut(x[], n)
init(x, n)
k0
while k >= 0 do
ok 0
while x[k] < n AND !ok do
x[k] x[k] + 1
if (valid(x, k)) then
ok 1
end if
end while
if (!ok) then
x[k] 0
k k1
else
if (k == n 1) then
print sol(x, n)
else
k k+1
end if
end if
end while

Definim n continuare subprogramele (functiile) utilizate n algoritm.


init(x[], n)
for i 0, n 1 do
x[i] 0
end for

valid(x[], k)
for i 0, k 1 do
if (x[k] == x[i]) then
return 0
end if
end for
return 1

47
print sol(x[], n)
for i 0, n 1 do
write x[i]
end for

Prezentam mai jos o varianta recursiva a algoritmului backtrack permut.


backtrack permut(k, n)
for i 0, n 1 do
x[k] i
if (valid(x, k)) then
if (k < n 1) then
backtrack permut(k + 1, n)
else
print sol(x, n)
end if
end if
end for

11.2 Tema
Exercitiul 11.1. Sa se genereze toate aranjamentele de m elemente din
multimea {1, 2, 3, . . . , n}, unde 0 m n.

Exercitiul 11.2. Sa se genereze toate combinarile de m elemente din multimea


{1, 2, 3, . . . , n}, unde 0 m n.

Exercitiul 11.3 . Rezolvati problema colorarii unei harti utilizand me-


toda Backtracking. Algoritmul va determina toate modalitatile de colorare.
Solutiile obtinute pot fi evaluate, astfel ncat sa se calculeze numarul minim
necesar de culori.

Exercitiul 11.4 . Problema damelor. Sa se genereze toate configuratiile


posibile pentru asezarea a n dame pe o tabla de sah cu n linii si n coloane
astfel ncat sa nu se poata ataca. Doua dame se pot ataca daca se gasesc pe
aceeasi linie, coloana sau diagonala.

48
Exercitiul 11.5 . Intr-o tabara se organizeaza n fiecare seara foc de tabara.
In prima seara toti participantii se aseaza n jurul focului. In cea de-a doua
seara acestia trebuie sa se aseze astfel ncat cei care au fost vecini n prima
seara sa nu mai stea alaturi. Determinati toate posibilitatile de a i aseza pe
cei n participanti n a doua seara.

Exercitiul 11.6. Un profesor doreste sa mparta m proiecte celor n elevi


ai unei grupe (m n) astfel ncat nici un proiect sa nu ramana nealocat. Un
proiect poate fi realizat de mai multi elevi. Sa se genereze toate posibilitatile
de a le mparti.
Observatie. Problema aceasta este echivalenta cu problema de a genera
toate functiile surjective f : {1, 2, . . . , n} {1, 2, . . . , m}.

Exercitiul 11.7 . Jocul Sudoku se compune dintr-un careu patratic cu 9


linii si 9 coloane care se poate mparti n 3x3 blocuri, fiecare cu dimensiunea
de 3x3 campuri. In unele campuri se gasesc deja cifre de la 1 la 9 (n
mod normal sunt deja completate intre 22 si 36 de campuri din cele 81).
Scopul este acela de a fi completate si campurile libere cu cifre de la 1 la 9
astfel ncat, pe toate liniile, coloanele si blocurile, fiecare cifra sa apara doar
o singura data. Problema cere rezolvarea unui Sudoku care se afla ntr-un
fisier sudoku.in. Campurile libere sunt marcate cu *. Se vor afisa pe ecran
toate posibilitatile de a completa careul.
Indiciu: se construieste solutia (formata din toate casutele necompletate
ale careului) folosind tehnica backtracking pentru a genera toate posibilitatile.

49
Laboratorul 12

Tehnici de programare.
Programare dinamica

Programarea dinamica se aplica atunci cand problema de rezolvat se


poate mparti recursiv n subprobleme. Spre deosebire de Divide et Impera,
aceasta metoda este eficienta atunci cand aceleasi subprobleme apar frecvent
n cadrul rezolvarii. Ideea este ca solutia fiecarei subprobleme sa fie calculata
o singura data si salvata pentru a fi folosita ulterior (memoization).
Principiul programarii dinamice este n general folosit n probleme de
optimizare (fiecare solutie are asociata o valoare si se cauta solutiile care au
valoare maxima sau minima), atunci cand sunt ndeplinite urmatoarele doua
criterii: substructura optimala - o solutie optima contine solutii optime ale
subproblemelor si suprapunerea subproblemelor - spatiul subproblemelor
este relativ mic.
Putem mparti un algoritm de programare dinamica n patru pasi:

1. caracterizarea structurii unei solutii optime

2. valoarea asociata unei solutii optime este definita n mod recursiv

3. se calculeaza valoarea asociata unei solutii optime

4. se construieste solutia optima pe baza datelor acumulate.

50
Pentru a calcula valoarea valoarea asociata unei solutii optime se foloseste
una dintre strategiile bottom-up (se calculeaza solutiile optime ale subprob-
lemelor si din aceste informatii se obtine solutia optima a problemei initiale)
sau top-down cu memoization (problema initiala este descompusa si, pe
masura ce subproblemele sunt rezolvate, solutiile sunt salvate pentru a putea
fi refolosite).
Exemplul 12.1. Sirul lui Fibonacci este definit astfel
f0 = 1
f1 = 1
fk = fk1 + fk2 , k > 1.
Implementarea acestuia n mod recursiv poate fi realizata cu ajutorul functiei
long f ( i n t k ) {
i f ( k == 0 | | k == 1 ) return 1 ;
e l s e return f ( k 1 ) + f ( k 2 ) ;
}
insa n abordarea de mai sus, se produc aceleasi apeluri de mai multe ori,
prin urmare aceeasi problema este rezolvata n mod repetat. Complexitatea
algoritmului este exponentiala.
Cu o strategie de tip bottom-up, primii n termeni ai sirului lui Fibonacci
pot fi calculati secvential, ncepand cu cel de-al treilea
f [0] = 1; f [1] = 1;
for ( k = 2 ; k < n ; k++)
f [k] = f [k 1] + f [k 2];
Obtinem un algoritm liniar.

12.1 Subsir strict crescator maximal


Fiind dat sirul a0 , a1 , . . . , an1 , sa se determine un subsir ai0 < ai1 < <
aik1 , astfel ncat k sa fie maxim posibil (cel mai lung subsir strict
crescator).
De exemplu, daca a = (1, 7, 2, 8, 5, 9, 3, 4, 6, 2), atunci un subsir strict
crescator maximal (SCM) este (1, 2, 3, 4, 6).
Pentru a rezolva aceasta problema aplicand principiul programarii dinam-
ice, se poate construi in sir l, de lungime n, cu proprietatea ca li reprezinta
lungimea celui mai lung subsir (al lui a) strict crescator, care are ca ultim
element pe ai . Pentru exemplul de mai sus, l = (1, 2, 2, 3, 3, 4, 3, 4, 5, 2).

51
Acest sir, odata construit, ofera toate informatiile necesare gasirii unui
subsir SCM: valoarea maxima din l reprezinta lungimea unui astfel de sir;
daca valoarea maxima se gaseste pe o pozitie p, atunci, ap reprezinta ul-
timul element al unui subsir SCM; pentru a gasi urmatoarele elemente (de la
dreapta spre stanga) se va parcurge l spre stanga, ncepand cu pozitia p - 1;
doua elemente consecutive dintr-un subsir SCM, aij si aij+1 , au proprietatea
ca aij < aij+1 si lij = lij+1 1.
Pentru exemplul de mai sus, cum valoarea maxima se gaseste pe pozitia
8 n l, rezulta ca un subsir SCM va avea ca ultim element pe a8 = 6. In
continuare, se va cauta n l, ncepand cu pozitia 7, un element cu proprietatea
ca este mai mic cu o unitate ca 5 si ca elementul corespunzator din a este mai
mic ca 6. Primul element care ndeplineste aceste conditii este chiar l7 . Se
continua algoritmul pana cand se construieste un subsir SCM de dimensiune
5.
Pentru a construi sirul l, se va tine cont de faptul ca
l0 = 1
li = 1 + max{lj |0 j i 1 si ai < aj }, i > 0.
Propunem urmatorul algoritm, care calculeaza valorile lui l ntr-o maniera
de tip bottom-up.
calcul l(l[], a[], n)
l[0] 1
for i 1, n 1 do
max 0
for j 0, i 1 do
if (a[i] > a[j] AND l[j] > max) then
max l[j]
end if
end for
l[i] max + 1
end for

Urmatorul algoritm calculeaza, pe baza valorilor din a si l, un subsir


SCM, pe car e l vom nota cu s.
calcul s(s[], l[], a[], n)
poz max 0
for i 1, n 1 do

52
if (l[i] > l[poz max]) then
poz max i
end if
end for
k l[poz max]
s[k 1] a[poz max]
for i k 2, 0, 1 do
j poz max 1
while (a[j] >= a[poz max] AND l[j]! = l[poz max] 1) do
j j1
end while
s[i] a[j]
poz max j
end for

12.2 Tema
Exercitiul 12.1. Inmultirea unui sir de matrice. Se considera un si
r A0 , A1 , . . . , Ak1 de matrice de dimensiune n0 n1 , n1 n2 , . . . , respectiv,
nk1 nk . Stiind ca n multirea matricelor este asociativa, sa se determine o
modalitate de parantetizare a produsului A0 A1 . . . Ak1 astfel ncat numarul
total de nmultiri elementare sa fie minim.

Exemplul 12.2. Fie matricele A0 , A1 si A2 cu dimensiunile 4030, 3010,


respectiv 10 2. Cele doua modalitati de grupare sunt fie (A0 A1 )A2 , fie
A0 (A1 A2 ). In primul caz se efectueaza 40 30 10 + 40 10 2 = 12800 de
nmultiri elementare, iar n cel de-al doilea caz 30102+40302 = 3000 de
nmultiri elementare. Este evident faptul ca este mai eficient sa se calculeze
cel de-al doilea produs.

53
Indicatie: se construieste matricea C de ordin k, cu proprietatea ca
C[i, j] = numarul minim de produse elementare necesare pentru a calcula
produsul Ai , Ai+1 , . . . , Aj , cu 0 i j < k. Elementele acestei matrice
(zona de deasupra diagonalei principale) se pot calcula n mod recursiv ast-
fel:

C[i, i] = 0
C[i, i + 1] = ni ni+1 ni+2
C[i, j] = min{C[i, l] + C[l + 1, j] + ni nl+1 nj+1 |0 i l j < k}.

Exercitiul 12.2 . Se considera un sir de piese de domino. Fiecare piesa


poate fi rotita n jurul centrului sau cu 180 . Sa se determine subsirul de
piese de lungime maxima n care oricare doua piese alaturate au nscrise
acelasi numar: al doilea de pe prima piesa coincide cu primul numar de pe
cea de-a doua.

Exercitiul 12.3
. Cel mai lung subsir comun. Sa se determine cel mai
lung subsir comun a doua siruri de dimensiune n, respectiv m.

54
Anexa A

Generarea de numere aleatoare

Pentru a genera numere aleatoare n C se foloseste functia rand(). Aceasta


functie returneaza un numar ntreg ntre 0 si constanta RAND MAX. Val-
oarea acestei constante este minim 32767.
Rulati programul de mai jos de mai multe ori. Ce observati?
#i n c l u d e <s t d i o . h>
#i n c l u d e <s t d l i b . h>

int main ( ) {
p r i n t f ( "%d %d %d\n" , rand ( ) , rand ( ) , rand ( ) ) ;

system ( " pause " ) ;


return 0 ;
}

De fiecare data se vor afisa aceleasi trei valori. Motivul pentru care se
ntampla acest lucru este pentru ca functia rand() foloseste generatoare de
numere pseudoaleatoare. Generatorul de numere pseudoaleatoare este
un algoritm care construieste un sir de numere cu proprietati asemanatoare
unui sir aleator de numere. Fiecare sir este complet determinat de un seed
(samanta). In mod implicit seed-ul utilizat este 1. Seed-ul poate fi initializat,
nainte de apelurile rand(), folosind functia srand(). O modalitate prin care
la fiecare rulare sa se foloseasca un alt seed este de a utiliza comanda
s r a n d ( time (NULL ) ) ;

Modificati programul de mai sus astfel:


#i n c l u d e <s t d i o . h>
#i n c l u d e <s t d l i b . h>

55
#i n c l u d e <time . h> / / ! ! ! !

int main ( ) {
s r a n d ( time (NULL ) ) ;
p r i n t f ( "%d %d %d\n" , rand ( ) , rand ( ) , rand ( ) ) ;

system ( " pause " ) ;


return 0 ;
}

La fiecare executie se vor genera alte trei numere ntregi.

Observatia A.1. Instructiunea de mai jos genereaza numere ntregi cuprinse


ntre 1 si n
p r i n t f ( "%d \n" , rand ()\% 1 0 + 1 ) ;

Urmatoarea instructiune genereaza numere reale din intervalul [0, 1]


p r i n t f ( "%f \n" , ( f l o a t ) rand ( ) /RAND MAX) ;

56

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