Sunteți pe pagina 1din 277

ALGORITMI SI STRUCTURI DE DATE

Note de curs
(draft v1.1)
Prefat a
Cand dorim s a reprezentam obiectele din lumea real a ntr-un program pe
calculator, trebuie s a avemn vedere:
modelarea obiectelor din lumea real a sub forma unor entit at i matematice
abstracte si tipuri de date,
operat iile pentru nregistrarea, accesul si utilizarea acestor entit at i,
reprezentarea acestor entitat i n memoria calculatorului, si
algoritmii pentru efectuarea acestor operat ii.
Primele doua elemente sunt n esent a de natur a matematica si se refera la
ce structuri de date si operat ii trebuie s a folosim, iar ultimile dou a elemente
implic a faza de implementare si se refera la cum sa realiz am structurile de date
si operat iile. Algoritmica si structurile de date nu pot separate. Desi algoritmica
si programarea pot separate, noi nu vom face acest lucru, ci vom implementa
algoritmii ntr-un limbaj de programare (Pascal, C/C++, Java). Din aceast a cauza
acest curs este si o init iere n algoritmic a si programare.
Scopul cursului este subordonat scopului specializarii (informatica, n cazul
nostru) care este sa preg ateasc a specialisti competent i, cu nalta calicare n
domeniul informaticii, cadre didactice competente n acest domeniu (profesor de
informatica n gimnaziu si liceu), informaticieni n diverse domenii cu prol tehnic,
economic, etc. ce pot ncepe lucrul imediat dup a absolvirea facult at ii.Dezideratul
nal este deci competent a. Competent a ntr-un domeniu de activitate implica
experient a n rezolvarea problemelor din acel domeniu de activitate. At at
competent a cat si experient a n rezolvarea problemelor se pot obt ine numai dac a
permanent se ntreprind eforturi pentru nsusirea de noi cunostint e. De exemplu,
orice informatician (programator sau profesor) care elaboreaza programe pentru
rezolvarea unor probleme diverse, trebuie sa aib a competent e conform schemei
1
:
PROBLEMA
(model fizic)
ALGORITMICA
(model virtual)
PROGRAMARE
Gandire algoritmica Experienta
(rezolvarea de probleme)
Cursul de Algoritmi si structuri de date este util (si chiar necesar) pentru
formarea competent elor si abilitat ilor unui bun programator sau profesor de
informatica. Pentru a vedea care sunt aceste competent e si abilit at i putem, de
1
M. Vlada; E-Learning si Software educat ional; Conferint a Nat ionala de

Inv at amant Virtual,
Bucuresti, 2003
exemplu, s a citim Programa pentru informatic a - Concursul nat ional unic pentru
ocuparea posturilor didactice declarate vacante n nv at am antul preuniversitar.
2

Intr-un fel, primul semestru al cursului Algoritmi si structuri de date este


echivalent cu ceea ce se pred a la informatic an clasa a IX-a iar al doilea semestru cu
clasa a X-a (specializarea: matematica-informatic a, intensiv informatica). Diferent a
este data n primul r and de dicultatea problemelor abordate de c atre noi n cadrul
acestui curs. Din aceasta cauza vom avea n vedere si ce prevede Pograma solar a
pentru clasa a IX-a, Prol real, Specializarea: Matematic a-informatic a, intensiv
informatic a. De asemenea, merita sa vedem ce pareri au cei care au terminat de
cur and o facultate cu un prol de informatic a si care au un nceput de cariera
reusit. Vom nt elege de ce acest curs este orientat pe rezolvarea de probleme.
Alegerea limbajului Java pentru prezentarea implement arilor algoritmilor a
fost f acut a din c ateva considerente. Java veric a validitatea indicilor tablourilor
(programele nu se pot termina printr-o violare de memorie sau eroare de sistem).
Java realizeaza gestiunea automat a a memoriei (recupereaza automat memoria
care nu mai este necesar a programului) ceea ce simplic a scrierea programelor
si permite programatorului sa se concentreze asupra esent ei algoritmului. Exist a
documentat ie pe internet. Compilatorul de Java este gratuit. Un program scris n
Java poate executat pe orice calculator (indiferent de arhitectur a sau sistem de
operare).
Student ii nu sunt obligat i sa realizeze implementarile algoritmilor n Java;
ei pot folosi Pascal sau C/C++. Algoritmii prezentat i n curs sunt descrisi n limbaj
natural sau n limbaj algoritmic iar implement arile sunt n limbajul de programare
Java. Java este un limbaj orientat-obiect, dar noi vom utiliza foarte put in aceasta
particularitate. Sunt prezentate toate elementele limbajului de programare Java
necesare pentru acest curs dar ecesta nu este un curs de programare n Java.
Cunostint ele minimale acceptate la sfarsitul cursului rezult a din Legea nr.
288 din 24 iunie 2004 privind organizarea studiilor universitare si, de exemplu,
din Ghidul calit at ii n nv at am antul superior
3
. Aici se precizeaza faptul c a diploma
de licent a se acorda unui absolvent al programului de studii care: demonstreaza
acumulare de cunostint e si capacitatea de a nt elege aspecte din domeniul
de studii n care s-a format, poate folosi at at cunostint ele acumulate precum
si capacitatea lui de nt elegere a fenomenelor printr-o abordare profesionala
n domeniul de activitate, a acumulat competent e necesare demonstrarii,
argumentarii si rezolvarii problemelor din domeniul de studii considerat, si-a
dezvoltat deprinderi de nvat are necesare procesului de educat ie continu a.
2
Aprobata prin O.M:Ed.C. nr.5287/15.11.2004
3
Editura Universitat ii din Bucuresti, 2004; Capitolul 4, Calitatea programelor de studii uni-
versitare, Prof.univ.dr. Gabriela M. Atanasiu - Universitatea Tehnica Gh.Asachi din Iasi
Cuprins
1 Not iuni fundamentale 1
1.1 Programe ciudate . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Un program ciudat n Pascal . . . . . . . . . . . . . . . . . 1
1.1.2 Un program ciudat n C++ . . . . . . . . . . . . . . . . . . 2
1.1.3 Un program ciudat n Java . . . . . . . . . . . . . . . . . . 3
1.1.4 Structura unui program Java . . . . . . . . . . . . . . . . . 4
1.2 Conversii ale datelor numerice . . . . . . . . . . . . . . . . . . . . 5
1.2.1 Conversia din baza 10 n baza 2 . . . . . . . . . . . . . . . . 5
1.2.2 Conversia din baza 2 n baza 10 . . . . . . . . . . . . . . . . 6
1.2.3 Conversii ntre bazele 2 si 2
r
. . . . . . . . . . . . . . . . . 6
2 Structuri de date 7
2.1 Date si structuri de date . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.1 Date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.2 Structuri de date . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Structuri si tipuri de date abstracte . . . . . . . . . . . . . . . . . . 10
2.2.1 Structuri de date abstracte . . . . . . . . . . . . . . . . . . 10
2.2.2 Tipuri de date abstracte . . . . . . . . . . . . . . . . . . . . 10
2.3 Structuri de date elementare . . . . . . . . . . . . . . . . . . . . . 11
2.3.1 Liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3.2 Stive si cozi . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.3 Grafuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.4 Arbori binari . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3.5 Heap-uri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.3.6 Structuri de mult imi disjuncte . . . . . . . . . . . . . . . . 16
3 Algoritmi 17
3.1 Etape n rezolvarea problemelor . . . . . . . . . . . . . . . . . . . . 17
3.2 Algoritmi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2.1 Ce este un algoritm? . . . . . . . . . . . . . . . . . . . . . . 18
3.2.2 Propriet at ile algoritmilor . . . . . . . . . . . . . . . . . . . 20
3.2.3 Tipuri de prelucr ari . . . . . . . . . . . . . . . . . . . . . . 20
v
3.3 Descrierea algoritmilor . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3.1 Limbaj natural . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3.2 Scheme logice . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3.3 Pseudocod . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.4 Limbaj algoritmic . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.4.1 Declararea datelor . . . . . . . . . . . . . . . . . . . . . . . 23
3.4.2 Operat ii de intrare/iesire . . . . . . . . . . . . . . . . . . . 23
3.4.3 Prelucr ari liniare . . . . . . . . . . . . . . . . . . . . . . . . 24
3.4.4 Prelucr ari alternative . . . . . . . . . . . . . . . . . . . . . 24
3.4.5 Prelucr ari repetitive . . . . . . . . . . . . . . . . . . . . . . 25
3.4.6 Subalgoritm . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.4.7 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . 27
3.4.8 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . 30
3.5 Instruct iuni corespondente limbajului algoritmic . . . . . . . . . . 32
3.5.1 Declararea datelor . . . . . . . . . . . . . . . . . . . . . . . 32
3.5.2 Operat ii de intrare/iesire . . . . . . . . . . . . . . . . . . . 34
3.5.3 Prelucr ari liniare . . . . . . . . . . . . . . . . . . . . . . . . 35
3.5.4 Prelucr ari alternative . . . . . . . . . . . . . . . . . . . . . 35
3.5.5 Prelucr ari repetitive . . . . . . . . . . . . . . . . . . . . . . 35
3.5.6 Subprograme . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.5.7 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . 37
3.5.8 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . 52
4 Analiza complexitat ii algoritmilor 55
4.1 Scopul analizei complexitat ii . . . . . . . . . . . . . . . . . . . . . . 55
4.1.1 Complexitatea spat iu . . . . . . . . . . . . . . . . . . . . . 57
4.1.2 Complexitatea timp . . . . . . . . . . . . . . . . . . . . . . 57
4.2 Notat ia asimptotic a . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.2.1 Denire si propriet at i . . . . . . . . . . . . . . . . . . . . . 58
4.2.2 Clase de complexitate . . . . . . . . . . . . . . . . . . . . . 60
4.2.3 Cazul mediu si cazul cel mai defavorabil . . . . . . . . . . . 61
4.2.4 Analiza asimptotic a a structurilor fundamentale . . . . . . 62
4.3 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.3.1 Calcularea maximului . . . . . . . . . . . . . . . . . . . . . 62
4.3.2 Sortarea prin select ia maximului . . . . . . . . . . . . . . . 62
4.3.3 Sortarea prin insert ie . . . . . . . . . . . . . . . . . . . . . . 63
4.3.4 Sortarea rapid a (quicksort) . . . . . . . . . . . . . . . . . . 64
4.3.5 Problema celebritat ii . . . . . . . . . . . . . . . . . . . . . . 66
4.4 Probleme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.4.1 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . 67
4.4.2 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . 69
5 Recursivitate 71
5.1 Funct ii recursive . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.1.1 Funct ii numerice . . . . . . . . . . . . . . . . . . . . . . . . 71
5.1.2 Funct ia lui Ackerman . . . . . . . . . . . . . . . . . . . . . 74
5.1.3 Recursii imbricate . . . . . . . . . . . . . . . . . . . . . . . 74
5.2 Proceduri recursive . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6 Analiza algoritmilor recursivi 77
6.1 Relat ii de recurent a . . . . . . . . . . . . . . . . . . . . . . . . . . 77
6.1.1 Ecuat ia caracteristic a . . . . . . . . . . . . . . . . . . . . . 78
6.1.2 Solut ia generala . . . . . . . . . . . . . . . . . . . . . . . . 78
6.2 Ecuat ii recurente neomogene . . . . . . . . . . . . . . . . . . . . . 80
6.2.1 O form a simpl a . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.2.2 O form a mai general a . . . . . . . . . . . . . . . . . . . . . 81
6.2.3 Teorema master . . . . . . . . . . . . . . . . . . . . . . . . 82
6.2.4 Transformarea recurent elor . . . . . . . . . . . . . . . . . . 84
6.3 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
7 Algoritmi elementari 93
7.1 Operat ii cu numere . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7.1.1 Minim si maxim . . . . . . . . . . . . . . . . . . . . . . . . 93
7.1.2 Divizori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
7.1.3 Numere prime . . . . . . . . . . . . . . . . . . . . . . . . . 95
7.2 Algoritmul lui Euclid . . . . . . . . . . . . . . . . . . . . . . . . . . 95
7.2.1 Algoritmul clasic . . . . . . . . . . . . . . . . . . . . . . . . 95
7.2.2 Algoritmul lui Euclid extins . . . . . . . . . . . . . . . . . . 96
7.3 Operat ii cu polinoame . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.3.1 Adunarea a dou a polinoame . . . . . . . . . . . . . . . . . . 97
7.3.2

Inmult irea a dou a polinoame . . . . . . . . . . . . . . . . . 98
7.3.3 Calculul valorii unui polinom . . . . . . . . . . . . . . . . . 98
7.3.4 Calculul derivatelor unui polinom . . . . . . . . . . . . . . . 98
7.4 Operat ii cu mult imi . . . . . . . . . . . . . . . . . . . . . . . . . . 100
7.4.1 Apartenent a la mult ime . . . . . . . . . . . . . . . . . . . . 100
7.4.2 Diferent a a dou a mult imi . . . . . . . . . . . . . . . . . . . 100
7.4.3 Reuniunea si intersect ia a dou a mult imi . . . . . . . . . . . 101
7.4.4 Produsul cartezian a dou a mult imi . . . . . . . . . . . . . . 101
7.4.5 Generarea submult imilor unei mult imi . . . . . . . . . . . . 102
7.5 Operat ii cu numere ntregi mari . . . . . . . . . . . . . . . . . . . . 104
7.5.1 Adunarea si scaderea . . . . . . . . . . . . . . . . . . . . . . 104
7.5.2 Inmult irea si mp artirea . . . . . . . . . . . . . . . . . . . . 105
7.5.3 Puterea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
7.6 Operat ii cu matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
7.6.1

Inmult irea . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
7.6.2 Inversa unei matrice . . . . . . . . . . . . . . . . . . . . . . 107
8 Algoritmi combinatoriali 109
8.1 Principiul includerii si al excluderii si aplicat ii . . . . . . . . . . . . 109
8.1.1 Principiul includerii si al excluderii . . . . . . . . . . . . . . 109
8.1.2 Determinarea funct iei lui Euler . . . . . . . . . . . . . . . . 110
8.1.3 Num arul funct iilor surjective . . . . . . . . . . . . . . . . . 111
8.1.4 Num arul permut arilor f ar a puncte xe . . . . . . . . . . . . 113
8.2 Principiul cutiei lui Dirichlet si aplicat ii . . . . . . . . . . . . . . . 115
8.2.1 Problema subsecvent ei . . . . . . . . . . . . . . . . . . . . . 115
8.2.2 Problema subsirurilor strict monotone . . . . . . . . . . . . 116
8.3 Numere remarcabile . . . . . . . . . . . . . . . . . . . . . . . . . . 116
8.3.1 Numerele lui Fibonacci . . . . . . . . . . . . . . . . . . . . 116
8.3.2 Numerele lui Catalan . . . . . . . . . . . . . . . . . . . . . 118
8.4 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
9 Algoritmi de cautare 123
9.1 Problema caut arii . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
9.2 Cautarea secvent ial a . . . . . . . . . . . . . . . . . . . . . . . . . . 123
9.3 Cautare binar a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
9.4 Inserare n tabel a . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
9.5 Dispersia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
10 Algoritmi elementari de sortare 129
10.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
10.2 Sortare prin select ie . . . . . . . . . . . . . . . . . . . . . . . . . . 130
10.3 Sortare prin insert ie . . . . . . . . . . . . . . . . . . . . . . . . . . 135
10.3.1 Insert ie directa . . . . . . . . . . . . . . . . . . . . . . . . . 135
10.3.2 Insert ie binar a . . . . . . . . . . . . . . . . . . . . . . . . . 137
10.4 Sortare prin interschimbare . . . . . . . . . . . . . . . . . . . . . . 138
10.5 Sortare prin micsorarea incrementului - shell . . . . . . . . . . . . 139
10.6 Sortare prin partitionare - quicksort . . . . . . . . . . . . . . . . . 140
11 Liste 141
11.1 Liste liniare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
11.2 Cozi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
11.3 Stive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
11.4 Evaluarea expresiilor aritmetice prexate . . . . . . . . . . . . . . 153
11.5 Operat ii asupra listelor . . . . . . . . . . . . . . . . . . . . . . . . . 155
12 Algoritmi divide et impera 159
12.1 Tehnica divide et impera . . . . . . . . . . . . . . . . . . . . . . . . 159
12.2 Ordinul de complexitate . . . . . . . . . . . . . . . . . . . . . . . . 160
12.3 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
12.3.1 Sortare prin interclasare - MergeSort . . . . . . . . . . . . . 161
12.3.2 Placa cu gauri . . . . . . . . . . . . . . . . . . . . . . . . . 162
13 Metoda optimului local - greedy 165
13.1 Metoda greedy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
13.2 Algoritmi greedy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
13.3 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
13.3.1 Plata restului . . . . . . . . . . . . . . . . . . . . . . . . . . 167
13.3.2 Problema continu a a rucsacului . . . . . . . . . . . . . . . . 168
14 Metoda backtracking 169
14.1 Generarea produsului cartezian . . . . . . . . . . . . . . . . . . . . 169
14.1.1 Generarea iterativa a produsului cartezian . . . . . . . . . . 169
14.1.2 Generarea recursiva a produsului cartezian . . . . . . . . . 174
14.2 Metoda bactracking . . . . . . . . . . . . . . . . . . . . . . . . . . 177
14.2.1 Bactracking iterativ . . . . . . . . . . . . . . . . . . . . . . 179
14.2.2 Backtracking recursiv . . . . . . . . . . . . . . . . . . . . . 179
14.3 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
14.3.1 Generarea aranjamentelor . . . . . . . . . . . . . . . . . . . 180
14.3.2 Generarea combinarilor . . . . . . . . . . . . . . . . . . . . 184
14.3.3 Problema reginelor pe tabla de sah . . . . . . . . . . . . . . 194
14.3.4 Turneul calului pe tabla de sah . . . . . . . . . . . . . . . . 196
14.3.5 Problema colorarii h art ilor . . . . . . . . . . . . . . . . . . 198
14.3.6 Problema vecinilor . . . . . . . . . . . . . . . . . . . . . . . 201
14.3.7 Problema labirintului . . . . . . . . . . . . . . . . . . . . . 203
14.3.8 Generarea partit iilor unui num ar natural . . . . . . . . . . 206
14.3.9 Problema parantezelor . . . . . . . . . . . . . . . . . . . . . 210
15 Programare dinamica 211
15.1 Prezentare generala . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
15.2 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
15.2.1 Inmult irea optimal a a matricelor . . . . . . . . . . . . . . . 213
15.2.2 Subsir crescator maximal . . . . . . . . . . . . . . . . . . . 215
15.2.3 Suma n triunghi . . . . . . . . . . . . . . . . . . . . . . . . 219
15.2.4 Subsir comun maximal . . . . . . . . . . . . . . . . . . . . . 220
16 Probleme 229
16.1 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
16.1.1 Poarta - OJI 2002 . . . . . . . . . . . . . . . . . . . . . . . 229
16.1.2 Mouse - OJI 2002 . . . . . . . . . . . . . . . . . . . . . . . 231
16.1.3 Numere - OJI 2003 . . . . . . . . . . . . . . . . . . . . . . . 235
16.1.4 Expresie - OJI 2004 . . . . . . . . . . . . . . . . . . . . . . 238
16.1.5 Reactivi - OJI 2004 . . . . . . . . . . . . . . . . . . . . . . 240
16.1.6 MaxD - OJI 2005 . . . . . . . . . . . . . . . . . . . . . . . . 242
16.1.7 Fract ii - ONI 2001 . . . . . . . . . . . . . . . . . . . . . . . 245
16.1.8 Competit ie dicil a - ONI 2001 . . . . . . . . . . . . . . . . 246
16.1.9 Pentagon - ONI 2002 . . . . . . . . . . . . . . . . . . . . . . 248
x
16.1.10Suma - ONI 2002 . . . . . . . . . . . . . . . . . . . . . . . . 250
16.1.11Masina - ONI 2003 . . . . . . . . . . . . . . . . . . . . . . . 252
16.1.12Sir - ONI 2004 . . . . . . . . . . . . . . . . . . . . . . . . . 254
16.1.13Triangulat ii - OJI 2002 . . . . . . . . . . . . . . . . . . . . 255
16.1.14Spirala - OJI 2003 . . . . . . . . . . . . . . . . . . . . . . . 257
16.1.15Partit ie - ONI 2003 . . . . . . . . . . . . . . . . . . . . . . . 261
Capitolul 1
Not iuni fundamentale

In general, student ii din anul I au cunostint e de programare n Pascal sau


C/C++. Noi vom prezenta implement arile algoritmilor n Java. Nu are prea mare
important a daca este Java, C/C++, Pascal sau alt limbaj de programare. Oricare
ar limbajul de programare, trebuie s a stim n primul r and cum se reprezinta
numerele n memoria calculatorului. Altfel putem avea surprize ciudate.
1.1 Programe ciudate
Daca nu suntem atent i la valorile pe care le pot lua variabilele cu care lucr am,
putem obt ine rezultate gresite chiar daca modalitatea de rezolvare a problemei este
corecta. Prezentam astfel de situat ii n Pascal, C/C++ si Java.
1.1.1 Un program ciudat n Pascal
Iat a un program Pascal n care dorim s a calculam suma 20.000 + 30.000.
var x,y,z:integer;
BEGIN
x:=20000;
y:=30000;
z:=x+y;
write(x,+,y,=,z);
END.
Desi ne asteptam s a apar a ca rezultat 50.000, surpriza este ca pe ecran apare
20000+30000=-15536
1
2 CAPITOLUL 1. NOT IUNI FUNDAMENTALE
Figura 1.1: Un program ciudat n Pascal
1.1.2 Un program ciudat n C++
Iat a un programn C++ n care dorim s a calculam suma 20.000 + 30.000.
#include<iostream.h>
int main()
{
int x,y,z;
x=20000; y=30000; z=x+y;
cout << x <<"+"<<y<<"="<<z;
return 0;
}
Desi ne asteptam s a apar a ca rezultat 50.000, surpriza este ca pe ecran apare
20000+30000=-15536
Figura 1.2: Un program ciudat n C++
1.1. PROGRAME CIUDATE 3
1.1.3 Un program ciudat n Java
Iat a un programn C++ n care dorim s a calculam suma 200.000 300.000.
class Ciudat {
public static void main(String args[]) {
int x,y,z;
x=200000;
y=300000;
z=x*y;
System.out.println(x+"*"+y+"="+z);
}
}
Desi ne asteptam s a apar a ca rezultat 60.000.000.000, surpriza este ca pe ecran
apare
200000*300000=-129542144
Figura 1.3: Un program ciudat n Java
Calculul cu numerele ntregi este relativ simplu. Calculele sunt facute ntr-o
aritmetica modulo N = 2
n
unde n este numarul de bit i ai cuv antului masina.
Exist a masini pe 16, 32 si 64 bit i pentru care N este aproximativ egal cu 6 10
4
,
4 10
9
si respectiv 2 10
19
.
Se pot reprezenta si numerele ntregi negative. Modul curent de reprezentare
este n complement fat a de 2.

In notat ie binar a, bitul cel mai semnicativ este
bitul de semn. Numerele negative sunt cuprinse ntre 2
n1
si 2
n1
1.
Atunci c and valorile obt inute din calcule depasesc marginile permise de tipul
variabilelor implicate n respectivele calcule, se pot obt ine rezultate eronate.
4 CAPITOLUL 1. NOT IUNI FUNDAMENTALE
1.1.4 Structura unui program Java
Un program simplu n Java are urm atoarea structura:
class numeClasa
{
public static void main(String args[])
{
// declarari de variabile
// instruct iuni
}
}
Programul prezentat n sect iunea anterioar a se poate scrie sub forma:
class Ciudat
{
public static void main(String args[])
{
// declarari de variabile
int x,y,z;
// instruct iuni
x=200000;
y=300000;
z=x*y;
System.out.println(x+*+y+=+z);
}
}
Clasa este elementul de baza n Java. Cel mai simplu program n Java este
format dintr-o clas a (numele clasei este la latitudinea programatorului; singura
recomandare este sa nceapa cu liter a mare) si funct ia main.

In exemplul de mai sus sunt declarate trei variabile (x, y si z) de tip int
(adic a de tip ntreg cu semn). Spat iul alocat variabilelor de tip int este de 4 octet i
(32 bit i). Aceasta nseamna ca o astfel de variabil a poate avea valori ntre 2
63
si
2
63
1. Valoarea maxima este de aproximativ 2 miliarde.

In programul anterior x are valoarea 200.000 iar y are valoarea 300.000, deci
produsul are valoarea 60 miliarde care dep aseste cu mult valoarea maxima de 2
miliarde.

In binar, 60 miliarde se scrie (folosint 36 bit i) sub forma


110111111000010001110101100000000000
dar sunt ret inut i numai 32 bit i din partea dreapt a, adic a
11111000010001110101100000000000
Primul bit reprezinta bitul de semn (1 reprezinta semnul - iar 0 reprezint a
semnul +). Aceasta reprezentare trebuie g andit a ca ind o reprezentare n cod
1.2. CONVERSII ALE DATELOR NUMERICE 5
complementar (ea este n memoria calculatorului si toate numerele ntregi cu semn
sunt reprezentate n acest cod).
Reprezentarea n cod direct se obt ine din reprezentarea n cod complementar
(mai precis, trec and prin reprezentarea n cod invers si adun and, n binar, 1):
11111000010001110101100000000000 (cod complementar)
10000111101110001010011111111111 (cod invers)
10000111101110001010100000000000 (cod direct)
Din codul direct se obt ine -129542144 n baza 10. Aceasta este explicat ia acelui
rezultat ciudat!
1.2 Conversii ale datelor numerice
1.2.1 Conversia din baza 10 n baza 2
Fie x = a
n
...a
0
num arul scris n baza 10. Conversia n baza 2 a num arului x
se efectueaza dup a urm atoarele reguli:
Se mparte num arul x la 2 iar restul va reprezenta cifra de ordin 0 a
num arului scris n noua baz a (b
0
).
Catul obt inut la mp art irea anterioar a se mparte la 2 si se obt ine
cifra de ordin imediat superior a num arului scris n noua baz a. Secvent a
de mp art iri se repet a p an a cand se ajunge la c atul 0.
Restul de la a k-a mp art ire va reprezenta cifra b
k1
. Restul de
la ultima mp art ire reprezint a cifra de ordin maxim n reprezentarea
num arului n baza 2.
Metoda conduce la obt inerea rezultatului dup a un num ar nit de mp artiri,
ntrucat n mod inevitabil se ajunge la un c at nul.

In plus, toate resturile obt inute
apart in mult imii 0, 1.
Exemplu.
Fie x = 13 num arul n baza 10. Secvent a de mp art iri este:
(1) se mparte 13 la 2 si se obt ine catul 6 si restul 1 (deci b
0
= 1)
(2) se mparte 6 la 2 si se obt ine catul 3 si restul 0 (deci b
1
= 0)
(3) se mparte 3 la 2 si se obt ine catul 1 si restul 1 (deci b
2
= 1)
(4) se mparte 1 la 2 si se obt ine catul 0 si restul 1 (deci b
3
= 1).
Prin urmare (13)
10
= (1101)
2
.
6 CAPITOLUL 1. NOT IUNI FUNDAMENTALE
1.2.2 Conversia din baza 2 n baza 10
Daca y = b
n
...b
1
b
0
este un numar n baza 2 , atunci reprezentarea n baza 10
se obt ine efectuand calculul (n baza 10):
x = b
n
2
n
+... +b
1
2 +b
0
.
Exemplu. Fie y = 1100. Atunci reprezentarea n baza 10 va
x = 1 2
3
+ 1 2
2
+ 0 2
1
+ 0 2
0
= 12.
1.2.3 Conversii ntre bazele 2 si 2
r
Pentru conversia unui num ar din baza p n baza q se poate converti numarul
din baza p n baza 10, iar acesta se converteste n baza q.

In cazul conversiei unui num ar din baza p = 2 n baza q = 2


r
se poate evita
trecerea prin baza 10 proced andu-se n modul urm ator: se formeaza grupuri de
cate r cifre pornind de la ultima cifra din dreapta, nspre st anga. Fiecare grup de
r cifre va convertit ntr-o cifr a a bazei q.
Fie, spre exemplu: p = 2, q = 16 = p
4
si x = (1011010)
2
.
Se obt in urm atoarele grupuri de c ate 4 cifre binare:
(1010)
2
= A
16
si (0101)
2
= 5
16
.
Deci scrierea numarului x n baza 16 este: (5A)
16
.
Se observa ca a fost completata cu 0, spre stanga, cea mai din st anga grup a,
p an a la formarea grupei complete de 4 cifre binare.

In cazul conversiei unui num ar din baza p = 2


r
n baza q = 2 se poate de
asemenea evita trecerea prin baza 10 proced andu-se n modul urm ator: ecare cifr a
din reprezentarea n baza p = 2
r
se nlocuieste cu r cifre binare care reprezint a
scrierea respectivei cifre n baza 2.
Fie, spre exemplu: p = 16 = 2
4
, q = 2 si x = (3A)
16
.
Se fac urm atoarele nlocuiri de cifre:
3 0011, A 1010.
Deci scrierea numarului x n baza 2 este: (111010)
2
.
Se observa ca nu apar cifrele 0 din st anga scrierii brute (00111010)
2
obt inute
prin nlocuiri.
Capitolul 2
Structuri de date

Inainte de a elabora un algoritm, trebuie s a ne g andim la modul n care


reprezentam datele.
2.1 Date si structuri de date
2.1.1 Date
Datele sunt entit at i purt atoare de informat ie.

In informatica, o data este un
model de reprezentare a informat iei, accesibil unui anumit procesor (om, unitate
centrala, program), model cu care se poate opera pentru a obt ine noi informat ii
despre fenomenele, procesele si obiectele lumii reale.

In functie de modul lor de
organizare, datele pot : elementare (simple) sau structurate.
Datele elementare au caracter atomic, n sensul ca nu pot descompuse n
alte date mai simple. Astfel de date sunt cele care iau ca valori numere sau siruri
de caractere. O data elementar a apare ca o entitate indivizibil a at at din punct de
vedere al informat iei pe care o reprezint a cat si din punct de vedere al procesorului
care o prelucreaz a.
O data elementar a poate privit a la nivel logic (la nivelul procesorului uman)
sau la nivel zic (la nivelul calculatorului).
Din punct de vedere logic, o data poate denit a ca un triplet de forma
(identificator, atribute, valori).
Din punct de vedere zic, o data poate denit a ca o zona de memorie de o
anumit a lungime, situat a la o anumit a adres a absolut a, n care sunt memorate n
timp si ntr-o form a specica valorile datei.
7
8 CAPITOLUL 2. STRUCTURI DE DATE
Identicatorul este un simbol asociat datei pentru a o distinge de alte date si
pentru a o putea referi n cadrul programului.
Atributele sunt proprietat ii ale datei si precizeaza modul n care aceasta va
tratat a n cadrul procesului de prelucrare. Dintre atribute, cel mai important este
atributul de tip care denestete apartenent a datei la o anumit a clas a de date.
O clas a de date este denita de natura si domeniul valorilor datelor care fac
parte din clasa respectiva, de operat iile specice care se pot efectua asupra datelor
si de modelul de reprezentare interna a datelor. Astfel, exista date de tip ntreg,
de tip real, de tip logic, de tip sir de caractere, etc.
O mult ime de date care au aceleasi caracteristici se numeste tip de date.
Evident, un tip de date este o clas a de date cu acelasi mod de interpretare logica
si reprezentare zic a si se caracterizeaza prin valorile pe care le pot lua datele si
prin operat iile care pot efectuate cu datele de tipul respectiv.
De exemplu, tipul ntreg se caracterizeaza prin faptul c a datele care i apart in
pot lua doar valori ntregi, si asupra lor pot efectuate operat ii aritmetice clasice
(adunare, sc adere, nmult ire, mp art ire n mult imea numerelor ntregi, comparat ii).
Se poate considera ca datele organizate sub forma tablourilor unidimensionale
formeaza tipul vector iar datele organizate sub forma tablourilor bidimensionale
formeaza tipul matrice.

In funct ie de natura elementelor care o compun, o structura de date poate :


omogena, atunci c and toate elementele au acelasi tip;
neomogena, atunci c and elementele componente au tipuri diferite.

In functie de num arul datelor care o compun, o structura de date poate :


statica, atunci c and num arul de componente este xat;
dinamic a, atunci c and num arul de componente este variabil.
Din punct de vedere al modului n care sunt utilizate datele pot :
Constante. Valoarea lor nu este si nu poate modicat a n cadrul
algoritmului, ind xat a de la nceputul acestuia. O constanta este
o dat a care pastreaza aceeasi valoare pe tot parcursul procesului de
prelucrare. Pentru constantele care nu au nume, ns asi valoarea lor
este cea prin care se identic a. Constante care au nume (identicator)
sunt init ializate cu o valoare n momentul declararii.
Variabile. Valoarea lor poate modicat a n cadrul algoritmului.

In momentrul declararii lor, variabilele pot init ializate (li se atribuie


o valoare) sau pot neinit ializate (nu li se atribuie nici o valoare).
O variabil a este o data care nu p astreaza neap arat aceeasi valoare pe
parcursul procesului de prelucrare.
Tipul unei date trebuie s a e precizat, n cadrul programului de prelucrare,
printr-o declarat ie de tip ce precede utilizarea respectivei constante sau variabile.
Valorile datei pot numere, sau valori de adev ar, sau siruri de caractere, etc.
2.1. DATE SI STRUCTURI DE DATE 9
2.1.2 Structuri de date
Datele apar frecvent sub forma unor colect ii de date de diferite tipuri, menite
sa faciliteze prelucrarea n cadrul rezolvarii unei anumite probleme concrete.
Datele structurate, numite uneori si structuri de date, sunt constituite din mai
multe date elementare (uneori de acelasi tip, alteori de tipuri diferite), grupate cu
un anumit scop si dup a anumite reguli.
Exemple.
1. Un sir nit de numere reale a
1
, a
2
, ..., a
n
poate reprezentat ca o data
structurat a (tablou unidimensional sau vector).
2. O matrice
_

_
a
1,1
a
1,2
a
1,n
a
2,1
a
2,2
a
2,n

.
.
.
a
m,1
a
m,1
a
m,n
_

_
poate reprezentata ca o dat a structurata (tablou bidimensional) specicnd ecare
element prin doi indici (de linie si de coloana).
O structur a de date este deci o colect ie de date, eventual de tipuri diferite,
pe care s-a denit o anumit a organizare si careia i este specic un anumit mod de
identicare a elementelor componente. Componetele unei structuri de date pot
identicate prin nume sau prin ordinea pe care o ocup a n cadrul structurii.
Daca accesul la o anumita component a a structurii de date se poate face far a
sa t inem seama de celelalte componente, vom spune ca structura de date este cu
acces direct.

In schimb, dac a accesul la o componenta a structurii de date se poate
face numai t in and cont de alte c ampuri ale structurii (n conformitate cu ordinea
structurii, printr-un proces de traversare) atunci vom spune c a structura este cu
acces secvent ial.
Structurile de date pot create pentru a depozitate n memoria intern a
(aceste structuri de date se numesc structuri interne) sau n memoria extern a (se
numesc structuri externe, sau siere). Structurile interne au un caracter de date
temporare (ele dispar odat a cu ncetarea activitat ii de prelucrare) iar cele externe
au un caracter de date permanente (mai bine spus, de lung a durat a).
Daca pe l ang a componentele structurii se nregistreaz a pe suport si alte date
suplimentare care s a materializeze relat ia de ordonare, atunci structura de date
respectiv a este explicit a, n caz contrar este implicita. De exemplu, structura de
date de tip tablou este o structura implicita de date iar structura de date de tip
list a liniar a este o structura explicit a de date.
Asupra structurilor de date se pot efectua operat ii care se refera structura
respectiv a sau la valorile datelor componente. Cele mai importante operat ii sunt:
operat ia de creare, care consta n memorarea pe suportul de memorie a
structurii de date n forma sa init ial a,
10 CAPITOLUL 2. STRUCTURI DE DATE
operat ia de consultare, care consta n accesul la elementele structurii n
vederea prelucr arii valorilor acestora, si
operat ia de actualizare, care consta n adaugarea de noi elemente, sau
eliminarea elementelor care nu mai sunt necesare, sau modicarea valorilor unor
componente ale structurii.
Toate structurile de date la fel organizate si pe care s-au denit aceleasi
operat ii, poart a numele de tip de structur a de date. Daca analizamns a operat iile
care se efectueaz a asupra unei structuri de date, vom putea vedea c a toate acestea
se reduc la executarea, eventual repetat a, a unui grup de operat ii specice numite
operat ii de baz a.
2.2 Structuri si tipuri de date abstracte
2.2.1 Structuri de date abstracte
Abstractizarea datelor reprezint a de fapt concentrarea asupra esent ialului,
ignor and detaliile (sau altfel spus, conteaz a ce nu cum).
St ap anirea aplicat iilor complexe se obt ine prin descompunerea n module.
Un modul trebuie sa e simplu, cu complexitatea ascunsan interiorul lui, si sa
aib a o interfat a simpl a care sa permit a folosirea lui f ar a a cunoaste implementarea.
O structur a de date abstracta este un modul constand din date si operat ii.
Datele sunt ascunse n interiorul modulului si pot accesate prin intermediul
operat iilor. Structura de date este abstract a deoarece este cunoscuta numai interfat a
structurii, nu si implementarea (operat iile sunt date explicit, valorile sunt denite
implicit, prin intermediul operat iilor).
2.2.2 Tipuri de date abstracte
Procesul de abstractizare se refera la dou a aspecte:
abstractizarea procedural a, care separa propriet at ile logice ale unei act iuni de
detaliile implementarii acesteia
abstractizarea datelor, care separa propriet at ile logice ale datelor de detaliile
reprezentarii lor
O structur a de date abstracte are un singur exemplar (o singura instant a).
Pentru a crea mai multe exemplare ale structurii de date abstracte se deneste un
tip de date abstract.

In Java, de exemplu, clasa asigur a un mod direct de denire
a oric arui tip de date abstract.
2.3. STRUCTURI DE DATE ELEMENTARE 11
2.3 Structuri de date elementare
2.3.1 Liste
O list a este o colect ie de elemente de informat ie (noduri) aranjate ntr-o
anumit a ordine. Lungimea unei liste este numarul de noduri din lista. Structura
corespunzatoare de date trebuie s a ne permit a sa determinam ecient care este
primul/ultimul nod n structur a si care este predecesorul/succesorul unui nod dat
(dac a exista). Iat a cum arat a cea mai simpla lista, lista liniar a:
capul
listei
coada
listei
Figura 2.1: List a liniar a
O list a circular a este o lista n care, dup a ultimul nod, urmeaza primul nod,
deci ecare nod are succesor si predecesor.
Cateva dintre operat iile care se efectueaz a asupra listelor sunt: inserarea
(ad augarea) unui nod, extragerea (stergerea) unui nod, concatenarea unor liste,
num ararea elementelor unei liste etc.
Implementarea unei liste se realizeaza n dou a moduri: secvent ial si n ant uit.
Implementarea secvential a se caracterizeaza prin plasarea nodurilor n locat ii
succesive de memorie, n conformitate cu ordinea lor n lista. Avantajele acestui
mod de implementare sunt accesul rapid la predecesorul/succesorul unui nod si
g asirea rapid a a primului/ultimului nod. Dezavantajele sunt modalit at ile relativ
complicate de inserarea/stergere a unui nod si faptul c a, n general, nu se foloseste
ntreaga memorie alocata listei.
Implementarea nl ant uit a se caracterizeaza prin faptul c a ecare nod cont ine
dou a p art i: informat ia propriu-zis a si adresa nodului succesor. Alocarea memoriei
pentru ecare nod se poate face n mod dinamic, n timpul rul arii programului.
Accesul la un nod necesita parcurgerea tuturor predecesorilor s ai, ceea ce conduce
la un consum mai mare de timp pentru aceast a operat ie.

In schimb, operat iile
de inserare/stergere sunt foarte rapide. Se consuma exact atat spat iu de memorie
cat este necesar dar, evident, apare un consum suplimentar de memorie pentru
nregistrarea leg aturii c atre nodul succesor. Se pot folosi dou a adrese n loc de
una, astfel nc at un nod s a cont in a pe lang a adresa nodului succesor si adresa
nodului predecesor. Obt inem astfel o list a dublu inl ant uit a, care poate traversat a
n ambele direct ii.
Listele nl ant uite pot reprezentate prin tablouri.

In acest caz, adresele
nodurilor sunt de fapt indici ai tabloului.
12 CAPITOLUL 2. STRUCTURI DE DATE
O alternativ a este sa folosim dou a tablouri val si next astfel: sa memoram
informat ia ecarui nod i n locat ia val[i], iar adresa nodului s au succesor n locat ia
next[i]. Indicele locat iei primului nod este memorat n variabila p. Vom conveni ca,
pentru cazul listei vide, s a avem p = 0 si next[u] = 0 unde u reprezint a ultimul nod
din lista. Atunci, val[p] va cont ine informat ia primului nod al listei, next[p] adresa
celui de-al doilea nod, val[next[p]] informat ia din al doilea nod, next[next[p]]
adresa celui de-al treilea nod, etc. Acest mod de reprezentare este simplu dar
apare problema gestion arii locat iilor libere. O solut ie este sa reprezentam locat iile
libere tot sub forma unei liste nlant uite. Atunci, stergerea unui nod din lista
init ial a implic a inserarea sa n lista cu locat ii libere, iar inserarea unui nod n lista
init ial a implic a stergerea sa din lista cu locat ii libere. Pentru implementarea listei
de locat ii libere, putem folosi aceleasi tablouri dar avem nevoie de o alt a variabil a,
freehead, care sa cont in a indicele primei locat ii libere din val si next. Folosim
aceleasi convent ii: dac a freehead = 0 nseamna ca nu mai avem locat ii libere, iar
next[ul] = 0 unde ul reprezint a ultima locat ie liber a.
Vom descrie in continuare doua tipuri de liste particulare foarte des folosite.
2.3.2 Stive si cozi
O stiv a este o lista liniar a cu proprietatea ca operat iile de inserare/extragere
a nodurilor se fac n/din coada listei. Daca nodurile A, B, C sunt inserate ntr-o
stiv a n aceasta ordine, atunci primul nod care poate sters/extras este C.

In mod
echivalent, spunem c a ultimul nod inserat este singurul care poate sters/extras.
Din acest motiv, stivele se mai numesc si liste LIFO (Last In First Out).
Cel mai natural mod de reprezentare pentru o stiv a este implementarea
secvent ial a ntr-un tablou S[1..n], unde n este numarul maxim de noduri. Primul
nod va memorat n S[1], al doilea n S[2], iar ultimul n S[top], unde top este
o variabil a care cont ine adresa (indicele) ultimului nod inserat. Init ial, cand stiva
este vida, avem (prin convent ie) top = 0.
O coada este o lista liniar a n care inser arile se fac doar n capul listei, iar
stergerile/extragerile se fac doar din coada listei. Din acest motiv, cozile se mai
numesc si liste FIFO (First In First Out).
O reprezentare secvent ial a pentru o coad a se obt ine prin utilizarea unui
tablou C[0..n1], pe care l trat am ca si cum ar circular: dup a locat ia C[n 1]
urmeaza locat ia C[0]. Fie tail variabila care cont ine indicele locat iei predecesoare
primei locat ii ocupate si e head variabila care cont ine indicele locat iei ocupate
ultima oar a. Variabilele head si tail au aceeasi valoare atunci si numai atunci c and
coada este vid a. Init ial, avem head = tail = 0.
Trebuie sa observam faptul c a testul de coad a vid a este acelasi cu testul de
coad a plin a. Dac a am folosi toate cele n locat ii la un moment dat, atunci nu am
putea distinge ntre situat ia de coad a plin a si cea de coada vid a, deoarece n
ambele situat ii am avea head = tail.

In consecint a, vom folosi efectiv, n orice
moment, cel mult n 1 locat ii din cele n ale tabloului C.
2.3. STRUCTURI DE DATE ELEMENTARE 13
2.3.3 Grafuri
Un graf este o pereche G =< V, M >, unde V este o mult ime de varfuri, iar
M V V este o mult ime de muchii. O muchie de la v arful a la v arful b este
notat a cu perechea ordonat a (a, b), dac a graful este orientat, si cu multimea a, b,
daca graful este neorientat.
Dou a v arfuri unite printr-o muchie se numesc adiacente. Un v arf care este
extremitatea unei singure muchii se numeste varf terminal.
Un drum este o succesiune de muchii de forma
(a
1
, a
2
), (a
2
, a
3
), ..., (a
n1
, a
n
)
sau de forma
a
1
, a
2
, a
2
, a
3
, ..., a
n1
, a
n

dup a cum graful este orientat sau neorientat. Lungimea drumului este egala cu
num arul muchiilor care l constituie. Un drum simplu este un drumn care nici un
v arf nu se repeta. Un ciclu este un drum care este simplu, cu except ia primului si
ultimului v arf, care coincid. Un graf aciclic este un graf f ar a cicluri.
Un graf neorientat este conex, dac a ntre oricare dou a v arfuri exist a un drum.
Pentru grafuri orientate, aceast a notiune este nt arit a: un graf orientat este tare
conex, dac a ntre oricare dou a v arfuri i si j exista un drum de la i la j si un drum
de la j la i.
V arfurilor unui graf li se pot atasa informat ii (numite valori), iar muchiilor
li se pot atasa informat ii numite uneori lungimi sau costuri.
Exist a cel put in trei moduri de reprezentare ale unui graf:
Printr-o matrice de adiacent a A, n care A[i, j] = true daca v arfurile i si j
sunt adiacente, iar A[i, j] = false n caz contrar. O alt a variant a este sa-i d am lui
A[i, j] valoarea lungimii muchiei dintre v arfurile i si j, considerand A[i, j] = +
atunci c and cele dou a v arfuri nu sunt adiacente. Cu aceast a reprezentare, putem
verica usor daca dou a v arfuri sunt adiacente. Pe de alt a parte, dac a dorim s a
a am toate varfurile adiacente unui v arf dat, trebuie s a analizam o ntreag a linie
din matrice. Aceasta necesita n operat ii (unde n este numarul de v arfuri n graf),
independent de num arul de muchii care conecteaza v arful respectiv.
Prin liste de adiacent a, adic a prin atasarea la ecare v arf i a listei de v arfuri
adiacente (pentru grafuri orientate, este necesar ca muchia sa plece din i).

Intr-
un graf cu m muchii, suma lungimilor listelor de adiacenta este 2m, dac a graful
este neorientat, respectiv m, dac a graful este orientat. Daca num arul muchiilor n
graf este mic, aceasta reprezentare este preferabila din punct de vedere al memoriei
necesare. Totusi, pentru a determina dac a dou a v arfuri i si j sunt adiacente, trebuie
sa analizam lista de adiacenta a lui i (si, posibil, lista de adiacenta a lui j), ceea ce
este mai put in ecient dec at consultarea unei valori logicen matricea de adiacent a.
Printr-o list a de muchii. Aceasta reprezentare este ecienta atunci c and
avem de examinat toate muchiile grafului.
14 CAPITOLUL 2. STRUCTURI DE DATE
2.3.4 Arbori binari
Un arbore este un graf neorientat, aciclic si conex. Sau, echivalent, un arbore
este un graf neorientat n care exista exact un drum ntre oricare dou a v arfuri.
Un arbore reprezentat pe niveluri se numeste arbore cu r adacina. V arful
plasat pe nivelul 0 se numeste r adacina arborelui. Pe ecare nivel i > 0 sunt
plasate v arfurile pentru care lungimea drumurilor care le leag a de r ad acin a este i.
Varfurile de pe un nivel i > 0 legate de acelasi varf j de pe nivelul i 1 se
numesc descendent ii direct i (ii) v arfului j iar v arful j se numeste ascendent direct
(tata) al acestor v arfuri.
Daca exista un drum de la un v arf i de pe nivelul ni la un v arf j de pe nivelul
nj > ni, atunci v arful i se numeste ascendent al lui j, iar v arful j se numeste
descendent al lui i.
Un v arf terminal (sau frunz a) este un varf f ar a descendent i. Varfurile care
nu sunt terminale se numesc neterminale.
Un arbore n care orice v arf are cel mult doi descendent i se numeste arbore
binar.

Intr-un arbore cu r ad acin a (reprezentat pe niveluri), adancimea unui v arf


este lungimea drumului dintre r ad acin a si acest varf iar n alt imea unui v arf este
lungimea celui mai lung drum dintre acest varf si un v arf terminal.

In alt imea arborelui este n alt imea rad acinii.

Intr-un arbore binar, num arul maxim de varfuri aate pe nivelul k este 2
k
.
Un arbore binar de n alt ime k are cel mult 2
k+1
1 v arfuri, iar dac a are exact
2
k+1
1 v arfuri, se numeste arbore plin.
Varfurile unui arbore plin se numeroteazan ordinea nivelurilor. Pentru acelasi
nivel, numerotarea se face n arbore de la st anga la dreapta.
nivelul 0
nivelul 1
nivelul 2
nivelul 3
1
2 3
4
5
6 7
8
9 10
11 12 13 14 15
Figura 2.2: Arbore binar plin
Un arbore binar cu n v arfuri si de n alt ime k este complet, dac a se obt ine
din arborele binar plin de n alt ime k, prin eliminarea, dac a este cazul, a varfurilor
numerotate cu n + 1, n + 2, ..., 2
k+1
1.
2.3. STRUCTURI DE DATE ELEMENTARE 15
Acest tip de arbore se poate reprezenta secvent ial folosind un tablou T,
pun and v arfurile de ad ancime k, de la st anga la dreapta, n pozit iile T[2
k
], T[2
k
+1],
..., T[2
k+1
1] (cu posibila except ie a ultimului nivel care poate incomplet).
nivelul 0
nivelul 1
nivelul 2
nivelul 3
1
2 3
4
5
6 7
8
9 10
11 12
Figura 2.3: Arbore binar complet
Tatal unui v arf reprezentat n T[i], i > 0, se aa n T[i/2]. Fiii unui v arf
reprezentat n T[i] se aa, dac a exista, n T[2i] si T[2i + 1].
2.3.5 Heap-uri
Un max-heap (heap=gramad a ordonat a, n traducere aproximativ a) este
un arbore binar complet, cu urm atoarea proprietate: valoarea ecarui varf este
mai mare sau egal a cu valoarea ecarui u al s au.
Un min-heap este un arbore binar complet n care valoarea ecarui v arf este
mai mica sau egal a cu valoarea ecarui u al s au.
nivelul 0
nivelul 1
nivelul 2
nivelul 3
2
3
4 5 6
7
9
10
11
7
7
7
Figura 2.4: Max-heap
16 CAPITOLUL 2. STRUCTURI DE DATE
Acelasi heap poate reprezentat secvent ial prin urm atorul tablou:
11 7 10 7 7 9 2 4 6 5 7 3
Caracteristica de baza a acestei structuri de date este ca modicarea valorii
unui v arf se face foarte ecient, pastr andu-se proprietatea de heap.
De exemplu, ntr-un max-heap, daca valoarea unui varf creste, astfel nc at
depaseste valoarea tatalui, este sucient s a schimbam ntre ele aceste doua valori
si sa continu am procedeul n mod ascendent, pan a cand proprietatea de heap este
restabilit a. Dac a, dimpotriv a, valoarea varfului scade, astfel nc at devine mai mica
dec at valoarea cel put in a unui u, este sucient sa schimbam intre ele valoarea
modicata cu cea mai mare valoare a iilor, apoi s a continu am procesul n mod
descendent, pan a cand proprietatea de heap este restabilita.
Heap-ul este structura de date ideala pentru extragerea maximului/minimului
dintr-o mult ime, pentru inserarea unui varf, pentru modicarea valorii unui v arf.
Sunt exact operat iile de care avem nevoie pentru a implementa o list a dinamica
de priorit at i: valoarea unui varf va da prioritatea evenimentului corespunzator.
Evenimentul cu prioritatea cea mai mare/mica se va aa mereu la radacina
heap-ului, iar prioritatea unui eveniment poate modicat a n mod dinamic.
2.3.6 Structuri de mult imi disjuncte
S a presupunem c a avem N elemente, numerotate de la 1 la N. Numerele care
identic a elementele pot , de exemplu, indici intr-un tablou unde sunt memorate
valorile elementelor. Fie o partitie a acestor N elemente, formata din submult imi
dou a cate dou a disjuncte: S1, S2, ... . Presupunem c a ne intereseaza reuniunea a
dou a submult imi, S
i
S
j
.
Deoarece submult imile sunt doua cate dou a disjuncte, putem alege ca etichet a
pentru o submult ime oricare element al ei. Vom conveni ca elementul minim al unei
mult imi sa e eticheta mult imii respective. Astfel, multimea 3, 5, 2, 8 va numit a
multimea 2.
Vom aloca tabloul set[1..N], n care ecarei locatii set[i] i se atribuie eticheta
submult imii care cont ine elementul i. Avem atunci proprietatea: set[i] i, pentru
1 i N. Reuniunea submult imilor etichetate cu a si b se poate realiza astfel:
procedure reuniune(a, b)
i a;
j b
if i > j
then interschimb a i si j
for k j to N do
if set[k] = j
then set[k] i
Capitolul 3
Algoritmi
3.1 Etape n rezolvarea problemelor
Principalele etape care se parcurg n rezolvarea unei probleme sunt:
(a) Stabilirea datelor init iale si a obiectivului (ce trebuie determinat).
(b) Alegerea metodei de rezolvare.
(c) Aplicarea metodei pentru date concrete.
Exemplu.
S a presupunem c a problema este rezolvarea, n R, a ecuat iei x
2
3x+2 = 0.
(a) Datele init iale sunt reprezentate de catre coecient ii ecuat iei iar
obiectivul este determinarea rad acinilor reale ale ecuat iei.
(b) Vom folosi metoda de rezolvare a ecuat iei de gradul al doilea av and
forma general a ax
2
+ bx + c = 0. Aceasta metoda poate descris a
astfel:
Pasul 1. Se calculeaza discriminantul: = b
2
4ac.
Pasul 2. Daca > 0
atunci ecuat ia are dou a r ad acini reale distincte: x
1,2
=
b

2a
altfel, daca = 0
atunci ecuat ia are o r ad acina real a dubl a: x
1,2
=
b
2a
altfel ecuat ia nu are r ad acini reale.
(c) Aplicarea metodei pentru datele problemei (a = 1, b = 3, c = 2)
conduce la rezultatul: x
1
= 1, x
2
= 2.
17
18 CAPITOLUL 3. ALGORITMI
3.2 Algoritmi
3.2.1 Ce este un algoritm?
Un algoritm este o succesiune de operat ii aritmetice si/sau logice care,
aplicate asupra unor date, permit obt inerea rezultatului unei probleme
din clasa celor pentru care a fost conceput.
S a observam ca nu apare n denit ie cuvantul calculator; algoritmii nu au
neap arat leg atur a cu calculatorul. Totusi, n acest curs ne vom concentra aproape
exclusiv pe algoritmi care pot implementat i rezonabil pe calculator. Altfel spus,
ecare pas din algoritm trebuie astfel g andit nc at ori este suportat direct de catre
limbajul de programare favorit (operat ii aritmetice, cicluri, recursivitate, etc) ori
este aseman ator cu ceva nv at at mai nainte (sortare, cautare binar a, parcurgere
n ad ancime, etc).
Secvent a de pasi prin care este descrisa metoda de rezolvare a ecuat iei de
gradul al doilea (prezentata n sect iunea anterioar a) este un exemplu de algoritm.
Calculul efectuat la Pasul 1 este un exemplu de operat ie aritmetica, iar analiza
semnului discriminantului (Pasul 2) este un exemplu de operat ie logica.
Descrierea unui algoritm presupune precizarea datelor init iale si descrierea
prelucrarilor efectuate asupra acestora. Astfel, se poate spune ca:
algoritm = date + prelucrari
Al-Khwarizmi a fost cel care a folosit pentru prima dat a reguli precise si clare
pentru a descrie procese de calcul (operat ii aritmetice fundamentale) n lucrarea
sa Scurt a carte despre calcul algebric. Mai t arziu, aceasta descriere apare sub
denumirea de algoritm n Elementele lui Euclid. Algoritmul lui Euclid pentru
calculul celui mai mare divizor comun a doua numere naturale este, se pare, primul
algoritm cunoscut n matematic a.

In matematic a not iunea de algoritm a primit mai multe denit ii: algoritmul
normal al lui A. A. Markov, algoritmul operat ional al lui A. A. Leapunov, masina
Turing, funct ii recursive, sisteme POST. S-a demonstrat ca aceste denit ii sunt
echivalente din punct de vedere matematic.

In informatica exista de asemenea mai multe denit ii pentru not iunea de


algoritm. De exemplu, n [35] not iunea de algoritm se deneste astfel:
Un algoritm este sistemul virtual
A = (M, V, P, R, Di, De, Mi, Me)
constituit din urm atoarele elemente:
M - memorie intern a format a din locat ii de memorie si utilizat a pentru
stocarea temporara a valorilor variabilelor;
3.2. ALGORITMI 19
V - mult ime de variabile denite n conformitate cu rat ionamentul R,
care utilizeaz a memoria M pentru stocarea valorilor din V ;
P - proces de calcul reprezentat de o colect ie de instruct iuni/comenzi
exprimate ntr-un limbaj de reprezentare (de exemplu, limbajul
pseudocod); folosind memoria virtual a M si mult imea de variabile
V , instruct iunile implementeaz a/codica tehnicile si metodele care
constituie rat ionamentul R; execut ia instruct iunilor procesului de
calcul determin a o dinamic a a valorilor variabilelor; dup a execut ia
tuturor instruct iunilor din P, solut ia problemei se a a n anumite
locat ii de memorie corespunzatoare datelelor de iesire De;
R - rat ionament de rezolvare exprimat prin diverse tehnici si metode
specice domeniului din care face parte clasa de probleme supuse
rezolvarii (matematica, zica, chimie etc.), care mbinate cu tehnici
de programare corespunzatoare realizeaza act iuni/procese logice,
utiliz and memoria virtual a M si mult imea de variabile V ;
Di - date de intrare care reprezint a valori ale unor parametri care
caracterizeaza ipotezele de lucru/starile init iale ale problemei si
care sunt stocate n memoria M prin intermediul instruct iunilor
de citire/intrare care utilizeaz a mediul de intrare Mi;
De - date de iesire care reprezint a valori ale unor parametri care
caracterizeaza solut ia problemei/starile nale; valorile datelor de
iesire sunt obt inute din valorile unor variabile generate de execut ia
instruct iunilor din procesul de calcul P, sunt stocaten memoria M,
si nregistrate pe un suport virtual prin intermediul instruct iunilor
de scriere/iesire care utilizeaz a mediul de iesire Me; ;
Mi - mediu de intrare care este un dispozitiv virtual de intrare/citire
pentru preluarea valorilor datelor de intrare si stocarea acestora n
memoria virtual a M;
Me - mediu de iesire care este un dispozitiv virtual de iesire/scriere
pentru preluarea datelor din memoria virtual a M si nregistrarea
acestora pe un suport virtual (ecran, h artie, disc magnetic, etc.).
Un limbaj este un mijloc de transmitere a informat iei.
Exist a mai multe tipuri de limbaje: limbaje naturale (engleza, rom an a, etc),
limbaje stiint ice (de exemplu limbajul matematic), limbaje algoritmice, limbaje
de programare (de exemplu Pascal, C, Java), etc.
Un limbaj de programare este un limbaj articial, riguros ntocmit,
care permite descrierea algoritmilor astfel nc at sa poat a transmisi
calculatorului cu scopul ca acesta sa efectueze operat iile specicate.
Un program este un algoritm tradus ntr-un limbaj de programare.
20 CAPITOLUL 3. ALGORITMI
3.2.2 Proprietat ile algoritmilor
Principalele propriet at i pe care trebuie s a le aib a un algoritm sunt:
Generalitate. Un algoritm trebuie s a poat a utilizat pentru o clas a
ntreag a de probleme, nu numai pentru o problem a particular a. Din
aceasta cauza, o metod a de rezolvare a unei ecuat ii particulare nu poate
considerat a algoritm.
Finitudine. Orice algoritm trebuie s a permit a obt inerea rezultatului
dup a un numar nit de prelucrari (pasi). Din aceast a cauza, o metod a
care nu asigur a obt inerea rezultatului dup a un num ar nit de pasi nu
poate considerat a algoritm.
Determinism. Un algoritm trebuie s a prevad a, f ar a ambiguit at i si
f ar a neclarit at i, modul de solut ionare a tuturor situat iilor care pot s a
apar a n rezolvarea problemei. Daca n cadrul algoritmului nu intervin
elemente aleatoare, atunci ori de cate ori se aplica algoritmul aceluiasi
set de date de intrare trebuie s a se obt in a acelasi rezultat.
3.2.3 Tipuri de prelucrari
Prelucrarile care intervin ntr-un algoritm pot simple sau structurate.
Prelucr arile simple sunt atribuiri de valori variabilelor, eventual prin
evaluarea unor expresii;
Prelucr arile structurate pot de unul dintre tipurile:
Liniare. Sunt secvent e de prelucrari simple sau structurate
care sunt efectuate n ordinea n care sunt specicate;
Alternative. Sunt prelucr ari caracterizate prin faptul c a n
funct ie de realizarea sau nerealizarea unei condit ii se alege
una din dou a sau mai multe variante de prelucrare;
Repetitive. Sunt prelucr ari caracterizate prin faptul c a
aceeasi prelucrare (simpl a sau structurat a) este repetata cat
timp este ndeplinit a o anumit a condit ie.
3.3 Descrierea algoritmilor
Algoritmii nu sunt programe, deci ei nu trebuie specicat i ntr-un limbaj de
programare. Detaliile sintactice, de exemplu din Pascal, C/C++ sau Java, nu au
nici o important a n elaborarea/proiectarea algoritmilor.
Pe de alt a parte, descrierea n limba rom an a (ca si n limba engleza [15]) n
mod uzual nu este o idee mai buna. Algoritmii au o serie de structuri - n special
3.3. DESCRIEREA ALGORITMILOR 21
condit ionale, repetitive, si recursivitatea - care sunt departe de a putea descrise
prea usor n limbaj natural. La fel ca orice limb a vorbit a, limba rom ana este plin a
de ambiguit at i, subnt elesuri si nuant e de semnicat ie, iar algoritmii trebuie s a e
descrisi cu o acuratet e maxim posibila.
Cea mai buna metoda de a descrie un algoritm este utilizarea limbajului
pseudocod. Acesta foloseste structuri ale limbajelor de programare si matematicii
pentru a descompune algoritmul n pasi elementari (propozit ii simple), dar care
pot scrise folosind matematica, rom ana curat a, sau un amestec al celor dou a.
Modul exact de structurare a pseudocodului este o alegere personala.
O descriere foarte buna a algoritmului arata structura interna a acestuia,
ascunde detaliile care nu sunt semnicative, si poate implementat a usor de
catre orice programator competent n orice limbaj de programare, chiar dac a el
nu nt elege ce face acel algoritm. Un pseudocod bun, la fel ca si un cod bun, face
algoritmul mult mai usor de nt eles si analizat; el permite de asemenea, mult mai
usor, descoperirea greselilor.
Pe de alt a parte, proba clara se poate face numai pe baza unui program
care sa dea rezultatele corecte! Oamenii sunt oameni! Cineva poate sa insiste ca
algoritmul lui este bun desi ... nu este! Si atunci ... program am!
3.3.1 Limbaj natural
Exemple.
1. Algoritmul lui Euclid. Permite determinarea celui mai mare divizor comun
(cmmdc) a dou a numere naturale a si b. Metoda de determinare a cmmdc poate
descrisa n limbaj natural dup a cum urmeaza.
Se mparte a la b si se ret ine restul r. Se consider a ca nou demp art it vechiul
mp art itor si ca nou mpart itor restul obt inut la mp art irea anterioar a. Operat ia
de mp art ire continu a p an a se obt ine un rest nul. Ultimul rest nenul (care a fost
si ultimul mp art itor) reprezint a rezultatul.
Se observa ca metoda descrisa ndeplineste propriet at ile unui algoritm: poate
aplicat a oric arei perechi de numere naturale iar num arul de prelucrari este nit
(dup a un num ar nit de mp art iri se ajunge la un rest nul).
De asemenea se observa ca prelucrarea principal a a algoritmului este una
repetitiv a, condit ia utilizat a pentru a analiza dac a s-a terminat prelucrarea ind
egalitatea cu zero a restului.
2. Schema lui Horner. Permite determinarea catului si restului mp art irii unui
polinom P[X] = a
n
X
n
+ a
n1
X
n1
+ ... + a
1
X + a
0
= 0 la un binom de forma
X b.
O modalitate simpl a de a descrie metoda de rezolvare este schema urmatoare:
a
n
a
n1
... a
k
... a
2
a
1
a
0
b a
n
bc
n1
+a
n1
... bc
k
+a
k
... bc
2
+a
2
bc
1
+a
1
bc
0
+a
0
... ...
c
n1
c
n2
... c
k1
... c
1
c
0
P[b]
22 CAPITOLUL 3. ALGORITMI
Valorile c
n1
, c
n2
, ..., c
1
, c
0
reprezint a coecient ii catului, iar ultima valoare
calculat a reprezint a valoarea restului (valoarea polinomului calculat a n b).
Si n acest caz prelucrarea principal a este una repetitiv a constandn evaluarea
expresiei bc
k
+a
k
pentru k lu and, n aceasta ordine, valorile n1, n2, ..., 2, 1, 0.
3.3.2 Scheme logice
Scrierea unui program pornind de la un algoritm descris ntr-un limbaj mai
mult sau mai put in riguros, ca n exemplele de mai sus, este dicil a ntrucat nu
sunt pusi n evident a foarte clar pasii algoritmului.
Modalit at i intermediare de descriere a algoritmilor, ntre limbajul natural
sau cel matematic si un limbaj de programare, sunt schemele logice si limbajele
algoritmice.
Schemele logice sunt descrieri grace ale algoritmilor n care ecarui
pas i se ataseaza un simbol grac, numit bloc, iar modul de nl ant uire
a blocurilor este specicat prin segmente orientate.
Schemele logice au avantajul c a sunt sugestive dar si dezavantajul c a pot
deveni dicil de urmarit n cazul unor prelucrari prea complexe. Acest dezavantaj,
dar si evolut ia modului de concepere a programelor, fac ca schemele logice sa e
din ce n ce mai put in folosite (n favoarea limbajelor algoritmice).
3.3.3 Pseudocod
Un limbaj algoritmic este o notat ie care permite exprimarea logicii algorit-
milor ntr-un mod formalizat far a a necesare reguli de sintaxa riguroase, ca n
cazul limbajelor de programare.
Un limbaj algoritmic mai este denumit si pseudocod. Un algoritm descris
n pseudocod cont ine atat enunt uri care descriu operat ii ce pot traduse direct
ntr-un limbaj de programare (unui enunt n limbaj algoritmic i corespunde o
instruct iune n program) c at si enunt uri ce descriu prelucr ari ce urmeaza a
detaliate abia n momentul scrierii programului.
Nu exist a un anumit standard n elaborarea limbajelor algoritmice, ecare
programator put and s a conceapa propriul pseudocod, cu condit ia ca acesta sa
permit a o descriere clar a si neambigu a a algoritmilor. Se poate folosi sintaxa lim-
bajului de programare preferat, n care apar enunt uri de prelucrari. De exemplu:
for ecare v arf v din V
{
culoare[v] = alb;
distanta[v] = innit;
predecesor[v]=-1;
}
3.4. LIMBAJ ALGORITMIC 23
3.4 Limbaj algoritmic

In continuare prezentam un exemplu de limbaj algoritmic.


3.4.1 Declararea datelor
Datele simple se declara sub forma:
<tip> <nume>;
unde <tip> poate lua una dintre valorile: byte, short, int, long, oat, double,
boolean, char.
Tablourile unidimensionale se declar a sub forma:
<tip> <nume> [n
1
..n
2
];
Elementele vectorului pot accesate cu ajutorul unui indice, care poate lua
valori ntre n
1
si n
2
, sub forma:
<nume>[i]
unde i poate lua orice valoare ntre n
1
si n
2
.

In cazul tablourilor bidimensionale, o declarat ie de forma:


<tip> <nume>[m
1
..m
2
][n
1
..n
2
];
specic a o matrice cu m
2
m
1
+1 linii si n
2
n
1
+1 coloane. Fiecare element se
specic a prin doi indici:
<nume>[i][j]
unde i reprezint a indicele liniei si poate avea orice valoare ntre m
1
si m
2
iar j
reprezint a indicele coloanei si poate avea orice valoare ntre n
1
si n
2
.
3.4.2 Operat ii de intrare/iesire
Preluarea valorilor pentru datele de intrare este descrisa sub forma:
read v
1
, v
2
, ...;
unde v
1
, v
2
, ... sunt nume de variabile.
Asarea rezultatelor este descrisa sub forma:
write e
1
, e
2
, ...;
24 CAPITOLUL 3. ALGORITMI
unde e
1
, e
2
, ... sunt expresii (n particular pot constante sau variabile).
Operat ia de atribuire. Operat ia de atribuire a unei valori c atre o variabil a
se descrie prin:
v = <expresie>;
unde v este un nume de variabila, <expresie> desemneaza o expresie aritmetica
sau logica, iar = este operatorul de atribuire. Pentru acesta din urma pot
folosite si alte simboluri, ca de exemplu := sau . Expresiile pot descrise
conform regulilor utilizate n matematic a.
3.4.3 Prelucrari liniare
O secvent a de prelucr ari se descrie n modul urm ator:
<prel 1>;
<prel 2>;
...
<prel n>;
sau
<prel 1>; <prel 2>; ... <prel n>;
O astfel de scriere indic a faptul c a n momentul execut iei prelucr arile se
efectueaza n ordinea n care sunt specicate.
3.4.4 Prelucrari alternative
O prelucrare alternativa complet a (cu dou a ramuri) este descrisa prin:
if <condit ie> <prel 1> else <prel 2>;
sau sub forma
if <condit ie> then <prel 1> else <prel 2>;
unde <condit ie> este o expresie relat ionala. Aceasta prelucrare trebuie nt eleasa
n modul urm ator: dac a condit ia este adev arata atunci se efectueaza prelucrarea
<prel 1>, altfel se efectueaza <prel 2>.
O prelucrare alternativa cu o singur a ramur a se descrie prin:
if <condit ie> <prel>;
sau
if <condit ie> then <prel>;
iar execut ia ei are urmatorul efect: daca condit ia este satisfacuta atunci se efectueaza
prelucrarea specicata, altfel nu se efectueaz a nici o prelucrare ci se trece la
urm atoarea prelucrare a algoritmului.
3.4. LIMBAJ ALGORITMIC 25
3.4.5 Prelucrari repetitive
Prelucrarile repetitive pot de trei tipuri:
cu test init ial,
cu test nal si
cu contor.
Prelucrarea repetitiv a cu test init ial se descrie prin: Prelucrarea repetitiv a cu
test init ial se descrie prin:
while <condit ie> <prel>;
sau
while <condit ie> do <prel>;

In momentul execut iei, atat timp cat condit ia este adevarat a, se va executa
instruct iunea. Daca condit ia nu este la nceput satisf acut a, atunci instruct iunea
nu se efectueaz a niciodata.
Prelucrarea repetitiv a cu test nal se descrie prin:
do <prel> while <condit ie>;
Prelucrarea se repeta p an a cand condit ia specicata devine fals a.

In acest caz
prelucrarea se efectueaza cel put in o dat a, chiar dac a condit ia nu este satisfacuta
la nceput.
Prelucrarea repetitiv a cu contor se caracterizeaza prin repetarea prelucr arii
de un num ar prestabilit de ori si este descrisa prin:
for i = i
1
, i
2
, ..., i
n
<prel>;
sau
for i = i
1
, i
2
, ..., i
n
do <prel>;
unde i este variabila contor care ia, pe r and, valorile i
1
, i
2
, ..., i
n
n aceasta
ordine, prelucrarea ind efectuat a pentru ecare valoare a contorului.
Alte forme utilizate sunt:
for i = v
i
to v
f
do <prel>;
n care contorul ia valori consecutive cresc atoare ntre v
i
si v
f
, si
for i = v
i
downto v
f
do <prel>;
n care contorul ia valori consecutive descrescatoare ntre v
i
si v
f
.
26 CAPITOLUL 3. ALGORITMI
3.4.6 Subalgoritm

In cadrul unui algoritm poate s a apar a necesitatea de a specica de mai


multe ori si n diferite locuri un grup de prelucr ari. Pentru a nu le descrie n
mod repetat ele pot constitui o unitate distinct a, identicabil a printr-un nume,
care este numita subalgoritm. Ori de c ate ori este necesara efectuarea grupului
de prelucrari din cadrul subalgoritmului se specica numele acestuia si, eventual,
datele curente asupra carora se vor efectua prelucrarile. Aceasta act iune se numeste
apel al subalgoritmului, iar datele specicate al aturi de numele acestuia si asupra
carora se efectueaza prelucrarile se numesc parametri.

In urma traducerii ntr-un limbaj de programare un subalgoritm devine un


subprogram.
Un subalgoritm poate descris n felul urm ator:
<nume subalg> (<tip> <nume p1>, <tip> <nume p2>, ... )

...
/* prelucrari specice subalgoritmului */
...
return <nume rezultat>;

unde <nume subalg> reprezint a numele subalgoritmului iar nume p1, nume p2,
... reprezinta numele parametrilor. Ultimul enunt , prin care se returneaz a rezultatul
calculat n cadrul subalgoritmului, este optional.
Modul de apel depinde de modul n care subalgoritmul returneaz a rezultatele
sale. Daca subalgoritmul returneaz a efectiv un rezultat, printr-un enunt de forma
return <nume rezultat>;
atunci subalgoritmul se va apela n felul urm ator:
v=<nume subalg>(nume p1, nume p2, ...);
Acesti subalgoritmi corespund subprogramelor de tip funct ie.
Daca n subalgoritm nu apare un astfel de enunt , atunci el se va apela prin:
<nume subalg>(nume p1, nume p2, ...);
variant a care corespunde subprogramelor de tip procedur a.
Observat ie. Prelucr arile care nu sunt detaliate n cadrul algoritmului sunt
descrise n limbaj natural sau limbaj matematic. Comentariile suplimentare vor
cuprinse ntre /* si */. Daca pe o linie a descrierii algoritmului apare simbolul //
atunci tot ce urmeaza dup a acest simbol, pe aceeasi linie cu el, este interpretat ca
ind un comentariu (deci, nu reprezinta o prelucrare a algoritmului).
3.4. LIMBAJ ALGORITMIC 27
3.4.7 Probleme rezolvate
1. Algoritmului lui Euclid.
Descrierea n pseudocod a algoritmului lui Euclid este urmatoarea:
int a, b, d, i, r;
read a, b;
if (a<b) d=a; i=b; else d=b; i=a; ;
r = d % i;
while (r ! = 0) d=i; i=r; r=d % i; ;
write i;
2. Schema lui Horner.
Descrierea n pseudocod a schemei lui Horner este urmatoarea:
int n, a, b, i;
read n, a, b;
int a[0..n], c[0..n-1];
for i=n,0,-1 read a[i];
c[n-1]=b*a[n];
for i=1,n-1 c[n-i-1]=b*c[n-i]+a[n-i];
val:=b*c[1]+a[1];
write val;
3. Conversia unui num ar natural din baza 10 n baza 2.
Fie n un num ar ntreg pozitiv. Pentru a determina cifrele reprezentarii n
baza doi a acestui num ar se poate folosi urm atoarea metoda:
Se mparte n la 2, iar restul va reprezenta cifra de rang 0. C atul obt inut la
mpartirea anterioar a se mparte din nou la 2, iar restul obt inut va reprezenta cifra
de ordin 1 s.a.m.d. Secvent a de mp art iri continu a pn a la obt inerea unui c at nul.
Descrierea n pseudocod a acestui algoritm este:
int n, d, c, r;
read n;
d = n;
c = d / 2; /* catul mp art irii ntregi a lui d la 2 */
r = d % 2; /* restul mp art irii ntregi a lui d la 2 */
write r;
while (c != 0)
d = c;
c = d / 2; /* catul mp art irii ntregi a lui d la 2 */
r = d % 2; /* restul mp art irii ntregi a lui d la 2 */
write r;

4. Conversia unui num ar ntreg din baza 2 n baza 10.


28 CAPITOLUL 3. ALGORITMI
Daca b
k
b
k1
...b
1
b
0
reprezint a cifrele num arului n baza 2, atunci valoarea n
baza 10 se obt ine efectund calculul:
(b
k
b
k1
...b
1
b
0
)
10
= b
k
2
k
+b
k1
2
k1
+... +b
1
2 +b
0
Desi calculul de mai sus este similar cu evaluarea pentru X = 2 a polinomului
P[X] = b
k
X
k
+b
k1
X
k1
+... +b
1
X +b
0
prelucrare pentru care ar putea folosit algoritmul corespunz ator schemei lui
Horner, n continuare prezentam o alt a variant a de rezolvare a acestei probleme,
care foloseste un subalgoritm pentru calculul puterilor unui num ar ntreg:
int k, i, s;
read k;
int b[0..k];
read b;
s = 0;
for i=0,k s = s+b[i] * putere(2,i);
write s;
putere(int a, int n)

int i, p;
p = 1;
for i=2,n p = p*a;
return p;

5. S a se scrie un algoritm pentru determinarea tuturor divizorilor naturali ai


unui num ar ntreg.
Rezolvare. Fie n num arul ai carui divizori trebuie determinat i. Evident 1 si
[n[ sunt divizori ai lui n. Pentru a determina restul divizorilor este sucient ca
acestia sa e cautat i printre elementele mult imii 2, 3, ..., [[n[] cu [x] desemnand
partea ntreag a a lui x.
Algoritmul poate descris n modul urm ator:
int n, d;
read n;
write 1; /* asarea primului divizor */
for d = 2, [[n[/2]
if (d divide pe n) then write d;
write [n[ /* asarea ultimului divizor */
6. S a se scrie un algoritm pentru determinarea celui mai mare element dintr-
un sir de numere reale.
Rezolvare. Fie x
1
, x
2
, ..., x
n
sirul analizat. Determinarea celui mai mare ele-
ment consta n init ializarea unei variabile de lucru max (care va cont ine valoarea
maximului) cu x
1
si compararea acesteia cu ecare dintre celelalte elemente ale
sirului. Daca valoarea curenta a sirului, x
k
, este mai mare dec at valoarea variaa-
bilei max atunci acesteia din urm a i se va da valoarea x
k
. Astfel, dup a a k 1
comparat ie variabila max va cont ine valoarea maxima din subsirul x
1
, x
2
, ..., x
k
.
Algoritmul poate descris n modul urm ator:
3.4. LIMBAJ ALGORITMIC 29
int k, n;
read n;
double x[1..n], max; /* vectorul si variabila de lucru */
read x; /* preluarea elementelor sirului */
max = x[1];
for k = 2, n
if (max < x[k]) then max = x[k];
write max;
7. S a se aproximeze, cu precizia , limita sirului
s
n
=
n

k=0
1
k!
.
Rezolvare. Calculul aproximativ (cu precizia ) al limitei sirului s
n
consta n
calculul sumei nite s
k
, unde ultimul termen al sumei, t
k
=
1
k!
, are proprietatea
t
k
< .

Intrucat t
k+1
=
t
k
k+1
, aceasta relat ie va folosit a pentru calculul valorii
termenului curent (permit and micsorarea num arului de calcule).
double eps, t, s;
int k;
k=1; /* init ializare indice */
t=1; /* init ializare termen */
s=1; /* init ializare suma */
do
s=s+t; /* ad augarea termenului curent */
k=k+1;
t=t/k; /* calculul urm atorului termen */
while (t eps);
s=s+t; (* ad augarea ultimului termen *)
write s;
8. Fie A o matrice cu m linii si n coloane, iar B o matrice cu n linii si p coloane,
ambele avand elemente reale. S a se determine matricea produs C = A B.
Rezolvare. Matricea C va avea m linii si p coloane, iar ecare element se
determin a efectuand suma:
c
i,j
=
n

k=1
a
i,k
b
k,j
, 1 i m, 1 j p.

In felul acesta calculul elementelor matricei C se efectueaza prin trei cicluri


imbricate (unul pentru parcurgerea liniilor matricei C, unul pentru parcurgerea
coloanelor matricei C, iar unul pentru efectuarea sumei specicate mai sus).
30 CAPITOLUL 3. ALGORITMI
int m, n, p; /* dimensiunile matricelor */
read m, n, p;
double a[1..m][1..n], b[1..n][1..p], c[1..m][1..p]; /* matrice */
int i, j, k; /* indici */
read a; /* citirea matricei a */
read b; /* citirea matricei b */
for i=1,m
for j=1,p
c[i,j]=0;
for k=1,n c[i][j]=c[i][j]+a[i][k]*b[k][j];

write c;
3.4.8 Probleme propuse
1. Fie D o dreapt a de ecuat ie ax+by+c = 0 si (C) un cerc de centru O(x
0
, y
0
)
si raza r. S a se stabileasca pozit ia dreptei fat a de cerc.
Indicat ie. Se calculeaza distant a de la centrul cercului la dreapta D utiliz and
formula:
d =
[ax
0
+by
0
+c[

a
2
+b
2
.
Daca d r+ atunci dreapta este exterioar a cercului, daca d r atunci dreapta
este secanta, iar dac a r < d < r + atunci este tangent a (la implementarea
egalitatea ntre dou a numere reale ...).
2. S a se genereze primele n elemente ale sirurilor a
k
si b
k
date prin relat iile
de recurent a:
a
k+1
=
5a
k
+ 3
a
k
+ 3
, b
k
=
a
k
+ 3
a
k
+ 1
, k 0, a
0
= 1.
3. S a se determine r ad acina p atrat a a unui num ur real pozitiv a cu precizia
= 0.001, folosind relat ia de recurent a:
x
n+1
=
1
2
_
x
n
+
a
x
n
_
, x
1
= a.
Precizia se consider a atins a cand [x
n+1
x
n
[ < .
4. Fie A o matrice patratica de dimensiune n. S a se transforme matricea A,
prin interrschimb ari de linii si de coloane, astfel nc at elementele de pe diagonala
principal a sa e ordonate cresc ator.
3.4. LIMBAJ ALGORITMIC 31
5. S a se determine cel mai mare divizor comun al unui sir de numere ntregi.
6. S a se calculeze coecient ii polinomului
P[X] = (aX +b)
n
, a, b Z, n N.
7. Fie A o matrice patratica. S a se calculeze suma elementelor din ecare zona
(diagonala principal a, diagonala secundar a, etc.) marcata n gura urm atoare:
a. b. c.
Figura 3.1: Zone n matrice patratica
8. Fie x
1
, x
2
, ..., x
n
Z r ad acinile unui polinom cu coecient i ntregi:
P[X] = a
n
X
n
+a
n1
X
n1
+... +a
1
X +a
0
.
S a se determine coecient ii polinomului.
9. S a se determine toate rad acinile rat ionale ale polinomului P[X] care are
coecient i ntregi.
140. Fie [P
1
, P
2
, ..., P
n
] un poligon convex dat prin coordonatele carteziene
ale v arfurilor sale (n ordine trigonometrica). S a se calculeze aria poligonului.
11. Fie f : [a, b] R o funct ie continu a cu proprietatea ca exista un unic
(a, b) care are proprietatea ca f() = 0. S a se aproximeze cu precizia = 0.001
utiliz and metoda bisect iei.
12. Fie P si Qpolinoame cu coecient i ntregi. S a se determine toate rad acinile
rat ionale comune celor dou a polinoame.
13. S a se determine toate numerele prime cu maxim 6 cifre care raman prime
si dup a r asturnarea lor (r asturnatul num arului abcdef este fedcba).
32 CAPITOLUL 3. ALGORITMI
3.5 Instruct iuni corespondente limbajului algorit-
mic
3.5.1 Declararea datelor
Datele simple se declara sub forma:
<tip> <nume>;
sau
<tip> <nume>= literal;
unde <tip> poate lua una dintre urm atoarele valori: byte, short, int, long, oat,
double, boolean, char.

In exemplul urmator sunt prezentate c ateva modalitat i
de declarare pentru diferite tipuri de variabile.
class Literali
{
public static void main(String args[])
{
long l1 = 5L;
long l2 = 12l;
int i1hexa = 0x1;
int i2hexa = 0X1aF;
int i3octal = 01;
long i4octal = 012L;
long i5LongHexa = 0xAL;
float f1 = 5.40F;
float f2 = 5.40f;
float f3 = 5.40e2f;
float f4 = 5.40e+12f;
float f5 = 5.40; // da eroare, trebuie cast
double d1 = 5.40; // implicit este double !
double d2 = 5.40d;
double d3 = 5.40D;
double d4 = 5.40e2;
double d5 = 5.40e+12d;
char c1 = r;
char c2 = \u4567;
}
}
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 33
Java deneste mai multe tipuri primitive de date. Fiecare tip are o anumit a
dimensiune, care este independenta de caracteristicile masinii gazda. Astfel, spre
deosebire de C/C++, unde unntreg poate reprezentat pe 16, 32 sau 64 de bit i, n
funct ie de arhitectura masinii, o valoare de tipntreg n Java va ocupa ntotdeauna
32 de bit i, indiferent de masina pe care ruleaz a. Aceast a consecvent a este esent ial a
deoarece o aceeasi aplicat ie va trebui s a ruleze pe masini cu arhitectur a pe 16, 32
sau 64 de bit i si sa produc a acelasi rezultat pe ecare masina n parte.
Tip Dimensiune Valoare Valoare Valoare Cifre
(octet i) minima maxima initiala semnicative
byte 1 2
7
2
7
1 0
short 2 2
15
2
15
1 0
int 4 2
31
2
31
1 0
long 8 2
63
2
63
1 0
oat 4 +-1.4E-45 +-3.4E+38 0 6-7
double 8 +-4.94E-324 +-1.79E+308 0 14-15
boolean 1 false
char 2 null
Tabelul 3.1: Tipurile primitive de date n Java
Variabilele pot init ializate la declararea lor sau n momentul utiliz arii lor
efective. Daca valoarea nu este specicata explicit atunci variabila se init ializeaza
cu o valoare init ial a implicit a. Tabelul anterior prezint a cateva exemple n acest
sens.
Conversiile ntre diferitele tipuri sunt permise (acolo unde au semnicat ie).
Se vede din tabel c a unele tipuri de variabile au posibilitatea s a reprezinte un
spectru mai mare de numere dec at altele.

In afara tipurilor de baz a, limbajul Java suport a si tipuri de date create


de utilizator, de pild a variabile de tip clas a, interfat a sau tablou. Ca si celelalte
variabile, dac a nu sunt explicit init ializate, valoarea atribuita implicit este null.
Modicatorul static este folosit pentru a specica faptul ca variabila are
o singur a valoare, comuna tuturor instant elor clasei n care ea este declarata.
Modicarea valorii acestei variabile din interiorul unui obiect face ca modicarea
sa e vizibil a din celelalte obiecte. Variabilele statice sunt init ializate la nc arcarea
codului specic unei clase si exist a chiar si daca nu exist a nici o instant a a clasei
respective. Din aceasta cauza, ele pot folosite de metodele statice.
Tablourile unidimensionale se declar a sub forma:
<tip>[ ] <nume> =new <tip>[n];
sau
<tip> <nume>[ ] =new <tip>[n];
34 CAPITOLUL 3. ALGORITMI
Elementele vectorului pot accesate cu ajutorul unui indice, sub forma:
<nume>[i]
unde i poate lua orice valoare ntre 0 si n 1.

In cazul tablourilor bidimensionale, o declarat ie de forma:


<tip>[ ] [ ] <nume> = new <tip>[m][n];
sau
<tip> <nume> [ ] [ ] = new <tip>[m][n];
specic a o matrice cu m linii si n coloane. Fiecare element se specic a prin doi
indici:
<nume>[i][j]
unde i reprezint a indicele liniei si poate avea orice valoare ntre 0 si m 1 iar j
reprezint a indicele coloanei si poate avea orice valoare ntre 0 si n 1.
3.5.2 Operat ii de intrare/iesire
Preluarea unei valori de tip int de la tastatura se poate face sub forma:
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
int vi=Integer.parseInt(br.readLine());
iar dintr-un sier (de exemplu fis.in), sub forma:
StreamTokenizer st = new StreamTokenizer(
new BufferedReader(
new FileReader("fis.in")));
st.nextToken(); int vi = (int) st.nval;
Scrierea valorii unei variabile v pe ecran se poate face sub forma:
System.out.print(v);
iar ntr-un sier (de exemplu fis.out), sub forma:
PrintWriter out = new PrintWriter(
new BufferedWriter(
new FileWriter("fis.out")));
out.print(v);
out.close();
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 35
3.5.3 Prelucrari liniare
O secvent a de prelucr ari se descrie n modul urm ator:
<instr 1>;
<instr 2>;
...
<instr n>;
sau
<instr 1>; <instr 2>; ... <instr n>;
O astfel de scriere indic a faptul c a n momentul execut iei instruct iunile se
efectueaza n ordinea n care sunt specicate.
3.5.4 Prelucrari alternative
O prelucrare alternativa complet a (cu dou a ramuri) este descrisa prin:
if (<condit ie>) <instr 1> else <instr 2>;
unde <condit ie> este o expresie relat ionala. Aceasta prelucrare trebuie nt eleasa
n modul urm ator: dac a condit ia este adev arata atunci se efectueaza prelucrarea
<instr 1>, altfel se efectueaza <instr 2>.
O prelucrare alternativa cu o singur a ramur a se descrie prin:
if (<condit ie>) <instr>;
iar execut ia ei are urmatorul efect: daca condit ia este satisfacuta atunci se efectueaza
instruct iunea specicata, altfel nu se efectueaz a nici o prelucrare ci se trece la
urm atoarea prelucrare a algoritmului.
3.5.5 Prelucrari repetitive
Prelucrarile repetitive pot de trei tipuri:
cu test init ial,
cu test nal si
cu contor.
Prelucrarea repetitiv a cu test init ial se descrie prin:
while (<condit ie>) <instr>;

In momentul execut iei, atat timp cat condit ia este adevarat a, se va executa
prelucrarea. Daca condit ia nu este la nceput satisf acut a, atunci prelucrarea nu se
efectueaza niciodata.
Prelucrarea repetitiv a cu test nal se descrie prin:
36 CAPITOLUL 3. ALGORITMI
do <instr> while (<condit ie>);
Instruct iunea se repet a p an a cand condit ia specicata devine fals a.

In acest
caz prelucrarea se efectueaza cel put in o dat a, chiar dac a condit ia nu este satisfa-
cuta la nceput.
Prelucrarea repetitiv a cu contor se caracterizeaza prin repetarea prelucr arii
de un num ar prestabilit de ori si este descrisa prin:
for(<instr1> ; <conditie>; <instr2>) <instr3>;

In general <instr1> reprezint a etapa de init ializare a contorului, <instr2>


reprezint a etapa de incrementare a contorului, <instr3> reprezint a instruct iunea
care se executa n mod repetat c at timp condit ia <conditie> are valoarea true.
3.5.6 Subprograme

In cadrul unui program poate s a apar a necesitatea de a specica de mai multe


ori si n diferite locuri un grup de prelucr ari. Pentru a nu le descrie n mod repetat
ele pot constitui o unitate distinct a, identicabil a printr-un nume, care este numit a
subprogram sau, mai precis, funct ie (daca returneaza un rezultat) sau procedur a
(daca nu returneaz a nici un rezultat).

In Java funct iile si procedurile se numesc
metode. Ori de c ate ori este necesara efectuarea grupului de prelucr ari din cadrul
programului se specic a numele acestuia si, eventual, datele curente asupra carora
se vor efectua prelucrarile. Aceasta act iune se numeste apel al subprogramului,
iar datele specicate alaturi de numele acestuia si asupra c arora se efectueaza
prelucrarile se numesc parametri.
Un subprogram poate descris n felul urm ator:
<tipr> <nume sp> (<tipp1> <numep1>, <tipp2> <numep2>, ... )

...
/* prelucrari specice subprogramului */
...
return <nume rezultat>;

unde <tipr> reprezint a tipul rezultatului returnat (void daca subprogramul nu


returneaz a nici un rezultat), <nume sp> reprezint a numele subprogramului, iar
numep1, numep2, ... reprezint a numele parametrilor. Ultimul enunt , prin care se
returneaz a rezultatul calculat n cadrul subprogramului, trebuie pus numai dac a
<tipr> nu este void.
Modul de apel depinde de modul n care subprogramul returneaz a rezultatele
sale. Daca subprogramul returneaz a efectiv un rezultat, printr-un enunt de forma
return <nume rezultat>;
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 37
atunci subprogramul se va apela n felul urm ator:
v=<nume sp>(nume p1, nume p2, ...);
Aceste subprograme corespund subprogramelor de tip funct ie.
Daca n subprogram nu apare un astfel de enunt , atunci el se va apela prin:
<nume sp>(nume p1, nume p2, ...);
variant a care corespunde subprogramelor de tip procedur a.
Observat ie. Prelucr arile care nu sunt detaliate n cadrul algoritmului sunt
descrise n limbaj natural sau limbaj matematic. Comentariile suplimentare vor
cuprinse ntre /* si */. Daca pe o linie a descrierii algoritmului apare simbolul //
atunci tot ce urmeaza dup a acest simbol, pe aceeasi linie cu el, este interpretat ca
ind un comentariu (deci, nu reprezinta o prelucrare a programului).
3.5.7 Probleme rezolvate
1. Descompunere Fibonacci. S a se descompuna un num ar natural, de cel mult
18-19 cifre, n suma de cat mai put ini termeni Fibonacci.
Rezolvare: Programul urm ator calculeaz a si aseaza primii 92 de termeni
din sirul Fibonacci (mai mult nu este posibil f ar a numere mari!), si descompune
num arul x introdus de la tastatur a. Metoda static int maxFibo ( long nr )
returneaz a indicele celui mai mare element din sirul lui Fibonacci care este mai
mic sau egal cu parametrul nr.
import java.io.*;
class DescFibo
{
static int n=92;
static long[] f=new long[n+1];
public static void main (String[]args) throws IOException
{
long x,y;
int iy, k, nrt=0;
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("x = ");
x=Long.parseLong(br.readLine());
f[0]=0; f[1]=1; f[2]=1;
for(k=3;k<=n;k++) f[k]=f[k-1]+f[k-2];
for(k=0;k<=n;k++) System.out.println(k+" : "+f[k]);
System.out.println(" "+Long.MAX_VALUE+" = Long.MAX_VALUE");
38 CAPITOLUL 3. ALGORITMI
System.out.println("x = "+x);
while(x>0)
{
iy=maxFibo(x);
y=f[iy];
nrt++;
System.out.println(nrt+" : "+x+" f["+iy+"] = "+y);
x=x-y;
}
}
static int maxFibo(long nr)
{
int k;
for(k=1;k<=n;k++) if (f[k]>nr) break;
return k-1;
}
}
De exemplu, pentru x = 5678 pe ecran apare:
1 : 5678 f[19] = 418
2 : 1497 f[16] = 987
3 : 510 f[14] = 377
4 : 133 f[11] = 89
5 : 44 f[9] = 34
6 : 10 f[6] = 8
7 : 2 f[3] = 2
2. Fie S
n
= x
n
1
+x
n
2
unde x
1
si x
2
sunt r ad acinile ecuat iei cu coecient i ntregi
ax
2
+bx +c = 0 (vom considera a = 1!). S a se aseze primii 10 termeni ai sirului
S
n
si sa se precizeze n dreptul ec arui termen dac a este numar prim, iar dac a nu
este numar prim s a se aseze descompunerea n factori.
Rezolvare:
class e02
{
public static void main(String[] args)
{
int a, b, c, nnp=0, s, p, n=10, k;
long[] ss=new long[n+1];
a=1;b=1;c=2;
s=-b/a;
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 39
p=c/a;
ss[1]=s;
ss[2]=s*s-2*p;
for(k=3;k<=n;k++) ss[k]=s*ss[k-1]-p*ss[k-2];
for(k=1;k<=n;k++)
if(esteprim(Math.abs(ss[k])))
System.out.println(k+" : "+ss[k]+" PRIM "+(++nnp));
else
{
System.out.print(k+" : "+ss[k]+" = ");
descfact(Math.abs(ss[k]));
}
System.out.println("nnp = "+nnp);
}// main
static void descfact(long nr)
{
long d=2;
if((nr==0)||(nr==1)){System.out.println(); return;}
while(nr%d==0){System.out.print(d+""); nr=nr/d;}
d=3;
while((d*d<=nr)&&(nr!=1))
{
while(nr%d==0){System.out.print(d+" "); nr=nr/d;}
d=d+2;
}
if(nr!=1) System.out.println(nr);
else System.out.println();
}
static boolean esteprim(long nr)
{
if((nr==0)||(nr==1)) return false;
if((nr==2)||(nr==3)) return true;
if(nr%2==0) return false;
long d=3;
while((nr%d!=0)&&(d*d<=nr)) d=d+2;
if(nr%d==0) return false; else return true;
}
}// class
Pe ecran apar urm atoarele rezultate:
1 : -1 =
2 : -3 PRIM 1
40 CAPITOLUL 3. ALGORITMI
3 : 5 PRIM 2
4 : 1 =
5 : -11 PRIM 3
6 : 9 = 3 3
7 : 13 PRIM 4
8 : -31 PRIM 5
9 : 5 PRIM 6
10 : 57 = 3 19
nnp = 6
Press any key to continue...
3. Se consider a funct ia f(x) = P(x)e
x
unde P(x) este un polinom de grad n
cu coecient i ntregi. S a se aseze toate derivatele pan a la ordinul m ale funct iei f,
si, n dreptul coecient ilor polinoamelor care apar n aceste derivate, sa se precizeze
daca respectivul coecient este numar prim, iar dac a nu este num ar prim s a se
aseze descompunerea n factori. De asemenea, sa se aseze care este cel mai mare
num ar prim care apare, si care este ordinul derivatei n care apare acest cel mai
mare numar prim.
Rezolvare: Derivata funct iei f are forma Q(x)e
x
unde Q este un polinom de
acelasi grad cu polinomul P. Toat a rezolvarea problemei se reduce la determinarea
coecient ilor polinomului Q n funct ie de coecient ii polinomului P.
class e03
{
static long npmax=1,pmax=0;
public static void main(String[] args)
{
int n=7, m=10, alfa=1, k;
long[] p=new long[n+1];
p[7]=1; p[3]=1; p[0]=1;
afisv(p,0);
for(k=1;k<=m;k++)
{
System.out.print("derivata = "+k);
p=deriv(p,alfa);
afisv(p,k);
}
System.out.println(npmax+" "+pmax);
System.out.println("GATA!!!");
}
static long[] deriv(long[] a,int alfa)
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 41
{
int n=a.length-1, k;
long[] b=new long[n+1];
b[n]=a[n]*alfa;
for(k=0;k<=n-1;k++) b[k]=(k+1)*a[k+1]+a[k]*alfa;
return b;
}
static void afisv(long[] x,int ppp)
{
int n=x.length-1;
int i;
System.out.println();
for(i=n;i>=0;i--)
if(esteprim(Math.abs(x[i])))
{
System.out.println(i+" : "+x[i]+" PRIM ");
if(npmax<Math.abs(x[i]))
{
npmax=Math.abs(x[i]);
pmax=ppp;
}
}
else
{
System.out.print(i+" : "+x[i]+" = ");
descfact(Math.abs(x[i]));
}
System.out.println();
}
static void descfact(long nr)
{
long d=2;
if((nr==0)||(nr==1))
{
System.out.println();
return;
}
while(nr%d==0)
{
System.out.print(d+" ");
nr=nr/d;
}
42 CAPITOLUL 3. ALGORITMI
d=3;
while((d*d<=nr)&&(nr!=1))
{
while(nr%d==0)
{
System.out.print(d+" ");
nr=nr/d;
}
d=d+2;
}
if(nr!=1) System.out.println(nr);
else System.out.println();
}
static boolean esteprim(long nr)
{
if((nr==0)||(nr==1)) return false;
if((nr==2)||(nr==3)) return true;
if(nr%2==0) return false;
long d=3;
while((nr%d!=0)&&(d*d<=nr)) d=d+2;
if(nr%d==0) return false; else return true;
}
}// class
4. R ad acini rat ionale. S a se determine toate rad acinile rat ionale ale unei
ecuat ii cu coecient i ntregi.
Rezolvare: Se caut a r ad acini rat ionale formate din fract ii n care num ar atorul
este divizor al termenului liber iar numitorul este divizor al termenului dominant.
Programul care urmeaz a genereaza coecient ii ecuat iei, plec and de la fract ii date
(ca rad acini), si apoi determin a r ad acinile rat ionale
class RadaciniRationale // generez p_i/q_i
{
static int k=0;
public static void main(String[] args)
{
int[] p={1,1,2,3, 3, 1}, q={2,3,3,2,-2,-1};
int[] a=genPol(p,q);
int n=a.length-1,alfa,beta;
int moda0=Math.abs(a[0]),modan=Math.abs(a[n]);
for(alfa=1;alfa<=moda0;alfa++)
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 43
{
if(moda0%alfa!=0) continue;
for(beta=1;beta<=modan;beta++)
{
if(modan%beta!=0) continue;
if(cmmdc(alfa,beta)!=1) continue;
if (f(a,alfa,beta)==0)
System.out.println("x["+(++k)+"] = "+alfa+"/"+beta+" ");
if (f(a,-alfa,beta)==0)
System.out.println("x["+(++k)+"] = -"+alfa+"/"+beta+" ");
}// for beta
}// for alfa
}// main
static int[] genPol(int[] a, int[] b) // X-a_i/b_i==>b_i X - a_i
{
int n=a.length;
int[] p={-a[0],b[0]},//p=b[0] X -a[0]
q={13,13}; // q initializat "aiurea" - pentru dimensiune !
afisv(p);
for(int k=1;k<n;k++)
{
q[0]=-a[k];
q[1]=b[k];
p=pxq(p,q);
afisv(p);
}
return p;
}// genPol()
static int[] pxq(int[] p,int[] q)
{
int gradp=p.length-1, gradq=q.length-1;
int gradpq=gradp+gradq;
int[] pq=new int[gradpq+1];
int i,j,k;
for(k=0;k<=gradpq;k++) pq[k]=0;
for(i=0;i<=gradp;i++)
for(j=0;j<=gradq;j++) pq[i+j]+=p[i]*q[j];
return pq;
}
static int f(int[]a,int alfa, int beta)
{
44 CAPITOLUL 3. ALGORITMI
int n=a.length-1,k,s=0;
for(k=0;k<=n;k++) s+=a[k]*putere(alfa,k)*putere(beta,n-k);
return s;
}
static int putere(int a, int n)
{
int p=1;
for(int k=1;k<=n;k++) p*=a;
return p;
}
static int cmmdc(int a, int b)
{
int d,i,c,r;
if (a>b) {d=a; i=b;} else {d=b; i=a;}
r=123; // ca sa inceapa while !!!
while (r > 0){c=d/i; r=d%i; d=i; i=r;}
return d;
}
static void afisv(int[] a)
{
for(int i=a.length-1;i>=0;i--) System.out.print(a[i]+" ");
System.out.println();
}// afisv()
}// class
5. S a se aseze frecvent a cifrelor care apar n
f(n) =
n

k=0
1
2
k
C
n
n+k
net in and cont de faptul c a f(n) are o expresie mult mai simpl a, si anume 2
n
. Suma
trebuie calculata simul and operat iile de adunare, nmult ire si mp art ire la 2, cu
numere mari.
Rezolvare: Funct ia se pune sub forma:
f(n) =
1
2
n
n

k=0
2
nk
C
n
n+k
Se calculeaza suma, si apoi se fac n mp art iri succesive la 2.
class e05
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 45
{
public static void main (String[]args)
{
int n, k;
int[] s;
int[] p;
for(n=10;n<=12;n++)
{
s=nrv(0);
for(k=0;k<=n;k++)
{
p=inm(comb(n+k,n),putere(2,n-k));
s=suma(s,p);
}
afisv(s);
for(k=1;k<=n;k++) s=impartLa2(s);
System.out.print(n+" : ");
afisv(s);
fcifre(s);
}
System.out.println("GATA");
}//main()
static int[] impartLa2(int[] a)
{
int na,nb,k,t=0;
na=a.length-1;
if(a[na]==1) nb=na-1; else nb=na;
int[] b=new int[nb+1];
if(na==nb)
for(k=na;k>=0;k--) {a[k]+=10*t; b[k]=a[k]/2; t=a[k]%2;}
else
{
t=a[na];
for(k=na-1;k>=0;k--){a[k]+=10*t; b[k]=a[k]/2; t=a[k]%2;}
}
return b;
}
static void fcifre(int[] x)
{
int i;
int[] f=new int[10];
for(i=0;i<x.length;i++) f[x[i]]++;
46 CAPITOLUL 3. ALGORITMI
System.out.println();
for(i=0;i<=9;i++) System.out.println(i+" : "+f[i]);
System.out.println();
}
static int[] suma(int[] x, int[] y)
{
int i, j, t, ncx=x.length, ncy=y.length, ncz;
if(ncx>ncy) ncz=ncx+1; else ncz=ncy+1;
int[] xx=new int[ncz];
int[] yy=new int[ncz];
int[] z=new int[ncz];
for(i=0;i<ncx;i++) xx[i]=x[i];
for(j=0;j<ncy;j++) yy[j]=y[j];
t=0;
for(i=0;i<ncz;i++){z[i]=xx[i]+yy[i]+t; t=z[i]/10; z[i]=z[i]%10;}
if(z[ncz-1]!= 0) return z;
else
{
int[]zz=new int[ncz-1];
for(i=0;i<=ncz-2;i++) zz[i]=z[i];
return zz;
}
}
static int[] inm(int[]x,int[]y)
{
int t, n=x.length, m=y.length, i, j;
int[][]a=new int[m][n+m];
int[]z=new int[m+n];
for(j=0;j<m;j++)
{
t=0;
for(i=0;i<n;i++)
{
a[j][i+j]=y[j]*x[i]+t;
t=a[j][i+j]/10;
a[j][i+j]=a[j][i+j]%10;
}
a[j][i+j]=t;
}
t=0;
for(j=0;j<m+n;j++)
{
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 47
z[j]=0;
for(i=0;i<m;i++) z[j]=z[j]+a[i][j];
z[j]=z[j]+t;
t=z[j]/10;
z[j]=z[j]%10;
}
if(z[m+n-1]!= 0) return z;
else
{
int[]zz=new int[m+n-1];
for(i=0;i<=m+n-2;i++)
zz[i]=z[i];
return zz;
}
}
static void afisv(int[]x)
{
int i;
for(i=x.length-1;i>=0;i--) System.out.print(x[i]);
System.out.print(" *** "+x.length);
System.out.println();
}
static int[] nrv(int nr)
{
int nrrez=nr, nc=0;
while(nr!=0) {nc++; nr=nr/10;}
int[]x=new int [nc];
nr=nrrez;
nc=0;
while(nr!=0){x[nc]=nr%10; nc++; nr=nr/10;}
return x;
}
static int[] putere (int a, int n)
{
int[] rez;
int k;
rez=nrv(1);
for(k=1;k<=n;k++) rez=inm(rez,nrv(a));
return rez;
}
48 CAPITOLUL 3. ALGORITMI
static int[] comb (int n, int k)
{
int[] rez;
int i, j, d;
int[]x=new int[k+1];
int[]y=new int[k+1];
for(i=1;i<=k;i++) x[i]=n-k+i;
for(j=1;j<=k;j++) y[j]=j;
for(j=2;j<=k;j++)
{
for(i=1;i<=k;i++)
{
d=cmmdc(y[j],x[i]);
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}
}
rez=nrv(1);
for(i=1;i<=k;i++) rez=inm(rez,nrv(x[i]));
return rez;
}
static int cmmdc (int a,int b)
{
int d,i,c,r;
if (a>b) {d=a;i=b;} else{d=b;i=a;}
while (i!=0){c=d/i; r=d%i; d=i; i=r;}
return d;
}
}// class
6. S a se aseze S(n, 1), S(n, 2), ..., S(n, m) (inclusiv suma cifrelor si num arul
cifrelor pentru ecare num a) stiind c a
S(n + 1, m) = S(n, m1) +mS(n, m)
si
S(n, 1) = S(n, n) = 1, n m.
Se vor implementa operat iile cu numere mari.
Rezolvare: Matricea de calcul este subdiagonal a. Se completeaz a cu 1 prima
coloan a si diagonala principal a, iar apoi se determin a celelalte elemente ale matricei
folosind relat ia dat a (aranjat a put in altfel!). Matricea de calcul va avea de fapt trei
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 49
dimensiuni (numerele devin foarte mari, asa ca elementul S
i,j
trebuie sa cont in a
vectorul cifrelor valorii sale).
class e06
{
public static void main(String[] args)
{
int n=50, m=40, i, j;
int[][][] s=new int[n+1][m+1][1];
for(i=1;i<=n;i++)
{
if(i<=m) s[i][i]=nr2v(1);
s[i][1]=nr2v(1);
for(j=2;j<=min(i,m);j++)
s[i][j]=suma(s[i-1][j-1],inm(nr2v(j),s[i-1][j]));
if(i<=m) s[i][i]=nr2v(1);
}
for(i=1;i<=m;i++)
{
System.out.print("\n"+i+" : "+s[n][i].length+" ");
afissumac(s[n][i]);
afisv(s[n][i]);
}
}
static int[] suma(int[] x,int[] y){...}
static int[] nr2v(int nr){...}
static int[] inm(int[]x, int[]y){...}
static void afisv(int[]x){...}
static void afissumac(int[]x)
{
int i,s=0;
for(i=x.length-1;i>=0;i--) s+=x[i];
System.out.print(s+" ");
}
static int min(int a, int b) { return (a<b)?a:b; }
}// class
Pe ecran apar urm atoarele valori (numerele devin foarte mari!):
1 : 1 1 1
2 : 15 64 562949953421311
50 CAPITOLUL 3. ALGORITMI
3 : 24 102 119649664052358811373730
4 : 29 138 52818655359845224561907882505
5 : 33 150 740095864368253016271188139587625
6 : 37 170 1121872763094011987454778237712816687
7 : 39 172 355716059292752464797065038013137686280
8 : 41 163 35041731132610098771332691525663865902850
9 : 43 189 1385022509795956184601907089700730509680195
10 : 44 205 26154716515862881292012777396577993781727011
11 : 45 177 267235754090021618651175277046931371050194780
12 : 46 205 1619330944936279779154381745816428036441286410
13 : 46 232 6238901276275784811492861794826737563889288230
14 : 47 205 16132809270066494376125322988035691981158490930
15 : 47 162 29226457001965139089793853213126510270024300000
16 : 47 216 38400825365495544823847807988536071815780050940
17 : 47 198 37645241791600906804871080818625037726247519045
18 : 47 225 28189332813493454141899976735501798322277536165
19 : 47 165 16443993651925074352512402220900950019217097000
20 : 46 237 7597921606860986900454469394099277146998755300
21 : 46 198 2820255028563506149657952954637813048172723380
22 : 45 189 851221883077356634241622276646259170751626380
23 : 45 198 211092494149947371195608696099645107168146400
24 : 44 192 43397743800247894833556570977432285162431400
25 : 43 168 7453802153273200083379626234837625465912500
26 : 43 186 1076689601597672801650712654209772574328212
27 : 42 189 131546627365808405813814858256465369456080
28 : 41 155 13660054661277961013613328658015172843800
29 : 40 165 1210546686654900169010588840430963387720
30 : 38 185 91860943867630642501164254978867961752
31 : 37 155 5985123385551625085090007793831362560
32 : 36 164 335506079163614744581488648870187520
33 : 35 153 16204251384884158932677856617905110
34 : 33 144 674833416425711522482381379544960
35 : 32 126 24235536318546124501501767693750
36 : 30 135 750135688292101886770568010795
37 : 29 141 19983209983507514547524896035
38 : 27 132 457149347489175573737344245
39 : 25 114 8951779743496412314947000
40 : 24 93 149377949042637543000150
7. S a se aseze B
1
, B
2
, ..., B
n
stiind c a
B
n+1
=
n

k=0
C
k
n
B
k
, B
0
= 1.
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 51
Se vor implementa operat iile cu numere mari.
Rezolvare: Vectorul de calcul va avea de fapt dou a dimensiuni (numerele devin
foarte mari, asa ca elementul B
i
trebuie sa cont in a vectorul cifrelor valorii sale).
class e07
{
public static void main(String[] args)
{
int n=71; // n=25 ultimul care incape pe long
int k,i;
int[][] b=new int[n+1][1];
int[] prod={1};
b[0]=nr2v(1);
for(i=1;i<=n;i++)
{
b[i]=nr2v(0);;
for(k=0;k<=i-1;k++)
{
prod=inm(comb(i-1,k),b[k]);
b[i]=suma(b[i],prod);
}
System.out.print(i+" : ");
afisv(b[i]);
}
System.out.println(" "+Long.MAX_VALUE);
System.out.println("... Gata ...");
}
static int[] suma(int[] x,int[] y){...}
static int[] nr2v(int nr){...}
static int[] inm(int[]x, int[]y){...}
static void afisv(int[]x){...}
static int[] comb(int n,int k)
{
int i,j,d;
int[] rez;
int[] x=new int[k+1];
int[] y=new int[k+1];
for(i=1;i<=k;i++) x[i]=n-k+i;
for(j=1;j<=k;j++) y[j]=j;
for(j=2;j<=k;j++)
for(i=1;i<=k;i++)
52 CAPITOLUL 3. ALGORITMI
{
d=cmmdc(y[j],x[i]);
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}
rez=nr2v(1);
for(i=1;i<=k;i++) rez=inm(rez,nr2v(x[i]));
return rez;
}
static int cmmdc(int a,int b) {...}
}
3.5.8 Probleme propuse
1. Fie S
n
= x
n
1
+x
n
2
+x
n
3
unde x
1
, x
2
si x
3
sunt r ad acinile ecuat iei cu coecient i
ntregi ax
3
+bx
2
+cx+d = 0 (vom considera a = 1!). S a se aseze primii 10 termeni
ai sirului S
n
si sa se precizeze n dreptul ec arui termen dac a este numar prim, iar
daca nu este num ar prim s a se aseze descompunerea n factori.
2. S a se aseze frecvent a cifrelor care apar n
f(n) =
n1

k=0
C
k
n1
n
n1k
(k + 1)!
net in and cont de faptul c a f(n) are o expresie mult mai simpl a, si anume n
n
. Suma
trebuie calculata simul and operat iile cu numere mari.
3. S a se aseze frecvent a cifrelor care apar n
f(n) = n
n1
+
n1

k=1
C
k
n
k
k1
(n k)
nk
net in and cont de faptul c a f(n) are o expresie mult mai simpl a, si anume n
n
. Suma
trebuie calculata simul and operat iile cu numere mari.
4. S a se calculeze
f(n) = n
_
1
1
p
1
__
1
1
p
2
_
...
_
1
1
p
m
_
3.5. INSTRUCT IUNI CORESPONDENTE LIMBAJULUI ALGORITMIC 53
unde n = p
i1
1
p
i2
2
...p
im
m
reprezint a descompunerea n factori primi a lui n.
5. S a se calculeze
(n) = card k N/1 k n, cmmdc(k, n) = 1 .
6. S a se calculeze
f(n) =

d|n
(n)
unde este funct ia de la exercit iul anterior, net in and cont de faptul c a f(n) are o
expresie mult mai simpla, si anume n.
7. S a se calculeze
f(n) = n!
_
1
1
1!
+
1
2!
... +
(1)
n
n!
_
.
8. S a se calculeze
f(m, n,
1
,
2
, ...,
n
) =
m

k=1
(1)
mk
C
k
m
_
C
1
k
_
1
_
C
2
k+1
_
2
...
_
C
n
k+n1
_
n
.
9. S a se calculeze
g(m, n,
1
,
2
, ...,
n
) =
_
C
1
m
_
1
_
C
2
m+1
_
2
...
_
C
n
m+n1
_
n
implementand operat iile cu numere mari.
10. S a se calculeze
f(n) =
1
2
n
_
(2n)! C
1
n
2(2n 1)! +C
2
n
2
2
(2n 2)! ... + (1)
n
2
n
n!
_
.
11. S a se calculeze
C
n
=
1
n + 1
C
n
2n
implementand operat iile cu numere mari.
12. S a se aseze P(100, 50) (inclusiv suma cifrelor si num arul cifrelor) stiind
ca
P(n +k, k) = P(n, 1) +P(n, 2) +... +P(n, k)
54 CAPITOLUL 3. ALGORITMI
si
P(n, 1) = P(n, n) = 1, n k 1.
Se vor implementa operat iile cu numere mari.
13. S a se determine cel mai mic numar natural r, astfel nc at p
r
= e, unde p
este o permutare data si e este permutarea identic a.
14. S a se aseze C
100
stiind c a
C
n
=
n

k=1
C
k1
C
nk
, C
0
= 1.
Se vor implementa operat iile cu numere mari.
15. S a se aseze E
100
stiind c a
En = E
2
E
n1
+E
3
E
n2
+... + E
n1
E
2
, E
1
= E
2
= 1.
Se vor implementa operat iile cu numere mari.
16. S a se calculeze
S(n, m) =
1
m!
m1

k=0
(1)
k
C
k
m
(mk)
n
17. S a se aseze C
100
stiind c a
C
n
=
n

k=1
C
k
n
F
k
.
unde F
k
este termen Fibonacci. Se vor implementa operat iile cu numere mari.
18. S a se aseze C
100
stiind c a
C
n
=
n

k=1
C
k
n
2
k
F
k
.
unde F
k
este termen Fibonacci. Se vor implementa operat iile cu numere mari.
19. S a se determine puterea a zecea a unui polinom dat.
Capitolul 4
Analiza complexit at ii
algoritmilor
4.1 Scopul analizei complexitat ii

In general exist a mai multi algoritmi care rezolv a aceeasi problem a. Dorim
sa exprim am ecient a algoritmilor sub forma unui criteriu care sa ne permit a sa
alegem din mai mult i algoritmi pe cel optim. Exist a mai multe moduri n care
putem exprima ecient a: prin timpul necesar pentru execut ia algoritmului sau
prin alte resurse necesare (de exemplu memoria).

In ambele cazuri ns a, avem o
dependent a de dimensiunea cazului studiat.
Se pune problema de alegere a unei unit at i de masur a pentru a exprima
ecient a teoretica a unui algoritm. O important a deosebita n rezolvarea acestei
probleme o are principiul invariant ei. Acesta ne arat a ca nu este necesar s a folosim
o astfel de unitate.
Principiul invariant ei: dou a implement ari diferite ale aceluiasi algoritm
nu difer a n ecient a cu mai mult de o constant a multiplicativa.
Implementarea unui algoritm presupune elementele legate de calculatorul
folosit, de limbajul de programare si ndemanarea programatorului (cu condit ia ca
acesta sa nu modice algoritmul). Datorit a principiului invariant ei vom exprima
ecient a unui algoritmn limitele unei constante multiplicative.
Un algoritm este compus din mai multe instruct iuni, care la r andul lor sunt
compuse din mai multe operat ii elementare. Datorit a principiului invariant ei nu
ne intereseaza timpul de execut ie a unei operat ii elementare, ci numai numarul lor,
dar ne intereseaza care si ce sunt operat iile elementare.
Denit ia 1 O operat ie elementar a este o operat ie al c arui timp de execut ie poate
m arginit superior de o constant a care depinde numai de particularitatea imple-
mentarii (calculator, limbaj de programare etc).
55
56 CAPITOLUL 4. ANALIZA COMPLEXIT

ATII ALGORITMILOR
Deoarece ne intereseaza timpul de executie n limita unei constante multi-
plicative, vom considera doar num arul operat iilor elementare executate ntr-un
algoritm, nu si timpul exact de execut ie al operat iilor respective.
Este foarte important ce anume denim ca operat ie elementar a. Este adunarea
o operat ie elementara? Teoretic nu este, pentru ca depinde de lungimea celor doi
operanzi. Practic, pentru operanzi de lungime rezonabil a putem s a consider am ca
adunarea este o operat ie elementar a. Vom considera n continuare c a adun arile,
scaderile, nmult irile, mp art irile, operat iile modulo (restul mp aart irii ntregi),
operat iile booleene, comparat iile si atribuirile sunt operat ii elementare.
Uneori ecient a difer a daca t inem cont numai de unele operat ii elementare si
le ignor am pe celelalte (de exemplu la sortare: comparat ia si interschimbarea). De
aceea n analiza unor algoritmi vom considera o anumit a operat ie elementara, care
este caracteristica algoritmului, ca operat ie barometru, neglij andu-le pe celelalte.
De multe ori, timpul de executie al unui algoritm poate varia pentru cazuri
de marime identic a. De exemplu la sortare, dac a introducem un sir de n numere
gata sortat, timpul necesar va cel mai mic dintre timpii necesari pentru sortarea
oricarui alt sir format din n numere. Spunem c a avem de-a face cu cazul cel mai
favorabil. Daca sirul este introdus n ordine invers a, avem cazul cel mai defavorabil
si timpul va cel mai mare dintre timpii de sortare a sirului de n numere.
Exist a algoritmi n care timpul de execut ie nu depinde de cazul considerat.
Daca dimensiunea problemei este mare, mbun at at irea ordinului algoritmului
este esent iala, n timp ce pentru timpi mici este sufcient a performant a hardware.
Elaborarea unor algoritmi ecient i presupune cunostint e din diverse domenii
(informatic a, matematica si cunostiint e din domeniul c aruia i apart ine problema
practic a a carui model este studiat, atunci cand este cazul).
Exemplul 1 Elaborat i un algoritm care returneaz a cel mai mare divizor comun
(cmmdc) a doi termeni de rang oarecare din sirul lui Fibonacci.
Sirul lui Fibonacci, f
n
= f
n1
+ f
n2
, este un exemplu de recursivitate n
cascada si calcularea efectiv a a celor doi termeni f
m
f
n
, urmat a de calculul celui
mai mare divizor al lor, este total neindicata. Un algoritm mai bun poate obt inut
daca t inem seama de rezultatul descoperit de Lucas n 1876:
cmmdc(f
m
, f
n
) = f
cmmdc(m,n)
Deci putem rezolva problema calculand un singur termen al sirului lui Fibonacci.
Exist a mai mult i algoritmi de rezolvare a unei probleme date. Prin urmare,
se impune o analiz a a acestora, n scopul determinarii ecient ei algoritmilor de
rezolvare a problemei si pe cat posibil a optimalit at ii lor. Criteriile n funct ie
de care vom stabili ecient a unui algoritm sunt complexitatea spat iu (memorie
utilizat a) si complexitatea timp (numarul de operat iilor elementare).
4.1. SCOPUL ANALIZEI COMPLEXIT

ATII 57
4.1.1 Complexitatea spat iu
Prin complexitate spat iu nt elegem dimensiunea spat iului de memorie utilizat
de program.
Un program necesita un spat iu de memorie constant, independent de datele
de intrare, pentru memorarea codului, a constantelor, a variabilelor si a structurilor
de date de dimensiune constanta alocate static si un spat iu de memorie variabil,
a carui dimensiune depinde de datele de intrare, const and din spat iul necesar
pentru structurile de date alocate dinamic, a c aror dimensiune depinde de instant a
problemei de rezolvat si din spat iul de memorie necesar apelurilor de proceduri si
funct ii.
Progresele tehnologice fac ca important a criteriului spat iu de memorie utilizat
sa scada, prioritar devenind criteriul timp.
4.1.2 Complexitatea timp
Prin complexitate timp nt elegem timpul necesar execut iei programului.

Inainte de a evalua timpul necesar execut iei programului ar trebui s a avem


informat ii detaliate despre sistemul de calcul folosit.
Pentru a analiza teoretic algoritmul, vom presupune c a se lucreaza pe un
calculator clasic, n sensul ca o singura instruct iune este executata la un moment
dat. Astfel, timpul necesar execut iei programului depinde numai de num arul de
operat ii elementare efectuate de algoritm.
Primul pas n analiza complexit at ii timp a unui algoritm este determinarea
operat iilor elementare efectuate de algoritm si a costurilor acestora.
Consider am operat ie elementar a orice operat ie al carei timp de execut ie este
independent de datele de intrare ale problemei.
Timpul necesar execut iei unei operat ii elementare poate diferit de la o
operat ie la alta, dar este xat, deci putem spune c a operat iile elementare au timpul
maginit superior de o constant a.
F ar a a restrange generalitatea, vom presupune c a toate operat iile elementare
au acelasi timp de execut ie, ind astfel necesara doar evaluarea num arului de
operat ii elementare, nu si a timpului total de execut ie a acestora.
Analiza teoretic a ignor a factorii care depind de calculator sau de limbajul
de programare ales si se axeaza doar pe determinarea ordinului de m arime a
num arului de operat i elementare.
Pentru a analiza timpul de execut ie se foloseste deseori modelul Random
Access Machine (RAM), care presupune: memoria consta ntr-un sir innit de
celule, ecare celul a poate stoca cel mult o data, ecare celul a de memorie poate
accesata ntr-o unitate de timp, instruct iunile sunt executate secvent ial si toate
instruct iunile de baz a se executa ntr-o unitate de timp.
Scopul analizei teoretice a algoritmilor este de fapt determinarea unor funct ii
care sa limiteze superior, respectiv inferior comportarea n timp a algoritmului.
Funct iile depind de caracteristicile relevante ale datelor de intrare.
58 CAPITOLUL 4. ANALIZA COMPLEXIT

ATII ALGORITMILOR
4.2 Notat ia asimptotica
4.2.1 Denire si proprietat i
Denit ia 2 Numim ordinul lui f, mult imea de funct ii
O(f) = t : N R
+
[c > 0, n
0
N a.. t(n) cf(n), n > n
0
(4.2.1)
Rezulta ca O(f) este mult imea tuturor funct iilor m arginite superior de un
multiplu real pozitiv al lui f, pentru valori sucient de mari ale argumentului.
Daca t(n) O(f) vom spune ca t este de ordinul lui f sau n ordinul lui f.
Fie un algoritm dat si o funct ie t : N R
+
, astfel nc at o anumit a imple-
mentare a algoritmului s a necesite cel mult t(n) unit at i de timp pentru a rezolva
un caz de marime n.
Principiul invariant ei ne asigura ca orice implementare a algoritmului necesit a
un timp n ordinul lui t. Mai mult, acest algoritm necesit a un timp n ordinul lui
f pentru orice functie f : N R
+
pentru care t O(f).

In particular t O(t).
Vom cauta s a g asim cea mai simpla funct ie astfel nc at t O(f).
Pentru calculul ordinului unei funct ii sunt utile urm atoarele proprietat i:
Proprietatea 1 O(f) = O(g) f O(g) si g O(f)
Proprietatea 2 O(f) O(g) f O(g) si g / O(f)
Proprietatea 3 O(f +g) = O(max(f, g))
Pentru calculul mult imilor O(f) si O(g) este util a proprietatea urmatoare:
Proprietatea 4 Fie f, g : N R
+
. Atunci
lim
n
f(n)
g(n)
R
+
O(f) = O(g)
lim
n
f(n)
g(n)
= 0 O(f) O(g).
Reciproca nu este n general valabil a.
Fie de exemplu, t(n) = n
2
+ 3n + 2, atunci
lim
n
n
2
+ 3n + 2
n
2
= 1 = O(n
2
+ 3n + 2) = O(n
2
)
lim
n
n
2
+ 3n + 2
n
3
= 0 = O(n
2
+ 3n + 2) O(n
3
)
4.2. NOTAT IA ASIMPTOTIC

A 59
lim
n
ln(n)

n
= lim
n
1
n
1
2

n
= lim
n
2

n
= 0 = O(ln(n)) O(

n)
dar O(

n) , O(ln(n)
Daca p este un polinom de gradul mn variabila n, atunci O(p) = O(n
m
).
Notat ia asimptotic a deneste o relat ie de ordine part ial a ntre funct ii.
Pentru f, g : N R

not am f g daca O(f) O(g).


Aceasta relat ie are propriet at ile corespunzatoare unei relat ii de ordine, adic a:
a) reexivitate: f f
b) antisimetrie: daca f g si g f atunci f = g
c) tranzitivitate: f g si g h, implic a f h.
Dar nu este o relat ie de ordine! Exist a si funct ii astfel nc at f , g (f / O(g))
si g , f (g / O(f)). De exemplu f(n) = n, g(n) = n
1+sin(n)
.
Putem deni si o relat ie de echivalent a: f g, dac a O(f) = O(g).

In
mult imea O(f) putem nlocui orice funct ie cu o funct ie echivalenta cu ea. De
exemplu: ln(n) log(n) log
2
(n).
Not and cu O(1) mult imea funct iilor m arginite superior de o constant a si
consider and m N , m 2, obt inem ierarhia:
O(1) O(log(n)) O(

n) O(n) O(n log(n)) O(n


m
) O(2
n
) O(n!)
si evident O(n
2
) O(n
3
) ... O(n
m
) pentru m 4.
Aceasta ierarhie corespunde ierarhiei algoritmilor dup a criteriul performant ei.
Pentru o problem a dat a, dorim sa realiz am un algoritm cu un ordin situat c at mai
n stanga n aceasta ierarhie.
Notatia O(f) este pentru a delimita superior timpul necesar unui algoritm.
Not am T
A
(n) timpul necesar execut iei algoritmului A.
Fie f : N R

+
o funct ie arbitrar a. Spunem c a algoritmul este de ordinul
lui f(n) (si not am T
A
(n) O(f(n))), dac a si numai dac a exista c > 0 si n
0
N,
astfel nc at T
A
(n) c f(n), n n
0
.
De exemplu:
a) Daca T
A
(n) = 3n+2, atunci T
A
(n) O(n), pentru c a 3n+2 4n, n 2.
Mai general, dac a T
A
(n) = a n + b, a > 0, atunci T
A
(n) O(n) pentru c a
exista c = a + 1 > 0 si n
0
= b N, astfel nc at a n +b (a + 1) n, n b.
b) Dac a T
A
(n) = 10n
2
+ 4n + 2, atunci T
A
(n) O(n
2
), pentru c a 10n
2
+
4n + 2 11n
2
, n 5.
Mai general, dac a TA(n) = an
2
+ bn + c, a > 0, atunci TA(n) O(n
2
),
pentru c a an
2
+bn +c (a + 1)n
2
, n max(b, c) + 1.
c) Daca T
A
(n) = 6 2
n
+ n
2
, atunci TA(n) O(2
n
), pentru c a T
A
(n)
7 2
n
,n 4.
Daca T
A
(n) = a
k
n
k
+ a
k1
n
k1
+ ... + a
1
n + a
0
, atunci TA(n) O(n
k
).
Aceasta rezulta din: T
A
(n) = [T
A
(n)[ = [a
k
n
k
+ a
k1
n
k1
+ ... + a
1
n + a
0
[
[a
k
[n
k
+[a
k1
[n
k1
+... +[a
1
[n+[a
0
[ ([a
k
[ +[a
k1
[ +... +[a
1
[ +[a
0
[)n
k
, n 1
si alegand c = [a
k
[ +[a
k1
[ +... +[a
1
[ +[a
0
[ si n = 1 rezult a T
A
(n) O(n
k
).
60 CAPITOLUL 4. ANALIZA COMPLEXIT

ATII ALGORITMILOR
4.2.2 Clase de complexitate
Notat ia O ofer a o limit a superioar a a timpului de execut ie a unui algoritm.
Un algoritm cu T
A
(n) O(1) necesita un timp de execut ie constant. Un
algoritm cu T
A
(n) O(n) se numeste liniar. Daca T
A
(n) O(n
2
) algoritmul se
numeste patratic, iar dac a T
A
(n) O(n
3
), cubic. Un algoritm cu T
A
(n) O(n
k
)
se numeste polinomial, iar dac a T
A
(n) O(2
n
) algoritmul se numeste exponent ial.
Tabelul urm ator ilustreaz a comportarea a cinci din cele mai importante
funct ii de complexitate.
O(log(n)) O(n) O(n.log(n)) O(n
2
) O(n
3
) O(2
n
)
(logaritmic) (liniar) (log-liniar) (p atratic) cubic (exponent ial)
0 1 0 1 1 2
1 2 2 4 8 4
2 4 8 16 64 16
3 8 24 64 512 256
4 16 64 256 4096 65536
5 32 160 1024 32768 4294967296
Tabelul 4.1: Funct ii de complexitate
Daca T
A
(n) O(2
n
), pentru n = 40, pe un calculator care face 10
9
de operat ii
pe secunda, sunt necesare aproximativ 18 minute. Pentru n = 50, acelasi program
va rula 13 zile pe acest calculator, pentru n = 60, vor necesari peste 310 ani, iar
pentru n = 100 aproximativ 4.10
13
ani.
Utilitatea algoritmilor polinomiali de grad mare este de asemenea limitat a.
De exemplu, pentru O(n
10
), pe un calculator care executa 10
9
operat ii pe secunda
sunt necesare 10 secunde pentru n = 10, aproximativ 3 ani pentru n = 100 si circa
3.10
13
ani pentru n = 1000.
Uneori este util s a determinam si o limit a inferioar a pentru timpul de execut ie
a unui algoritm. Notat ia matematica este .
Denit ie: Spunem c a T
A
(n) (f(n)) dac a si numai dac a c > 0 si n
0
N
astfel nc at T
A
(n) c f(n), n n
0
.
De exemplu:
a) dac a T
A
(n) = 3n+2, atunci T
A
(n) (n), pentru c a 3n+2 3n, n 1;
b) dac a T
A
(n) = 10n
2
+4n+2, atunci T
A
(n) (n), pentru c a 10n
2
+4n+2
n2, n 1;
c) daca T
A
(n) = 6 2
n
+n
2
, atunci T
A
(n) (2
n
), pentru c a 6 2
n
+n
2
2
n
,
n 1.
Exist a funct ii f care constituie at at o limit a superioar a cat si o limit a infe-
rioar a a timpului de execut ie a algoritmului. De exemplu, daca T
A
(n) = a
k
n
k
+
a
k1
n
k1
+... + a
1
n +a
0
, a
k
> 0 atunci T
A
(n) (n
k
).
Denit ie : Spunem c a T
A
(n) (f(n)) dac a si numai dac a c
1
, c
2
> 0 si
n
0
N astfel nc at c
1
f(n) T
A
(n) c
2
f(n), n n
0
.
4.2. NOTAT IA ASIMPTOTIC

A 61

In acest caz f(n) constituie at at o limit a inferioar a cat si o limit a superioar a


pentru timpul de execut ie a algoritmului. Din acest motiv se poate numi ordin
exact. Se poate ar ata usor ca (f(n)) = O(f(n)) (f(n)). De asemenea, daca
T
A
(n) = a
k
n
k
+a
k1
n
k1
+... +a
1
n + a
0
, a
k
> 0 atunci T
A
(n) (n
k
).
4.2.3 Cazul mediu si cazul cel mai defavorabil
Am ar atat ca timpul de execut ie al unui algoritm este direct proport ional cu
num arul de operat ii elementare si am stabilit o notat ie asimptotic a pentru timpul
de execut ie. Totusi, numarul de operat ii elementare efectuate de algoritm poate
varia considerabil pentru diferite seturi de date de intrare.
Determinarea complexit at ii timp a algoritmului ca o funct ie de caracteristi-
cile datelor de intrare este o sarcina usoara doar pentru algoritmi relativ simpli,
dar n general problema este dicil a si din aceast a cauza analizam complexitatea
algoritmilor n medie sau n cazul cel mai defavorabil.
Complexitatea n cazul cel mai defavorabil este numarul maxim de operat ii
elementare efectuate de algoritm.
Dar chiar dac a este cunoscut cazul cel mai defavorabil, datele utilizate efectiv
n practic a pot conduce la timpi de execut ie mult mai mici. Numerosi algoritmi
foarte utili au o comportare convenabil a n practic a, dar foarte proast a n cazul
cel mai defavorabil.
Cel mai cunoscut exemplu este algoritmul de sortare rapida (quicksort) care
are complexitatea n cazul cel mai defavorabil de O(n
2
), dar pentru datele nt alnite
n practic a funct ioneaza n O(n log n).
Determinarea complexit at ii n medie necesita cunoasterea repartit iei proba-
bilistice a datelor de intrare si din acest motiv analiza complexit at ii n medie este
mai dicil de realizat. Pentru cazuri simple, de exemplu un algoritm de sortare care
act ioneaza asupra unui tablou cu n componente ntregi aleatoare sau un algoritm
geometric pe o mult ime de N puncte n plan de coordonate aleatoare cuprinse n
intervalul [0, 1], putem caracteriza exact datele de intrare.
Daca not am:
D - spat iul datelor de intrare
p(d) - probabilitatea aparit iei datei d D la intrarea algoritmului
T
A
(d) - num arul de operat ii elementare efectuate de algoritm pentru d D
atunci complexitatea medie este

dD
p(d) T
A
(d).
62 CAPITOLUL 4. ANALIZA COMPLEXIT

ATII ALGORITMILOR
4.2.4 Analiza asimptotica a structurilor fundamentale
Consider am problema determin arii ordinului de complexitate n cazul cel mai
defavorabil pentru structurile algoritmice: secvent iala, alternativ a si repetitiv a.
Presupunem c a structura secvent ial a este constituit a din prelucr arile A
1
, A
2
,
..., A
k
si ecare dintre acestea are ordinul de complexitate O(g
i
(n)), 1 i n.
Atunci structura va avea ordinul de complexitate O(maxg
1
(n), ..., g
k
(n)).
Daca condit ia unei structuri alternative are cost constant iar prelucr arile
celor doua variante au ordinele de complexitate O(g
1
(n)) respectiv O(g
2
(n)) atunci
costul structurii alternative va O(maxg
1
(n), g
2
(n)).

In cazul unei structuri repetitive pentru a determina ordinul de complexitate


n cazul cel mai defavorabil se consider a num arul maxim de iterat ii. Daca acesta
este n iar n corpul ciclului prelucr arile sunt de cost constant atunci se obt ine
ordinul O(n).
4.3 Exemple
4.3.1 Calcularea maximului
Fiind date n elemente a
1
, a
2
, ..., a
n
, sa se calculeze maxa
1
, a
2
, ..., a
n
.
max = a[1];
for i = 2 to n do
if a[i] > max
then max = a[i];
Vom estima timpul de execut ie al algoritmului n funct ie de n, num arul de
date de intrare. Fiecare iterat ie a ciclului for o vom considera operat ie elementara.
Deci complexitatea algoritmului este O(n), at at n medie cat si n cazul cel mai
defavorabil.
4.3.2 Sortarea prin select ia maximului
Sort am crescator vectorul a, care are n componente.
for j=n,n-1,...,2
{
max=a[1];
pozmax=1;
for i=2,3,...,j
{
if a[i]>max { a[i]=max; pozmax=i; }
4.3. EXEMPLE 63
a[pozmax]=a[j];
a[j]=max;
}
}
Estim am complexitatea algoritmului n funct ie de n, dimensiunea vectorului.
La ecare iterat ie a ciclului for exterior este calculat maxa
1
, a
2
, ..., a
j
si plasat
pe pozit ia j, elementele de la j +1 la n ind deja plasate pe pozit iile lor denitive.
Conform exemplului anterior, pentru a calcula maxa
1
, a
2
, ..., a
j
sunt nece-
sare j 1 operat ii elementare, n total 1 + 2 + ... + (n 1) = n(n 1)/2. Deci
complexitatea algoritmului este de O(n
2
). S a observam ca timpul de execut ie este
independent de ordinea init ial a a elementelor vectorului.
4.3.3 Sortarea prin insert ie
Este o metoda de asemenea simpla, pe care o utiliz am adesea cand ordon am
cart ile la jocuri de cart i.
for i=2,3,...,n
{
val=a[i];
poz=i;
while a[poz-1]>val
{
a[poz]=a[poz-1];
poz=poz-1;
}
a[poz]=val;
}
Analiz am algoritmul n funct ie de n, dimensiunea vectorului ce urmeaza a
sortat. La ecare iterat ie a ciclului for elementele a
1
, a
2
, ..., a
i1
sunt deja ordonate
si trebuie s a inser am valorea a[i] pe pozit ia corectan sirul ordonat.

In cazul cel mai


defavorabil, c and vectorul este init ial ordonat descrescator, ecare element a[i] va
plasat pe prima pozit ie, deci ciclul while se executa de i 1 ori. Consider and drept
operat ie elementara comparat ia a[poz1] > val urmat a de deplasarea elementului
de pe pozit ia poz 1, vom avea n cazul cel mai defavorabil 1 +2 +... +(n 1) =
n(n 1)/2 operat ii elementare, deci complexitatea algoritmului este de O(n
2
).
S a analizam comportarea algoritmului n medie. Consider am ca elementele
vectorului sunt distincte si ca orice permutare a lor are aceeasi probabilitate de
aparit ie. Atunci probabilitatea ca valoarea a
i
sa e plasat a pe pozit ia k n sirul
a
1
, a
2
, ..., a
i
, k 1, 2, ...., i este 1/i. Pentru i xat, num arul mediu de operat ii
elementare este:
i

k=1
1
i
(k 1) =
1
i

i

k=1
(k 1) =
1
i
_
i(i + 1)
2
i
_
=
i + 1
2
1 =
i 1
2
64 CAPITOLUL 4. ANALIZA COMPLEXIT

ATII ALGORITMILOR
Pentru a sorta cele n elemente sunt necesare
n

i=2
i 1
2
=
1
2
_
n(n + 1)
2
1 (n 1)
_
=
n
2
_
(n + 1)
2
1
_
=
n(n 1)
4
operat ii elementare. Deci complexitatea algoritmului n medie este tot O(n
2
).
4.3.4 Sortarea rapida (quicksort)
Acest algoritm a fost elaborat de C.A.R. Hoare n 1960 si este unul dintre cei
mai utilizat i algoritmi de sortare.
void quicksort(int st, int dr)
{
int m;
if st<dr
{
m=divide(st, dr);
quicksort(st, m-1);
quicksort(m+1, dr);
}
}
Init ial apel am quicksort(1,n).
Funct ia divide are rolul de aplasa primul element (a[st]) pe pozit ia sa corecta
n sirul ordonat.

In st anga sa se vor gasi numai elemente mai mici, iar n dreapta
numai elemente mai mari decat el.
int divide(int st, int dr)
{
int i, j, val;
val=a[st];
i=st; j=dr;
while(i<j)
{
while((i<j) && (a[j] >= val)) j=j-1;
a[i]=a[j];
while((i<j) && (a[i] <= val)) i=i+1;
a[j]=a[i];
}
a[i]=val;
return i;
}
Observat ie : Vectorul a este considerat variabila global a.
4.3. EXEMPLE 65

In cazul cel mai defavorabil, cand vectorul a era init ial ordonat, se fac n 1
apeluri succesive ale procedurii quicksort, cu parametrii (1, n), (1, n1), ..., (1, 2)
(dac a vectorul a era init ial ordonat descrescator) sau (1, n), (2, n), ..., (n 1, n)
(dac a vectorul a era ordonat cresc ator).
La ecare apel al procedurii quicksort este apelata funct ia divide(1,i)
(respectiv divide(i, n)) care efectueaza i 1, (respectiv n i 1) operat ii
elementare.

In total num arul de operat ii elementare este (n1)+(n2)+... +1 =


n(n1)/2. Complexitatea algoritmului n cazul cel mai defavorabil este de O(n
2
).
S a analizam comportarea algoritmului n medie. Vom consider am ca orice
permutare a elementelor vectorului are aceeasi probabilitate de aparit ie si not am
cu T
n
num arul de operat ii elementare efectuate pentru a sorta n elemente.
Probabilitatea ca un element al vectorului s a e plasat pe pozit ia kn vectorul
ordonat, este de 1/n.
T
n
=
_
0, daca n = 0 sau n = 1
1
n

n
k=1
(T
k1
+T
nk
) + (n 1), daca n > 1
(pentru a ordona crescator n elemente, determinam pozit ia k n vectorul ordonat a
primului element, ceea ce necesita n1 operat ii elementare, sortam elementele din
stanga, ceea ce necesit a T
k1
operat ii elementare, apoi cele din dreapta, necesit and
T
nk
operat ii elementare).
Problema se reduce la a rezolva relat ia de recurent a de mai sus. Mai nt ai
observ am ca
T
0
+T
1
+... +T
n1
= Tn 1 +... +T
1
+T
0
.
Deci,
T
n
= n 1 +
2
n
n

k=1
T
k1

Inmult im ambii membri ai acestei relat ii cu n. Obt inem:


nT
n
= n(n 1) + 2
n

k=1
T
k1
Scazand din aceast a relat ie, relat ia obt inut a pentru n 1, adic a
(n 1)T
n1
= (n 1)(n 2) + 2
n1

k=1
T
k1
obt inem
nT
n
(n 1)T
n1
= n(n 1) (n 1)(n 2) + 2T
n1
de unde rezult a
nT
n
= 2(n 1) + (n + 1)T
n1
66 CAPITOLUL 4. ANALIZA COMPLEXIT

ATII ALGORITMILOR

Impart ind ambii membri cu n(n + 1) obt inem


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

k=3
k 1
k(k + 1)
Deci
T
n
n + 1
=
T
2
3
+2
n

k=3
_
1
k + 1

1
k
+
1
k + 1
_
=
T
2
3
+
2
n + 1
+2
n

k=3
1
k
2
n

k=1
1
k
2 lnn
Deci, n medie, complexitatea algoritmului este de O(nlog n).
4.3.5 Problema celebritat ii
Numim celebritate o persoan a care este cunoscuta de toat a lumea, dar nu
cunoaste pe nimeni. Se pune problema de a identica o celebritate, daca exista,
ntr-un grup de n persoane pentru care relat iile dintre persoane sunt cunoscute.
Putem reformula problema n limbaj de grafuri astfel: ind dat un digraf cu
n v arfuri, vericat i daca exista un v arf cu gradul exterior 0 si gradul interior n1.
Reprezentam graful asociat problemei prin matricea de adiacent a a
nn
a
i,j
=
_
1, daca persoana i cunoaste persoana j;
0, altfel.
O prim a solut ie ar sa calculam pentru ecare persoan a p din grup num arul
de persoane pe care p le cunoaste (out) si num arul de persoane care cunosc per-
soana p (in). Cu alte cuvinte, pentru ecare v arf din digraf calcul am gradul interior
si gradul exterior. Dac a gasim o persoan a pentru care out = 0 si in = n1, aceasta
va celebritatea cautat a.
celebritate=0;
for p=1,2,...,n
{
in=0; out=0;
for j=1,2,...,n
{
in=in+a[j][p];
out=out+a[p][j];
}
if (in=n-1) and (out = 0) celebritate=p;
}
if celebritate=0 writeln(Nu exista celebritati !)
else writeln(p, este o celebritate.);
4.4. PROBLEME 67
Se poate observa cu usurint a ca algoritmul este de O(n
2
). Putemmbun at at i
algoritmul f acand observat ia ca atunci c and test am relat iile dintre persoanele x si
y apar urm atoarele posibilit at ii:
a[x, y] = 0 si n acest caz y nu are nici o sansa sa e celebritate, sau
a[x, y] = 1 si n acest caz x nu poate celebritate.
Deci la un test elimin am o persoan a care nu are sanse sa e celebritate.
F acand succesiv n 1 teste, n nal vom avea o singur a persoan a candidat
la celebritate. Ramane sa calculam num arul de persoane cunoscute si num arul de
persoane care l cunosc pe acest candidat, singura celebritate posibila.
candidat=1;
for i=2,n
if a[candidat][i]=1 candidat=i;
out=0;
in=0;
for i=1,n
{
in=in+a[i][candidat];
out=out+a[candidat][i];
}
if (out=0) and (in=n-1) write(candidat, este o celebritate .)
else write(Nu exista celebritati.);

In acest caz algoritmul a devenit liniar.


4.4 Probleme
4.4.1 Probleme rezolvate
Problema 1 Care armat ii sunt adevarate:
a) n
2
O(n
3
)
b) n
3
O(n
2
)
c) 2
n+1
O(2
n
)
d) (n + 1)! O(n!)
e) f : N R

, f O(n) = f
2
O(n
2
)
f ) f : N R

, f O(n) = 2
f
O(2
n
)
Rezolvare:
a) Armat ia este adevarata pentru c a: lim
n
n
2
n
3
= 0 = n
2
O(n
3
).
b) Armat ia este falsa pentru c a: lim
n
n
3
n
2
=
68 CAPITOLUL 4. ANALIZA COMPLEXIT

ATII ALGORITMILOR
c) Armat ia este adevarata pentru c a: lim
n
2
n+1
2
n
= 2 = O(2
n+1
) =
O(2
n
).
d) Armat ia este falsa pentru c a: lim
n
(n+1)!
n!
= lim
n
n+1
1
=
e) Armat ia este adevarata pentru c a: f O(n) = c > 0 si n
0
N
astfel nc at f(n) < c n, n > n
0
. Rezult a ca c
1
= c
2
astfel nc a f
2
(n) < c
1
n
2
,
n > n
0
, deci f
2
O(n
2
).
e) Armat ia este adevarata pentru c a: f O(n) = c > 0 si n
0
N astfel
nc at f(n) < c n, n > n
0
. Rezult a ca c
1
= 2
c
astfel nc a 2
f(n)
< 2
cn
= 2
c
2
n
=
c
1
2
n
, n > n
0
, deci 2
f
O(2
n
).
Problema 2 Aratat i c a log n O(

n) dar

n / O(log n).
Indicat ie: Prelungim domeniile funct iilor pe R
+
, pe care sunt derivabile, si aplic am
relula lui LH ospital pentru log n/

n.
Problema 3 Demonstrat i urm atoarele armat ii:
i) log
a
(log
b
n), pentru oricare a, b > 1
ii)
n

i=1
i
k
(n
k+1
), pentru oricare k N
iii)
n

i=1
1
i
(nlog n)
iv) log n! (nlog n)
Indicat ii: La punctul iii) se t ine cont de relat ia

i=1
1
i
+ ln n
unde 0.5772 este constanta lui Euler.
La punctul iv) din n! < n
n
, rezult a log n! < nlog n, deci log n! O(nlog n).
Trebuie sa g asim si o margine inferioar a. Pentru 0 i n 1 este adevarat a
relat ia
(n i)(i + 1) n
Deoarece
(n!)
2
= (n 1)((n 1) 2)((n 2) 3)...(2 (n 1))(1 n) n
n
rezult a 2 log n! nlog n, adic a log n! 0.5nlog n, deci log n! (nlog n).
Relat ia se poate demonstra si folosind aproximarea lui Stirling
n!

2n
_
n
e
_
n
(1 + (1/n))
4.4. PROBLEME 69
4.4.2 Probleme propuse
1. Ar atat i ca:
a) n
3
+ 10
6
n

(n
3
)
b) n
2
n
+ 6 2
n
()(n
2
n
)
c) 2n
2
+nlog n (n
2
)
d) n
k
+n + n
k
log n (n
k
log n), k 1
e) log
a
n (log
b
n), a, b > 0, a ,= 1, b ,= 1.
2. Pentru oricare doua functii f, g : N R

demonstrat i ca
O(f +g) = O(max(f, g)) (4.4.1)
unde suma si maximul se iau punctual.
3. Fie f, g : N R
+
Demonstrat i ca:
i) lim
n
f(n)
g(n)
R
+
O(f) = O(g), ii) lim
n
f(n)
g(n)
= 0 O(f) O(g)
Observat ie: Implicat iile inverse nu sunt n general adev arate, deoarece se
poate ntampla ca limitele s a nu existe.
4. Demonstrat i prin induct ie ca pentru a determina maximul a n numere sunt
necesare n 1 comparat ii.
5. Care este timpul de execut ie a algoritmului quicksort pentru un vector
cu n componente egale?
6. S a consideram urm atorul algoritm de sortare a unui vector a cu n compo-
nente:
do
{
ok=true;
for i=1,n-1
if a[i]>a[i+1] { aux=a[i]; a[i]=a[i+1]; a[i+1]= aux; }
ok=false;
} while !ok;
Analizat i algoritmul n medie si n cazul cel mai defavorabil.
7. Analizat i complexitatea algoritmului de interclasare a doi vectori ordonat i,
a cu n componente, respectiv b cu m componente :
i=1; j=1; k=0;
while (i <= n) and (j <= m)
{
k=k+1;
if a[i] < b[j] { c[k]=a[i]; i=i+1; }
else { c[k]=b[j]; j=j+1; }
70 CAPITOLUL 4. ANALIZA COMPLEXIT

ATII ALGORITMILOR
}
for t=i,n { k=k+1; c[k]=a[t]; }
for t=j,m { k=k+1; c[k]=b[t]; }
8. Fiind dat a, un vector cu n componente distincte, vericat i daca o valoare
dat a x se gaseste sau nu n vector. Evaluat i complexitatea algoritmului n cazul
cel mai defavorabil si n medie.
9. Se d a a un vector cu n componente. Scriet i un algoritm liniar care s a
determine cea mai lunga secvent a de elemente consecutive de valori egale.
10. Fie T un text. Vericat i n timp liniar dac a un text dat T

este o permutare
circular a a lui T.
11. Fie X = (x
1
, x
2
, ..., x
n
) o secvent a de numere ntregi. Fiind dat x, vom
numi multiplicitate a lui x n X num arul de aparit ii ale lui x n X. Un element se
numeste majoritar daca multiplicitatea sa este mai mare decat n/2. Descriet i un
algoritm liniar care s a determine elementul majoritar dintr-un sir, dac a un astfel
de element exista.
12. Fie a
1
, a
2
, ..., a
n
si b
1
, b
2
, ..., b
m
, dou a mult imi de numere ntregi,
nenule (m < n). S a se determine x
1
, x
2
, ..., x
m
, o submult ime a mult imii a
1
, a
2
, ..., a
n

pentru care funct ia f(x


1
, x
2
, ..., x
m
) = a
1
x
1
+a
2
x
2
+... +a
n
x
m
ia valoare maxim a,
prin doi algoritmi de complexitate diferit a.
Capitolul 5
Recursivitate
Denit iile prin recurent a sunt destul de curente n matematic a: progresia
aritmetica, progresia geometrica, sirul lui Fibonacci, limite de siruri, etc.
5.1 Funct ii recursive
5.1.1 Funct ii numerice
Pentru calculul termenilor sirului lui Fibonacci, a transcriere literal a a for-
mulei este urmatoarea:
static int fib(int n) {
if (n <= 1)
return 1;
else
return fib(n-1) + fib(n-2);
}
fib este o funct ie care utilizeaza propriul nume n denit ia proprie. De asemenea,
daca argumentul n este mai mic decat 1 returneaza valoarea 1 iar n caz contrar
returneaz a fib(n 1) +fib(n 2).

In Java este posibil, ca de altfel n multe alte limbaje de programare (Fortran,


Pascal, C, etc), sa denim astfel de funct ii recursive. Dealtfel, toate sirurile denite
prin recurent a se scriu n aceasta manier a n Java, cum se poate observa din
urm atoarele doua exemple numerice: factorialul si triunghiul lui Pascal.
71
72 CAPITOLUL 5. RECURSIVITATE
static int fact(int n) {
if (n != 1)
return 1;
else
return n * fact (n1);
}
fibo(4)
fibo(0)
fibo(3)
fibo(2)
fibo(2) fibo(1)
fibo(1)
fibo(1)
fibo(0)
fact(4)
fact(3)
fact(2)
fact(1)
static int comb(int n, int p) {
if ((p == 0) || (p == n))
return 1;
else
return comb(n-1, p) + comb(n-1, p-1);
}
comb(4,2)
comb(3,2)
comb(2,2)
comb(3,1)
comb(2,1) comb(2,0)
comb(1,1) comb(1,0)
comb(2,1)
comb(1,1) comb(1,0)
Ne putem ntreba cum efectueaza Java calculul funct iilor recursive. Putem
sa r aspundem prin urm arirea calculelor n cazul calculului lui fibo(4). Reamintim
ca argumentele sunt transmise prin valoare n acest caz, iar un apel de funct ie
consta n evaluarea argumentului, apoi lansarea n execut ie a funct iei cu valoarea
5.1. FUNCT II RECURSIVE 73
argumentului. Deci
fibo(4) fibo(3) +fibo(2)
(fibo(2) +fibo(1)) +fibo(2)
((fibo(1) +fibo(1)) +fibo(1)) +fibo(2)
((1 +fibo(1)) +fibo(1)) +fibo(2)
((1 + 1) +fibo(1)) +fibo(2)
(2 +fibo(1)) +fibo(2)
(2 + 1) +fibo(2)
3 +fibo(2)
3 + (fibo(1) +fibo(1))
3 + (1 +fibo(1))
3 + (1 + 1)
3 + 2
5
Exist a deci un num ar semnicativ de apeluri succesive ale funct iei fib (9
apeluri pentru calculul lui fibo(4)). S a not am prin R
n
num arul apelurilor funct iei
fibo pentru calculul lui fibo(n). Evident R
0
= R
1
= 1, si R
n
= 1 +R
n1
+ R
n2
pentru n > 1. Pun and R

n
= R
n
+1, obt inem ca R

n
= R

n1
+R

n2
pentru n > 1,
si R

1
= R

0
= 2. Rezult a R

n
= 2 fibo(n) si de aici obt inem ca R
n
= 2 fibo(n)1.
Num arul de apeluri recursive este foarte mare! Exista o metoda iterativ a simpl a
care permite calculul lui fibo(n) mult mai repede.
_
fibo(n)
fibo(n 1)
_
=
_
1 1
1 0
_

_
fibo(n 1)
fibo(n 2)
_
= ... =
_
1 1
1 0
_
n

_
1
0
_
_
u
v
_
=
_
1 1
1 0
_

_
u0
v0
_
static int fibo(int n) {
int u, v;
int u0, v0;
int i;
u = 1; v = 1;
for (i = 2; i <= n; ++i) {
u0 = u; v0 = v;
u = u0 + v0;
v = v0;
}
return u;
}
74 CAPITOLUL 5. RECURSIVITATE
Se poate calcula si mai repede folosind ultima form a si calcul and puterea
matricei ...
Pentru a rezuma, o regul a bun a este sa nu ncercam sa intr am n meandrele
detaliilor apelurilor recursive pentru a nt elege sensul unei funct ii recursive.

In
general este sufucient s a nt elegem sintetic funct ia. Funct ia lui Fibonacci este un
caz particular n care calculul recursiv este foarte lung. Cam la fel se nt ampl a
(dac a nu chiar mai r au!) si cu triunghiul lui Pascal. Dar nu aceasta este situat ia
n general. Nu numai c a scrierea recursiva se poate dovedi ecace, dar ea este
totdeauna natural a si deci cea mai estetic a. Ea nu face dec at sa respecte denit ia
matematica prin recurent a. Este o metoda de programare foarte puternica.
5.1.2 Funct ia lui Ackerman
Sirul lui Fibonacci are o crestere exponent ial a. Exist a funct ii recursive care au
o crestere mult mai rapid a. Prototipul este funct ia lui Ackerman.

In loc sa denim
matematic aceasta funct ie, este de asemenea simplu sa d am denit ia recursiva n
Java.
static int ack(int m, int n) {
if (m == 0)
return n+1;
else
if (n == 0)
return ack (m-1, 1);
else
return ack(m-1, ack(m, n-1));
}
Se poate verica ca ack(0, n) = n + 1, ack(1, n) = n + 2, ack(2, n) 2n,
ack(3, n) 2
n
, ack(5, 1) ack(4, 4) 2
65536
> 10
80
, adic a num arul atomilor din
univers [11].
5.1.3 Recursii imbricate
Funct ia lui Ackerman cont ine dou a apeluri recursive imbricate ceea ce deter-
min a o crestere rapid a. Un alt exemplu este funct ia 91 a lui MacCarty [11]:
static int f(int n) {
if (n > 100)
return n-10;
else
return f(f(n+11));
}
5.2. PROCEDURI RECURSIVE 75
Pentru aceast a funct ie, calculul lui f(96) d a
f(96) = f(f(107)) = f(97) = ... = f(100) = f(f(111)) = f(101) = 91.
Se poate ar ata ca aceasta funct ie va returna 91 dac a n 100 si n 10 daca
n > 100. Aceasta funct ie anecdotica, care foloseste recursivitatea imbricata, este
interesant a pentru c nu este evident ca o astfel de denit ie da d a acelasi rezultat.
Un alt exemplu este funct ia lui Morris [11] care are urm atoarea forma:
static int g(int m, int n) {
if (m == 0)
return 1;
else
return g(m-1, g(m, n));
}
Ce valoare are g(1, 0)? Efectul acestui apel de funct ie se poate observa din
denit ia ei: g(1, 0) = g(0, g(1, 0)). Se declanseaza la nesf arsit apelul g(1, 0). Deci,
calculul nu se va termina niciodat a!
5.2 Proceduri recursive
Procedurile, la fel ca si funct iile, pot recursive si pot suporta apeluri recur-
sive. Exemplul clasic este cel al turnurilor din Hanoi. Pe 3 tije din fat a noastra,
numerotate 1, 2 si 3 de la stanga la dreapta, sunt n discuri de dimensiuni diferite
plasate pe tija 1 form and un con cu discul cel mai mare la baz a si cel mai mic n
v arf. Se doreste mutarea discurilor pe tija 3, mut and numai c ate un singur disc si
neplas and niciodat a un disc mai mare peste unul mai mic. Un rat ionament recur-
siv permite scrierea solut iei n c ateva r anduri. Dac a n 1, problema este triviala.
Presupunem problema rezolvat a pentru mutarea a n 1 discuri de pe tija i pe
tija j (1 i, j 3). Atunci, exista o solut ie foarte simpla pentru mutarea celor n
discuri de pe tija i pe tija j:
1. se muta primele n1 discuri (cele mai mici) de pe tija i pe tija k = 6ij,
2. se muta cel mai mare disc de pe tija i pe tija j,
3. se muta cele n 1 discuri de pe tija k pe tija j.
static void hanoi(int n, int i, int j) {
if (n > 0) {
hanoi (n-1, i, 6-(i+j));
System.out.println (i + " -> " + j);
hanoi (n-1, 6-(i+j), j);
}
}
76 CAPITOLUL 5. RECURSIVITATE
Aceste cateva linii de program arat a foarte bine cum generalizand problema,
adic a mutarea de pe oricare tija i pe oricare tij aj, un program recursiv de cateva
linii poate rezolva o problem a apriori complicat a. Aceasta este fort a recursivit at ii
si a rat ionamentului prin recurent a.
1 2 3 1 2 3
1 2 3 1 2 3
a) b)
c) d)
pasul 1
pasul 2
pasul 3
Capitolul 6
Analiza algoritmilor
recursivi
Am vazut n capitolul precedent cat de puternic a si util a este recursivitatea
n elaborarea unui algoritm. Cel mai important c astig al exprimarii recursive este
faptul c a ea este naturala si compacta.
Pe de alt a parte, apelurile recursive trebuie folosite cu discern amant, deoarece
solicit a si ele resursele calculatorului (timp si memorie).
Analiza unui algoritm recursiv implic a rezolvarea unui sistem de recurent e.
Vom vedea n continuare cum pot rezolvate astfel de recurent e.
6.1 Relat ii de recurent a
O ecuat ie n care necunoscutele sunt termenii x
n
, x
n+1
, ...x
n+k
ai unui sir
de numere se numeste relat ie de recurent a de ordinul k. Aceasta ecuat ie poate
satisf acut a de o innitate de siruri. Ca s a putem rezolva ecuat ia (relat ia de
recurent a) mai avem nevoie si de condit ii init iale, adic a de valorile termenilor
x
0
, x
1
, ..., x
k1
. De exemplu relat ia de recurent a
(n + 2)C
n+1
= (4n + 2)C
n
, pentru n 0, C
0
= 1
este de ordinul 1.
Daca un sir x
n
de numere satisface o formul a de forma
a
0
x
n
+a
1
x
n+1
+... +a
k
x
n+k
= 0, k 1, a
i
R, a
0
, a
k
,= 0 (6.1.1)
atunci ea se numeste relat ie de recurent a de ordinul k cu coecient i constant i.
Coecient ii sunt constant i n sensul ca nu depind de valorile sirului x
n
.
77
78 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
O astfel de formul a este de exemplu F
n+2
= F
n+1
+ F
n
, F
0
= 0, F
1
= 1,
adic a relat ia de recurent a care deneste sirul numerelor lui Fibonacci. Ea este o
relat ie de recurent a de ordinul 2 cu coecient i constant i.
6.1.1 Ecuat ia caracteristica
G asirea expresiei lui x
n
care sa satisfaca relat ia de recurent a se numeste
rezolvarea relat iei de recurent a. F acand substitut ia
x
n
= r
n
obt inem urmatoarea ecuat ie, numit a ecuat ie caracteristic a:
a
0
+a
1
r +a
2
r
2
+... +a
k
r
k
= 0 (6.1.2)
6.1.2 Solut ia generala
Solut ia generala a relat iei de recurent a omogena de ordinul k cu coecient i
constant i este de forma
x
n
=
k

i=1
c
i
x
(i)
n
(6.1.3)
unde
_
x
(i
n
)[i 1, 2, ..., k
_
sunt solut ii liniar independente ale relat iei de recurent a
(se mai numesc si sistem fundamental de solut ii). Pentru determinarea acestor
solut ii distingem urm atoarele cazuri:
Ecuat ia caracteristica admite radacini reale si distincte
Daca r
1
, r
2
, ..., r
k
sunt r ad acini reale ale ecuat iei caracteristice, atunci r
n
i
sunt
solut ii ale relat iei de recurent a.

Intr-adev ar, introduc and expresiile r


n
i
n relat ia de recurent a, obt inem:
a
0
r
n
i
+a
1
r
n+1
i
+a
2
r
n+2
i
+... +a
k
r
n+k
i
= r
n
i
_
a
0
+a
1
r
i
+a
2
r
2
i
+... +a
k
r
k
i
_
= 0
Daca r ad acinile r
i
(i = 1, 2, ..., k) sunt distincte, atunci relat ia de recurent a
are solut ia generala
x
n
= c
1
r
n
1
+c
2
r
n
2
+... +c
k
r
n
k
(6.1.4)
unde coecient ii c
1
, c
2
, ..., c
k
se pot determina din condit iile init iale.
Ecuat ia caracteristica admite radacini reale multiple
Fie r o r ad acin a multipl a de ordinul p a ecuat iei caracteristice. Atunci
r
n
, nr
n
, n
2
r
n
, ..., n
p1
r
n
sunt solut ii liniar independente ale relat iei de recurent a si
x
n
=
_
c
1
+c
2
n +... +c
p1
n
p1
_
r
n
(6.1.5)
6.1. RELATII DE RECURENT

A 79
este o solut ie a relat iei de recurent a. Acest lucru se mai poate demonstra usor daca
t inem cont de faptul c a o r ad acin a multipl a de ordinul p a unui polinom P(x) este
r ad acin a si a polinoamelor derivate P

(x), P

(x), ..., P
(p1)
(x).
Solut ia generala este suma dintre solut ia generala corespunzatoare r ad acinilor
simple ale ecuat iei caracteristice si solut ia general a corespunzatoare r ad acinilor
multiple.
Daca ecuat ia caracteristica are r ad acinile simple r
1
, r
2
, ..., r
s
si r ad acinile mul-
tiple r
s1
, r
s+2
, ..., r
s+t
de multiplicitate p
1
, p
2
, ..., p
t
(s+p
1
+p
2
+...+p
t
= k), atunci
solut ia generala a relat iei de recurent a este
x
n
= c
1
r
n
1
+c
2
r
n
2
+... +c
s
r
n
s
+
_
c
(1)
1
+c
(1)
2
n +... +c
(1)
p11
n
p11
_
+
...
_
c
(t)
1
+c
(t)
2
n +... +c
(1)
pt1
n
pt1
_
+
unde c
1
, ..., c
s
, c
(1)
1
, ..., c
(1)
p11
, ..., c
(t)
1
, ..., c
(t)
pt1
sunt constante, care se pot determina
din condit iile init iale.
Ecuat ia caracteristica admite radacini complexe simple
Fie r = ae
ib
= a(cos b + i sinb) o r ad acin a complexa. Ecuat ia caracteristica
are coecient i reali, deci si conjugata r = ae
ib
= a(cos b i sin b) este rad acin a
pentru ecuat ia caracteristica. Atunci solut iile corespunzatoare acestora n sistemul
fundamental de solut ii pentru recurent a liniar a si omogena sunt
x
(1)
n
= a
n
cos bn, x
(2)
n
= a
n
sinbn.
Ecuat ia caracteristica admite radacini complexe multiple Daca
ecuat ia caracteristica admite perechea de r ad acini complexe
r = ae
ib
, r = ae
ib
b ,= 0
de ordin de multiplicitate k, atunci solut iile corespunzatoare acestora n sistemul
fundamental de solut ii sunt
x
(1)
n
= a
n
cos bn, x
(2)
n
= na
n
cos bn, ..., x
(k)
n
= n
k1
a
n
cos bn,
x
(k+1)
n
= a
n
sin bn, x
(k+2)
n
= na
n
sin bn, ..., x
(2k)
n
= n
k1
a
n
sin bn,
Pentru a obt ine solut ia generala a recurent ei omogene de ordinul n cu coecient i
constant i se procedeaza astfel:
1. Se determina r ad acinile ecuat iei caracteristice
2. Se scrie contribut ia ecarei rad acini la solut ia general a.
3. Se nsumeaza si se obt ine solut ia generala n funct ie de n constante
arbitrare.
4. Daca sunt precizate condit iile init iale atunci se determin a constantele
si se obt ine o solut ie unic a.
80 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
6.2 Ecuat ii recurente neomogene
6.2.1 O forma simpla
Consider am acum recurent e de urmatoarea forma mai general a
a
0
t
n
+a
1
t
n1
+... +a
k
t
nk
= b
n
p(n)
unde b este o constant a, iar p(n) este un polinom n n de grad d. Ideea generala
este sa reducem un astfel de caz la o forma omogena.
De exemplu, o astfel de recurent a poate :
t
n
2t
n1
= 3
n

In acest caz, b = 3 si p(n) = 1.



Inmult im recurent a cu 3, si obt inem
3t
n
6t
n1
= 3
n+1

Inlocuind pe n cu n + 1 n recurent a init ial a, avem


t
n+1
2t
n
= 3
n+1
Scadem aceste doua ecuat ii
t
n+1
5t
n
+ 6t
n1
= 0
Am obt inut o recurent a omogena. Ecuat ia caracteristica este:
x
2
5x + 6 = 0
adic a (x 2)(x 3) = 0. Intuitiv, observ am ca factorul (x 2) corespunde p art ii
stangi a recurent ei init iale, n timp ce factorul (x 3) a ap arut ca rezultat al
calculelor efectuate pentru a sc apa de partea dreapt a.
Generalizand acest procedeu, se poate ar ata ca, pentru a rezolva ecuat ia
init ial a, este sucient sa lu am urm atoarea ecuat ie caracteristica:
(a
0
x
k
+a
1
x
k1
+ +a
k
)(x b)
d+1
= 0
Odat a ce s-a obt inut aceasta ecuat ie, se procedeaza ca n cazul omogen.
Vom rezolva acum recurent a corespunzatoare problemei turnurilor din Hanoi:
t
n
= 2t
n1
+ 1, n = 1
iar t
0
= 0. Rescriem recurent a astfel
t
n
2t
n1
= 1
6.2. ECUAT II RECURENTE NEOMOGENE 81
care este de forma generala prezentata la nceput, cu b = 1 si p(n) = 1. Ecuat ia
caracteristica este atunci (x 2)(x 1) = 0, cu solut iile 1 si 2. Solut ia general a a
recurent ei este:
t
n
= c
1
1
n
+c
2
2
n
Avem nevoie de doua condit ii init iale. Stim ca t
0
= 0; pentru a g asi cea de-a
doua condit ie calculam
t
1
= 2t
0
+ 1 = 1.
Din condit iile init iale, obt inem
t
n
= 2
n
1.
Daca ne intereseaza doar ordinul lui t
n
, nu este necesar s a calculam efectiv
constantele n solut ia generala. Dac a stim ca t
n
= c
1
1
n
+c
2
2
n
, rezult a t
n
O(2
n
).
Din faptul c a num arul de mut ari a unor discuri nu poate negativ sau
constant, deoarece avemn mod evident t
n
n, deducem c a c
2
> 0. Avem atunci
t
n
(2
n
) si deci, t
n
(2
n
). Putem obt ine chiar ceva mai mult. Substituind
solut ia generala napoi n recurent a init ial a, g asim
1 = t
n
2t
n1
= c
1
+c
2
2
n
2(c
1
+c
2
2
n1
) = c1
Indiferent de condit ia init ial a, c
1
este deci 1.
6.2.2 O forma mai generala
O ecuat ie recurenta neomogena de form a mai general a este:
k

j=0
a
j
Tn j = b
n
1
p
d1
(n) +b
n
2
p
d2
(n) +...
n care
p
d
(n) = n
d
+c
1
n
d1
+... +c
d
Ecuat ia caracteristica completa este:
_
_
k

j=0
a
j
r
kj
_
_
(r b
1
)
d1+1
(r b
2
)
d2+1
... = 0
Exemplul 3: T
n
= 2T(n 1) +n + 2
n
, n 1, T
0
= 0.
Acestui caz i corespund b
1
= 1, p
1
(n) = n, d
1
= 1 si b
2
= 2, p
2
(n) = 1,
d
2
= 0, iar ecuat ia caracteristica completa este:
(r 2)(r 1)
2
(r 2) = 0
82 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
cu solut ia:
T(n) = c
1
1
n
+c
2
n 2
n
+c
3
2n +c
4
n 2
n
_

_
T(0) = 0
T(1) = 2T(0) + 1 + 2
1
= 3
T(2) = 2T(1) + 2 + 2
2
= 12
T(3) = 2T(2) + 3 + 2
3
= 35
_

_
c
1
+ c
3
= 0
c
1
+c
2
+ 2c
3
+ 2c
4
= 3
c
1
+ 2c
2
+ 4c
3
+ 8c
4
= 12
c
1
+ 3c
2
+ 8c
3
+ 24c
4
= 35

_
c
1
= 2
c
2
= 1
c
3
= 2
c
4
= 1
Deci
T(n) = 2 n + 2
n+1
+n 2
n
= O(n 2
n
).
6.2.3 Teorema master
De multe ori apare relat ia de recurent a de forma
T(n) = aT(n/b) +f(n) (6.2.6)
unde a si b sunt constante iar f(n) este o funct ie (aplicarea metodei Divide et
Impera conduce de obicei la o astfel de ecuat ie recurenta). Asa numita teorema
Master d a o metoda general a pentru rezolvarea unor astfel de recurent e cand f(n)
este un simplu polinom. Solut ia dat a de teorema master este:
1. dac a f(n) = O
_
n
log
b
(a)
_
cu > 0 atunci T(n) =
_
n
log
b
a
_
2. dac a f(n) =
_
n
log
b
a
_
atunci T(n) =
_
n
log
b
a
lg n
_
3. dac a f(n) =
_
n
log
b
(a+)
_
si a f
_
n
b
_
c f(n) cu c < 1 atunci T(n) = (f(n)).
Din p acate, teorema Master nu funct ioneaza pentru toate funct iile f(n), si
multe recurent e utile nu sunt de forma (6.2.6). Din fericire ns a, aceasta este o
tehnic a de rezolvare a celor mai multe relat ii de recurent a provenite din metoda
Divide et Impera.
Pentru a rezolva astfel de ecuat ii recurente vom reprezenta arborele generat
de ecuat ia recursiva. R ad acina arborelui cont ine valoarea f(n), si ea are noduri
descendente care sunt noduri rad acin a pentru arborele provenit din T(n/b).
6.2. ECUAT II RECURENTE NEOMOGENE 83
Pe nivelul i se aa nodurile care cont in valoarea a
i
f(n/b
i
). Recursivitatea se
opreste cand se obt ine un caz de baza pentru recurent a.
Presupunem c a T(1) = f(1).
Cu aceasta reprezentare este foarte clar ca T(n) este suma valorilor din
nodurile arborelui. Presupun and c a ecare nivel este plin, obt inem
T(n) = f(n) +af(n/b) +a
2
f(n/b
2
) +a
3
f(n/b
3
) +... +a
k
f(n/b
k
)
unde k este adancimea arborelui de recursivitate. Din n/b
k
= 1 rezult a k = log
b
n.
Ultimul termen diferit de zero n suma este de forma a
k
= a
log
b
n
= n
log
b
a
(ultima
egalitate ind nt alnit a n liceu!).
Acum putem usor enunt a si demonstra teorema Master.
Teorema 1 (Teorema Master) Relat ia de recurent a T(n) = aT(n/b)+f(n) are
urm atoarea solut ie:
daca af(n/b) = f(n) unde < 1 atunci T(n) = (f(n));
daca af(n/b) = f(n) unde > 1 atunci T(n) = (n
log
b
a
);
daca af(n/b) = f(n) atunci T(n) = (f(n) log
b
n);
Demonstrat ie: Daca f(n) este un factor constant mai mare dec at f(b/n), atunci
prin induct ie se poate arata ca suma este a unei progresii geometrice descrescatoare.
Suma n acest caz este o constant a nmult it a cu primul termen care este f(n).
Daca f(n) este un factor constant mai mic dec at f(b/n), atunci prin induct ie
se poate arata ca suma este a unei progresii geometrice crescatoare. Suma n acest
caz este o constanta nmult ita cu ultimul termen care este n
log
b
a
.
84 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
Daca af(b/n) = f(n), atunci prin induct ie se poate arata ca ecare din cei
k + 1 termeni din sum a sunt egali cu f(n).
Exemple.
1. Select ia aleatoare: T(n) = T(3n/4) +n.
Aici af(n/b) = 3n/4 iar f(n) = n, rezult a = 3/4, deci T(n) = (n).
2. Algoritmul de multiplicare al lui Karatsuba: T(n) = 3T(n/2) +n.
Aici af(n/b) = 3n/2 iar f(n) = n, rezult a = 3/2, deci T(n) = (n
log
2
3
).
3. Mergesort: T(n) = 2T(n/2) +n.
Aici af(n/b) = n, iar f(n) = n, rezult a = 1 deci T(n) = (nlog
2
n).
Folosind acceasi tehnic a a arborelui recursiv, putem rezolva recurent e pentru
care nu se poate aplica teorema Master.
6.2.4 Transformarea recurent elor
La Mergesort am avut o relai e de recurent a de forma T(n) = 2T(n/2) +n si
am obt inut solut ia T(n) = O(nlog
2
n) folosind teorema Master (metoda arborelui
de recursivitate). Aceasta modalitate este corecta daca n este o putere a lui 2, dar
pentru alte valori ale lui n aceasta recurent a nu este corecta. Cand n este impar,
recurent a ne cere sa sortam un num ar elemente care nu este ntreg! Mai r au chiar,
daca n nu este o putere a lui 2, nu vom atinge niciodat a cazul de baza T(1) = 0.
Pentru a obt ine o recurent a care sa e valid a pentru orice valori ntregi ale
lui n, trebuie s a determinam cu atent ie marginile inferioar a si superioar a:
T(n) = T(n/2|) + T(n/2|) +n.
Metoda transform arii domeniului rescrie funct ia T(n) sub forma S(f(n)),
unde f(n) este o funct ie simpla si S() are o recurent a mai usoara.
Urmatoarele inegalitat i sunt evidente:
T(n) 2T(n/2|) +n 2T(n/2 + 1) +n.
Acum denim o noua funct ie S(n) = T(n + ), unde este o constant a
necunoscuta, aleas a astfel nc at sa e satisfacut a recurent a din teorema Master
S(n) S(n/2) + O(n). Pentru a obt ine valoarea corecta a lui , vom compara
dou a versiuni ale recurent ei pentru funct ia S(n +):
_
S(n) 2S(n/2) +O(n) T(n +) 2T(n/2 +) +O(n)
T(n) 2T(n/2 + 1) +n T(n +) 2T((n +)/2 + 1) +n +
Pentru ca aceste doua recurent e sa e egale, trebuie ca n/2+ = (n+)/2+1,
care implic a = 2. Teorema Master ne spune acum ca S(n) = O(nlog n), deci
T(n) = S(n 2) = O((n 2) log(n 2) = O(nlog n).
6.2. ECUAT II RECURENTE NEOMOGENE 85
Un argument similar d a o ajustare a marginii inferioare T(n) = (nlog n).
Deci, T(n) = (nlog n) este un rezultat ntemeiat desi am ignorat marginile
inferioar a si superioar a de la nceput!
Transformarea domeniului este util a pentru nl aturarea marginilor inferioar a
si superioar a, si a termenilor de ordin mic din argumentele oric arei recurent e care
se potriveste un pic cu teorema master sau metoda arborelui de recursivitate.
Exist a n geometria computat ional a o structura de date numit a arbore pliat,
pentru care costul operatiei de c autare ndeplineste relat ia de recurent a
T(n) = T(n/2) +T(n/4) + 1.
Aceasta nu se potriveste cu teorema master, pentru ca cele doua subprobleme
au dimensiuni diferite, si utiliz and metoda arborelui de recursivitate nu obt inem
dec at niste margini slabe

n << T(n) << n.
Daca nu au forma standard, ecuat iile recurente pot aduse la aceast a form a
printr-o schimbare de variabil a. O schimbare de variabil a aplicabil a pentru ecuat ii
de recurent a de tip multiplicativ este:
n = 2
k
k = log n
De exemplu, e
T(n) = 2 T(n/2) +n log n, n > 1
Facem schimbarea de variabila t(k) = T(2
k
) si obt inem:
t(k) 2 t(k 1) = k 2
k
, deci b = 2, p(k) = k, d = 1
Ecuat ia caracteristica completa este:
(r 2)
3
= 0
cu solut ia
t(k) = c
1
2
k
+c
2
k 2
k
+ c
3
k
2
2
k
Deci
T(n) = c
1
n +c
2
n log n +c
3
n log
2
n O(n log
2
n[n = 2
k
)
Uneori, printr-o schimbare de variabil a, putem rezolva recurent e mult mai
complicate.

In exemplele care urmeaza, vom nota cu T(n) termenul general al
recurent ei si cu t
k
termenul noii recurent e obt inute printr-o schimbare de variabil a.
Presupunem pentru nceput c a n este o putere a lui 2.
Un prim exemplu este recurenta
T(n) = 4T(n/2) +n, n > 1
86 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
n care nlocuim pe n cu 2
k
, notam t
k
= T(2
k
) = T(n) si obt inem
t
k
= 4t
k1
+ 2
k
Ecuat ia caracteristica a acestei recurent e liniare este
(x 4)(x 2) = 0
si deci, t
k
= c
1
4
k
+c
2
2
k
.

Inlocuim la loc pe k cu log
2
n
T(n) = c
1
n
2
+c
2
n
Rezulta
T(n) O(n
2
[n este o putere a lui 2)
Un al doilea exemplu l reprezinta ecuat ia
T(n) = 4T(n/2) +n
2
, n > 1
Proced and la fel, ajungem la recurenta
t
k
= 4t
k1
+ 4
k
cu ecuat ia caracteristica
(x 4)
2
= 0
si solut ia general a t
k
= c
1
4
2
+c
2
k4
2
.
Atunci,
T(n) = c
1
n
2
+c
2
n
2
lg n
si obt inem
T(n) O(n
2
log n[n este o putere a lui 2)

In sf arsit, s a consider am si exemplul


T(n) = 3T(n/2) +cn, n > 1
c ind o constanta. Obt inem succesiv
T(2
k
) = 3T(2
k1
) +c2
k
t
k
= 3t
k1
+c2
k
cu ecuat ia caracteristica
(x 3)(x 2) = 0
t
k
= c
1
3
k
+c
2
2
k
T(n) = c
1
3
lg n
+c
2
n
6.3. PROBLEME REZOLVATE 87
si, deoarece
a
lg b
= b
lg a
obt inem
T(n) = c
1
n
lg 3
+c
2
n
deci,
T(n) O(n
lg 3
[n este o putere a lui 2)
Putem enunt a acum o proprietate care este utila ca ret eta pentru analiza
algoritmilor cu recursivit at i de forma celor din exemplele precedente.
Fie T : N R
+
o funct ie eventual nedescrescatoare
T(n) = aT(n/b) +cn
k
, n > n
0
unde: n
0
1, b 2 si k 0 sunt ntregi; a si c sunt numere reale pozitive; n/n
0
este o putere a lui b. Atunci avem
T(n)
_

_
(n
k
), pentru a < b
k
;
(n
k
log n), pentru a = b
k
;
(n
log
b
a
), pentru a > b
k
;
6.3 Probleme rezolvate
1. S a se rezolve ecuat ia:
F
n+2
= F
n+1
+F
n
, F
0
= 0, F
1
= 1.
Ecuat aia caracteristica corespunzatoare
r
2
r 1 = 0
are solut iile
r
1
=
1 +

5
2
, r
2
=
1

5
2
.
Solut ia generala este
F
n
= c
1
_
1 +

5
2
_
n
+c
2
_
1

5
2
_
n
.
Determinam constantele c
1
si c
2
din condit iile init iale F
0
= 0 si F
1
= 1.
Rezolvand sistemul
_
c
1
+c
2
= 0
c
1
_
1+

5
2
_
+c
2
_
1

5
2
_
= 1
88 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
obt inem c
1
=
1

5
si c
1
=
1

5
.
Deci, solut ia relat iei de recurent a care deneste numerele lui Fibonacci este:
F
n
=
1

5
_
1 +

5
2
_
n

5
_
1

5
2
_
n
2. S a se rezolve relat ia de recurent a:
x
n+3
= x
n+2
+ 8x
n+1
12x
n
, x
0
= 0, x
1
= 2, x
2
= 3.
Ecuat ia caracteristica corespunzatoare este:
r
3
r
2
8r + 12 = 0
si are solut iile: r
1
= r
2
= 2 si r
3
= 3.
Solut ia generala este de forma:
x
n
= (c
1
+nc
2
)2
n
+c
3
(3)
n
.
Din condit iile init iale rezulta constantele: c
1
=
1
5
, c
2
=
1
2
si c
3
=
1
5
.
Solut ia generala este:
x
n
=
_
n
2
+
1
5
_
2
n

1
5
(3)
n
.
3. S a se rezolve relat ia de recurent a:
x
n+3
= 6x
n+2
12x
n+1
+ 8x
n
, x
0
= 0, x
1
= 2, x
2
= 4.
Ecuat ia caracteristica corespunzatoare este:
r
3
6r
2
+ 12r 8 = 0
si are solut iile: r
1
= r
2
= r
3
= 2.
Solut ia generala este de forma:
x
n
= (c
1
+c
2
n +c
3
n
2
)2
n
.
Din condit iile init iale rezulta constantele: c
1
= 0, c
2
=
3
2
si c
3
=
1
2
.
Solut ia generala este:
x
n
=
_
3
2
n
1
2
n
2
_
2
n
= (3n n
2
)2
n1
.
4. S a se rezolve relat ia de recurent a:
x
n+2
= 2x
n+1
2x
n
, x
0
= 0, x
1
= 1.
6.3. PROBLEME REZOLVATE 89
Ecuat ia caracteristica corespunzatoare este:
r
2
2r + 2 = 0
si are solut iile: r
1
= 1 +i si r
2
= 1 i care se pot scrie sub forma trigonometrica
astfel:
r
1
=

2
_
cos

4
+i sin

4
_
, r
2
=

2
_
cos

4
i sin

4
_
.
Solut iile fundamentale sunt:
x
(1)
n
=
_

2
_
n
cos
n
4
, x
(2)
n
=
_

2
_
n
sin
n
4
.
Solut ia generala este de forma:
x
n
=
_

2
_
n
_
c
1
cos
n
4
+c
2
sin
n
4
_
.
Din condit iile init iale rezulta constantele: c
1
= 0 si c
2
= 1.
Solut ia generala este:
x
n
=
_

2
_
n
sin
n
4
.
5. S a se rezolve relat ia de recurent a:
x
n+3
= 4x
n+2
6x
n+1
+ 4x
n
, x
0
= 0, x
1
= 1, x
2
= 1.
Ecuat ia caracteristica corespunzatoare este:
r
3
4r
2
+ 6r 4 = 0
si are solut iile: r
1
= 2, r
2
= 1 +i si r
3
= 1 i.
Solut ia generala este de forma:
x
n
= c
1
2
n
+c
2
_

2
_
n
cos
n
4
+c
3
_

2
_
n
sin
n
4
.
Din condit iile init iale rezulta constantele: c
1
=
1
2
, c
2
=
1
2
si c
3
=
3
2
.
Solut ia generala este:
x
n
= 2
n1
+
_
2
_
n
2
_
cos
n
4
+ 3 sin
n
4
_
.
6. S a se rezolve relat ia de recurent a:
T(n) 3T(n 1) + 4T(n 2) = 0, n 2, T(0) = 0, T(1) = 1.
90 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
Ecuat ia caracteristica r
2
3r + 4 = 0 are solut iile r
1
= 1, r
2
= 4, deci
T(n) = c
1
(1)
n
+c
2
4
n
Constantele se determina din condit iile init iale:
_
c
1
+c
2
= 0
c
1
+ 4c
2
= 1

_
c
1
=
1
5
c
2
=
1
5
Solut ia este:
T(n) =
1
5
[4
n
(1)
n
] .
7. S a se rezolve relat ia de recurent a:
T(n) = 5T(n1) 8T(n2) +4T(n3), n 3, cu T(0) = 0, T(1) = 1, T(2) = 2.
Ecuat ia caracteristica:
r
3
5r
2
+ 8r 4 = 0 r
1
= 1, r
2
= r
3
= 2
deci
T(n) = c
1
1
n
+c
2
2
n
+c
3
n2
n
Determinarea constantelor
_

_
c
1
+c
2
= 0
c
1
+ 2c
2
+ 2c
3
= 1
c
1
+ 4c
2
+ 8c
3
= 2

_
c
1
= 2
c
2
= 2
c
3
=
1
2
Deci
T(n) = 2 + 2
n+1

n
2
2
n
= 2
n+1
n2
n1
2.
8. S a se rezolve relat ia de recurent a:
T(n) = 4T(n/2) +nlg n.

In acest caz, avem af(n/b) = 2nlg n 2n, care nu este tocmai dublul lui
f(n) = nlg n. Pentru n sucient de mare, avem 2f(n) > af(n/b) > 1.9f(n).
Suma este marginit a si inferior si superior de c atre serii geometrice cresc atoare,
deci solut ia este T(n) = (n
log
2
4
) = (n
2
). Acest truc nu merge n cazurile doi si
trei ale teoremei Master.
9. S a se rezolve relat ia de recurent a:
T(n) = 2T(n/2) +nlg n.
6.3. PROBLEME REZOLVATE 91
Nu putem aplica teorema Master pentru c a af(n/b) = n/(lg n 1) nu este
egal a cu f(n) = n/ lg n, iar diferent a nu este un factor constant.
Trebuie sa calcul am suma pe ecare nivel si suma total a n alt mod. Suma
tuturor nodurilor de pe nivelul i este n/(lg n i).

In particular, aceasta nseamna
ca ad ancimea arborelui este cel mult lg n 1.
T(n) =
lg n1

i=0
n
lg n i
=
lg n

j=1
n
j
= nH
lg n
= (nlg lg n).
10. (Quicksort aleator). S a se rezolve relat ia de recurent a:
T(n) = T(3n/4) +T(n/4) +n.

In acest caz nodurile de pe acelasi nivel al arborelui de recursivitate au diferite


valori. Nodurile din orice nivel complet (adic a, deasupra oric arei frunze) au suma
n, deci este la fel ca n ultimul caz al teoremei Master si orice frunz a are nivelul
ntre log
4
n si log
4/3
n.
Pentru a obt ine o margine superioara, vom supraevalua T(n) ignor and cazurile
de baza si extinz and arborele n jos c atre nivelul celei mai ad anci frunze.
Similar, pentru a obt ine o margine inferioar a pentru T(n), vom subevalua
T(n) contoriz and numai nodurile din arbore p an a la nivelul frunzei care este cea
mai put in ad anc a. Aceste observat ii ne dau marginile inferioar a si superioar a:
nlog
4
n T(n) nlog
4/3
n.
Deoarece aceste margini difer a numai printr-un factor constant, avem c a
T(n) = (nlog n).
11. (Select ie determinista). S a se rezolve relat ia de recurent a:
T(n) = T(n/5) +T(7n/10) +n.
Din nou, avem un arbore recursiv trunchiat. Dac a ne uit am numai la
nivelurile complete ale arborelui, observ am ca suma pe nivel formeaza o serie
geometrica descrescatoare T(n) = n+9n/10 +81n/100+..., deci este ca n primul
caz al teoremei Master. Putem s a obt inem o margine superioar a ignor and cazurile
de baza n totalitate si crescand arborele spre innit, si putem obt ine o margine
inferioar a contoriz and numai nodurile din nivelurile complete.

In ambele situat ii,
seriile geometrice sunt majorate de termenul cel mai mare, deci T(n) = (n).
12. S a se rezolve relat ia de recurent a:
T(n) = 2

n T(

n) +n.
Avem cel mult lg lg n niveluri dar acum avem nodurile de pe nivelul i care
au suma 2
i
n. Avem o serie geometrica crescatoare a sumelor nivelurilor, la fel ca
92 CAPITOLUL 6. ANALIZA ALGORITMILOR RECURSIVI
n cazul doi din teorema Master, deci T(n) este majorata de suma nivelurilor cele
mai ad anci. Se obt ine:
T(n) = (2
lg lg n
n) = (nlog n).
13. S a se rezolve relat ia de recurent a:
T(n) = 4

n T(

n) +n.
Suma nodurilor de pe nivelul i este 4
i
n. Avem o serie geometrica crescatoare,
la fel ca n cazul doi din teorema master, deci nu trebuie dec at sa avem grij a de
aceste niveluri. Se obt ine
T(n) = (4
lg lg n
n) = (nlog
2
n).
Capitolul 7
Algoritmi elementari
7.1 Operat ii cu numere
7.1.1 Minim si maxim
S a presupunem c a dorim s a determinam valorile minim a si maxima dintru-un
vector x[1..n. Proced am astfel:
vmin = x[1];
vmax = x[1];
for i=2, n
vmin = minim(vmin, x[i])
vmax = maxim(vmax, x[i])
Evident se fac 2n2 comparat ii. Se poate mai repede? Da!

Impart im sirul n
dou a si determin am vmin si vmax n cele doua zone. Comparam vmin1 cu vmin2
si stabilim vminm. La fel pentru vmax. Prelucrarea se repeta pentru cele dou a
zone (deci se foloseste recursivitatea). Apar c ate dou a comparat ii n plus de ecare
dat a. Dar c ate sunt n minus? Presupunem c a n este o putere a lui 2 si T(n) este
num arul de comparat ii. Atunci
T(n) = 2T(n/2) + 2 si T(2) = 1.
Cum rezolvam aceasta relat ie de recurent a? Banuim c a solut ia este de forma
T(n) = an +b. Atunci a si b trebuie sa satisfaca sistemul de ecuat ii
_
2a +b = 1
an +b = 2(an/2 +b) + 2
93
94 CAPITOLUL 7. ALGORITMI ELEMENTARI
care are solut ia b = 2 si a = 3/2, deci (pentru n putere a lui 2), T(n) = 3n/22,
adic a 75% din algoritmul anterior. Se poate demonstra c a num arul de comparat ii
este 3 n/2| 2 pentru a aa minimum si maximum.
O idee similar a poate aplicat a pentru varianta secvent iala. Presupunem c a
sirul are un num ar par de termeni. Atunci, algoritmul are forma:
vmin = minim(x[1],x[2])
vmax = maxim(x[1],x[2])
for(i=3;i<n;i=i+2)
cmin = minim(x[i],x[i+1])
cmax = maxim(x[i],x[i+1])
if cmin < vmin
vmin = cmin
if vmax > cmax
vmax = cmax
Fiecare iterat ie necesita trei comparat ii, iar init ializarea variabilelor necesit a
o comparat ie. Ciclul se repeta de (n 2)/2 ori, deci avem un total de 3n/2 2
comparat ii pentru n par.
7.1.2 Divizori
Fie n un num ar natural. Descompunerea n facori primi
n = p
1
1
p
2
2
... p

k
k
(7.1.1)
se numeste descompunere canonica. Daca not am prin d(n) num arul divizorilor lui
n N, atunci:
d(n) = (1 +
1
)(1 +
2
)...(1 +
k
) (7.1.2)
Pentru calculul lui d(n) putem folosi urm atorul algoritm:
static int ndiv(int n)
{
int d,p=1,nd;
d=2;nd=0;
while(n%d==0){nd++;n=n/d;}
p=p*(1+nd);
d=3;
while(d*d<=n)
{
nd=0;
while(n%d==0){nd++;n=n/d;}
7.2. ALGORITMUL LUI EUCLID 95
p=p*(1+nd);
d=d+2;
}
if(n!=1) p=p*2;
return p;
}
7.1.3 Numere prime
Pentru testarea primalit at i unui num ar putem folosi urm atorul algoritm:
static boolean estePrim(int nr)
{
int d;
if(nr<=1) return false;
if(nr==2) return true;
if(nr%2==0) return false;
d=3;
while((d*d<=nr)&&(nr%d!=0)) d=d+2;
if(d*d>nr) return true; else return false;
}
7.2 Algoritmul lui Euclid
7.2.1 Algoritmul clasic
Un algoritm pentru calculul celui mai mare divizor comun (cmmdc) a dou a
numere naturale poate descompunerea lor n factori si calculul produsului tuturor
divizorilor comuni. De exemplu dac a a = 1134 = 2 3 3 3 3 7 si b = 308 =
2 2 7 11 atunci cmmdc(a, b) = 2 7 = 14.
Descompunerea n factori a unui num ar natural n poate necesita ncercarea
tuturor numerelor naturale din intervalul [2,

n].
Un algoritm ecient pentru calculul cmmdc(a, b) este algoritmul lui Euclid.
static int cmmdc(int a, int b}
{
int c;
if (a < b) { c = a; a = b; b = c; }
while((c=a%b) != 0) { a = b; b = c;}
return b;
}
96 CAPITOLUL 7. ALGORITMI ELEMENTARI
Pentru a = 1134 si b = 308 se obt ine:
a
0
= 1134, b
0
= 308;
a
1
= 308, b
1
= 210;
a
2
= 210, b
2
= 98;
a
3
= 98, b
3
= 14.
Lema 1 cmmdc(a x b, b) = cmmdc(a, b).
Demonstrat ie: Pentru nceput ar at am ca cmmdc(a x b, b) >= cmmdc(a, b).
Presupunem c a d divide a s b, deci a = c
1
d si b = c
2
d. Atunci d divide a x b
pentru c a a x b = (c
1
x c
2
) d.
Demonstram si inegalitatea contrar a cmmdc(a x b, b) <= cmmdc(a, b).
Presupunem c a d divide a x b si b, deci a x b = c
3
d si b = c
2
d. Atunci d
divide a pentru c a a = (a x b) +x b = (c
3
+x c
2
) d. De aici rezult a ca
cmmdc(b, c) = cmmdc(c, b) = cmmdc(a mod b, b) = gcd(a, b).
Prin induct ie rezulta ca cel mai mare divizor comun al ultimelor dou a numere
este egal cu cel mai mare divizor comun al primelor dou a numere. Dar pentru cele
dou a numere a si b din nal, cmmdc(a, b) = b, pentru c a b divide a.
7.2.2 Algoritmul lui Euclid extins
Pentru orice dou a numere intregi pozitive, exist a x si y (unul negativ) astfel
nc at xa+y b = cmmdc(a, b). Aceste numere pot calculate parcurgandnapoi
algoritmul clasic al lui Euclid.
Fie a
k
si b
k
valorile lui a si b dup a k iterat ii ale buclei din algoritm. Fie x
k
si y
k
numerele care indeplinesc relat ia x
k
a
k
+y
k
b
k
= cmmdc(a
k
, b
k
) = cmmdc(a, b).
Prin inductie presupunem c a x
k
s y
k
exista, pentru c a la sf arsit, cand b
k
divide
a
k
, putem lua x
k
= 0 s y
k
= 1.
Presupun and c a x
k
si y
k
sunt cunoscute, putem calcula x
k1
si y
k1
.
a
k
= b
k1
si b
k
= a
k1
mod b
k1
= a
k1
d
k1
b
k1
, unde
d
k1
= a
k1
/b
k1
(mp art ire ntreag a).
Substituind aceste expresii pentru a
k
si b
k
obt inem
cmmdc(a, b) = x
k
a
k
+y
k
b
k
= x
k
b
k1
+y
k
(a
k1
d
k1
b
k1
)
= y
k
a
k1
+ (x
k
y
k
d
k1
) b
k1
.
Astfel, t in and cont de relat ia x
k1
a
k1
+y
k1
b
k1
= cmmdc(a, b), obt inem
x
k1
= y
k
,
y
k1
= x
k
y
k
d
k1
.
Pentru 1134 si 308, obt inem:
a
0
= 1134, b
0
= 308, d
0
= 3;
a
1
= 308, b
1
= 210, d
1
= 1;
a
2
= 210, b
2
= 98, d
2
= 2;
a
3
= 98, b
3
= 14, d
3
= 7.
7.3. OPERATII CU POLINOAME 97
si de asemenea, valorile pentru x
k
si y
k
:
x
3
= 0, y
3
= 1;
x
2
= 1, y
2
= 0 1 2 = 2;
x
1
= 2, y
1
= 1 + 2 1 = 3;
x
0
= 3, y
1
= 2 3 3 = 11.
Desigur relat ia 3 1134 11 308 = 14 este corecta. Solut ia nu este unic a.
S a observam ca (3 + k 308) 1134 (11 + k 1134) 308 = 14, pentru orice k,
ceea ce arata ca valorile calculate pentru x = x
0
si y = y
0
nu sunt unice.
7.3 Operat ii cu polinoame
Toate operat iile cu polinoame obisnuite se fac utiliz and siruri de numere
care reprezint a coecient ii polinomului. Not am cu a si b vectorii coecient ilor
polinoamelor cu care se opereaz a si cu m si n gradele lor. Deci
a(X) = a
m
X
m
+... +a
1
X + a
0
si b(X) = b
n
X
n
+... +b
1
X +b
0
.
7.3.1 Adunarea a doua polinoame
Este aseman atoare cu adunarea numerelor mari.
static int[] sumap(int[] a, int[] b)
{
int m,n,k,i,j,minmn;
int[] s;
m=a.length-1;
n=b.length-1;
if(m<n) {k=n; minmn=m;} else {k=m; minmn=n;}
s=new int[k+1];
for(i=0;i<=minmn;i++) s[i]=a[i]+b[i];
if(minmn<m) for(i=minmn+1;i<=k;i++) s[i]=a[i];
else for(i=minmn+1;i<=k;i++) s[i]=b[i];
i=k;
while((s[i]==0)&&(i>=1)) i--;
if(i==k) return s;
else
{
int[] ss=new int[i+1];
for(j=0;j<=i;j++) ss[j]=s[j];
return ss;
}
}
98 CAPITOLUL 7. ALGORITMI ELEMENTARI
7.3.2

Inmult irea a doua polinoame
Evident, gradul polinomului produs p = a b este m+n iar coecientul p
k
este
suma tuturor produselor de forma a
i
b
j
unde i +j = k, 0 i m si 0 j n.
static int[] prodp(int[] a, int[] b)
{
int m,n,i,j;
int[] p;
m=a.length-1;
n=b.length-1;
p=new int[m+n+1];
for(i=0;i<=m;i++)
for(j=0;j<=n;j++)
p[i+j]+=a[i]*b[j];
return p;
}
7.3.3 Calculul valorii unui polinom
Valoarea unui polinom se calculeaza ecient cu schema lui Horner:
a(x) = (...((a
n
x +a
n1
) x +a
n2
) x +... + a
1
) x +a
0
static int valp(int[] a, int x)
{
int m,i,val;
m=a.length-1;
val=a[m];
for(i=m-1;i>=0;i--)
val=val*x+a[i];
return val;
}
7.3.4 Calculul derivatelor unui polinom
Fie
b(X) = b
n
X
n
+b
n1
X
n1
+... +b
1
X +b
0
derivata de ordinul 1 a polinomului
a(X) = a
m
X
m
+a
m1
X
m1
+... +a
1
X +a
0
.
7.3. OPERATII CU POLINOAME 99
Dar
a

(X) = m a
m
X
m1
+ (m1) a
m1
X
m2
+... + 2 a
2
X +a
1
.
Rezulta ca
n = m1
si
b
i
= (i + 1) a
i+1
pentru 0 i n.
static int[] derivp(int[] a)
{
int m,n,i;
int[] b;
m=a.length-1;
n=m-1;
b=new int[n+1];
for(i=0;i<=n;i++)
b[i]=(i+1)*a[i+1];
return b;
}
Pentru calculul valorii v = a

(x) a derivatei polinomului a n x este sucient


apelul
v=valp(derivp(a),x);.
Daca vrem sa calcul am derivata de ordinul k 0 a polinomului a, atunci
static int[] derivpk(int[] a,int k)
{
int i;
int[] b;
m=a.length-1;
b=new int[m+1];
for(i=0;i<=n;i++)
b[i]=a[i];
for(i=1;i<=k;i++)
b=derivp(b);
return b;
}
Pentru calculul valorii v = a
(k)
(x) a derivatei de ordinul k a polinomului a
n x este sucient apelul
v=valp(derivpk(a,k),x);.
100 CAPITOLUL 7. ALGORITMI ELEMENTARI
7.4 Operat ii cu mult imi
O mult ime A se poate memora ntr-un vector a, ale carui elemente sunt
distincte. Folosind vectorii putem descrie operat iile cu mult imi.
7.4.1 Apartenent a la mult ime
Testul de apartenent a a unui element x la o multime A, este prezentat n
algoritmul urm ator:
static boolean apartine(int[] a, int x)
{
int i,n=a.length;
boolean ap=false;
for(i=0;i<n;i++)
if(a[i]==x) {ap=true; break;}
return ap;
}
7.4.2 Diferent a a doua mult imi
Diferent a a dou a mult imi este data de mult imea
C = AB = x[x A, x / B
Not am card A = m.
static int[] diferenta(int[] a, int[] b)
{
int i, j=0, m=a.length;
int[] c=new int[m];
for(i=0;i<m;i++)
if(!apartine(b,a[i]) c[j++]=a[i];
if(j==m) return c;
else
{
int[] cc=new int[j];
for(i=0;i<j;i++) cc[i]=c[i];
return cc;
}
}
7.4. OPERATII CU MULT IMI 101
7.4.3 Reuniunea si intersect ia a doua mult imi
Reuniunea a dou a mult imi este multimea:
C = A B = A (B A).
Introducem n C toate elementele lui A si apoi elementele lui B A.
static int[] reuniune(int[] a, int[] b)
{
int i, j, m=a.length, n=b.length;
int[] c=new int[m+n];
for(i=0;i<m;i++) c[i]=a[i];
j=m;
for(i=0;i<n;i++) if(!apartine(a,b[i]) c[j++]=b[i];
if(j==m+n) return c;
else
{
int[] cc=new int[j];
for(i=0;i<j;i++) cc[i]=c[i];
return cc;
}
}
Intersect ia a dou a mult imi este multimea:
C = A B = x[x A si x B
static int[] reuniune(int[] a, int[] b)
{
int i, j, m=a.length;
int[] c=new int[m];
j=0;
for(i=0;i<m;i++) if(apartine(b,a[i]) c[j++]=a[i];
if(j==m) return c;
else
{
int[] cc=new int[j];
for(i=0;i<j;i++) cc[i]=c[i];
return cc;
}
}
7.4.4 Produsul cartezian a doua mult imi
102 CAPITOLUL 7. ALGORITMI ELEMENTARI
Produs cartezian a doua multimi este multimea:
AB = (x, y)[x A si y B
Putem stoca produsul cartezian sub forma unei matrice C cu dou a linii si
m n coloane. Fiecare coloan a a matricei cont ine cate un element al produsului
cartezian.
static int[][] prodc(int[] a, int[] b)
{
int i, j, k, m=a.length, n=b.length;
int[][] c=new int[2][m*n];
k=0;
for(i=0;i<m;i++)
for(j=0;j<n;j++)
{
c[0][k]=a[i];
c[1][k]=b[j];
k++;
}
return c;
}
De exemplu, pentru A = 1, 2, 3, 4 si B = 1, 2, 3, matricea C este
0 1 2 3 4 5 6 7 8 9 10 11
linia 0 1 1 1 2 2 2 3 3 3 4 4 4
linia 1 1 2 3 1 2 3 1 2 3 1 2 3
7.4.5 Generarea submult imilor unei mult imi
Generarea submult imilor unei multimi A = a
1
, a
2
, ..., a
n
, este identica cu
generarea submult imilor mult imii de indici 1, 2, ..., n.
O submult ime se poate memora sub forma unui vector cu n componente,
unde ecare component a poate avea valori 0 sau 1. Componenta i are valoarea 1
daca elementul a
i
apart ine submult imii si 0 n caz contrar. O astfel de reprezentare
se numeste reprezentare prin vector caracteristic.
Generarea tuturor submult imilor nseamna generarea tuturor combinat iilor
de 0 si 1 care pot ret inute de vectorul caracteristic V , adic a a tuturor numerelor
n baza 2 care se pot reprezenta folosind n cifre.
Pentru a genera adunarea n binar, t inem cont ca trecerea de la un ordin la
urm atorul se face cand se obt ine suma egala cu 2, adic a 1 + 1 = (10)
2
.
De exemplu, pentru n = 4, vom folosi un vector v
pozit ia 1 2 3 4
valoarea
7.4. OPERATII CU MULT IMI 103
init ial 0 0 0 0 si adun am 1
obt inem 0 0 0 1 si adun am 1
obt inem 0 0 0 2 care nu este permis, si trecem la ordinul urm ator
obt inem 0 0 1 0 si adun am 1
obt inem 0 0 1 1 si adun am 1
obt inem 0 0 1 2 care nu este permis, si trecem la ordinul urm ator
obt inem 0 0 2 0 care nu este permis, si trecem la ordinul urm ator
obt inem 0 1 0 0 si asa mai departe
obt inem p an a cand
obt inem 1 1 1 1
Aceste rezultate se pot ret ine ntr-o matrice cu n linii si 2
n
coloane.
0 1 2 3 4 5 6 7 8 9 A B C D E F
a
1
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0
a
2
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 1
a
3
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 2
a
4
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 3
Ultima coloan a cont ine num arul liniei din matrice. Coloana 0 reprezint a
mult imea vida, coloana F reprezint a ntreaga mult ime, si, de exemplu, coloana 5
reprezint a submultimea a
2
, a
4
iar coloana 7 reprezint a submultimea a
2
, a
3
, a
4
.
static int[][] submultimi(int n)
{
int i, j, nc=1;
int[] v=new int[n+1];
int[][] c;
for(i=1;i<=n;i++) nc*=2;
c=new int[n][nc];
for(i=1;i<=n;i++) v[i]=0;
j=0;
while(j<nc)
{
v[n]=v[n]+1;
i=n;
while(v[i]>1) { v[i]=v[i]-2; v[i-1]=v[i-1]+1; i--; }
for(i=1;i<=n;i++) c[j][i-1]=v[i];
j++;
}
return c;
}
104 CAPITOLUL 7. ALGORITMI ELEMENTARI
7.5 Operat ii cu numere ntregi mari
Operat iile aritmetice sunt denite numai pentru numere reprezentate pe 16,
32 sau 64 bit i. Daca numerele sunt mai mari, operat iile trebuie implementate de
utilizator.
7.5.1 Adunarea si scaderea
Adunarea si scaderea sunt directe: aplic and metodele din scoala elementara.
static int[] suma(int[] x, int[] y)
{
int nx=x.length;
int ny=y.length;
int nz;
if(nx>ny)
nz=nx+1;
else
nz=ny+1;
int[] z=new int[nz];
int t,s,i;
t=0;
for (i=0;i<=nz-1;i++)
{
s=t;
if(i<=nx-1)
s=s+x[i];
if(i<=ny-1)
s=s+y[i];
z[i]=s%10;
t=s/10;
}
if(z[nz-1]!=0)
return z;
else
{
int[] zz=new int[nz-1];
for (i=0;i<=nz-2;i++) zz[i]=z[i];
return zz;
}
}
7.5. OPERATII CU NUMERE

INTREGI MARI 105
7.5.2 Inmult irea si mpartirea
Metoda nv at ata n soal a este corecta.
static int[] produs(int[]x,int[]y)
{
int nx=x.length;
int ny=y.length;
int nz=nx+ny;
int[] z=new int[nz];
int[] [] a=new int[ny][nx+ny];
int i,j;
int t,s;
for(j=0;j<=ny-1;j++)
{
t=0;
for(i=0;i<=nx-1;i++)
{
s=t+y[j]*x[i];
a[j][i+j]=s%10;
t=s/10;
}
a[j][i+j]=t;
}
t=0;
for(j=0;j<=nz-1;j++)
{
s=0;
for(i=0;i<=ny-1;i++)
s=s+a[i][j];
s=s+t;
z[j]=s%10;
t=s/10;
}
if(z[nz-1]!=0)
return z;
else
{
int[] zz=new int [nz-1];
for(j=0;j<=nz-2;j++)
zz[j]=z[j];
return zz;
}
}
106 CAPITOLUL 7. ALGORITMI ELEMENTARI
7.5.3 Puterea
Presupunem c a vrem sa calcul am x
n
. Cum facem acest lucru? Este evident
ca urm atoarea secvent a funct ioneaza:
for (p = 1, i = 0; i < n; i++) p *= x;
Presupun and c a toate nmult irile sunt efectuate ntr-o unitate de timp, acest
algoritm are complexitatea O(n). Totusi, putem s a facem acest lucru mai repede!
Presupun and, pentru nceput, c a n = 2
k
, urm atorul algoritm este corect:
for (p = x, i = 1; i < n; i *= 2) p *= p;
Aici num arul de treceri prin ciclu este egal cu k = log
2
n.
Acum, sa consider am cazul general. Presupunem c a n are expresia binara
(b
k
, b
k1
, ..., b
1
, b
0
). Atunci putem scrie
n =
k

i=0,bi=1
2
i
.
Deci,
x
n
=
k

i=0,bi=1
x
2
i
.
int exponent_1(int x, int n)
{
int c, z;
for (c = x, z = 1; n != 0; n = n / 2)
{
if (n & 1) /* n este impar */
z *= c;
c *= c;
}
return z;
}
int exponent_2(int x, int n)
{
if (n == 0)
return 1;
if (n & 1) /* n este impar */
return x * exponent_2(x, n - 1);
return exponent_2(x, n / 2) * exponent_2(x, n / 2);
}
7.6. OPERATII CU MATRICE 107
int exponent_3(int x, int n)
{
int y;
if (n == 0)
return 1;
if (n & 1) /* n este impar */
return x * exponent_3(x, n - 1);
y = exponent_3(x, n / 2);
return y * y;
}
7.6 Operat ii cu matrice
7.6.1

Inmult irea
O funct ie scrisa n C/C++:
void matrix_product(int** A, int** B, int** C)
{
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
{
C[i][j] = 0;
for (k = 0; k < n; k++)
C[i][j] += A[i][k] * B[k][j];
}
}
7.6.2 Inversa unei matrice
O posibilitate este cea din scoal a. Aceasta presupune calculul unor determinant i.
Determinantul det(A) se deneste recursiv astfel:
det(A) =
n1

i=0
(1)
i+j
a
i,j
det(A
i,j
).
unde a
i,j
este element al matricei iar A
i,j
este submatricea obt inut a prin eliminarea
liniei i si a coloanei j.
108 CAPITOLUL 7. ALGORITMI ELEMENTARI
int determinant(int n, int[][] a)
{
if (n == 1)
return a[0][0];
int det = 0;
int sign = 1;
int[][] b = new int[n - 1][n - 1];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < i; j++)
for (int k = 1; k < n; k++)
b[j][k - 1] = a[j][k];
for (int j = i + 1; j < n; j++)
for (int k = 1; k < n; k++)
b[j - 1][k - 1] = a[j][k];
det += sign * a[i][0] * determinant(n - 1, b);
sign *= -1;
}
}
Folosind determinant i, inversa matricei se poate calcula folosind regula lui
Cramer. Presupunem c a A este inversabila si e B = (b
i,j
) matricea denit a prin
b
i,j
= (1)
i+j
det(A
i,j
)/ det(A).
Atunci A
1
= B
T
, unde B
T
este transpusa matricei B.
Capitolul 8
Algoritmi combinatoriali
8.1 Principiul includerii si al excluderii si aplicat ii
8.1.1 Principiul includerii si al excluderii
Fie A si B dou a mult imi nite. Notam prin [A[ cardinalul mult imii A. Se
deduce usor ca:
[A B[ = [A[ +[B[ [A B[.
Fie A o mult ime nita si A
1
, A
2
, ..., A
n
submult imi ale sale. Atunci num arul
elementelor lui A care nu apar n nici una din submult imile A
i
(i = 1, 2, ..., n) este
egal cu:
[A[
n

i=1
[A
i
[+

1i<jn
[A
i
A
j
[

1i<j<kn
[A
i
A
j
A
k
[+...+(1)
n
[A
1
A
2
...A
n
[
Se pot demonstra prin induct ie matematica urm atoarele formule:
[
n
_
i=1
A
i
[ =
n

i=1
[A
i
[

1i<jn
[A
i
A
j
[+

1i<j<kn
[A
i
A
j
A
j
[...+(1)
n+1
[
n

i=1
A
i
[
[
n

i=1
A
i
[ =
n

i=1
[A
i
[

1i<jn
[A
i
A
j
[+

1i<j<kn
[A
i
A
j
A
j
[...+(1)
n+1
[
n
_
i=1
A
i
[
109
110 CAPITOLUL 8. ALGORITMI COMBINATORIALI
8.1.2 Determinarea funct iei lui Euler
Funct ia (n) a lui Euler ne d a num arul numerelor naturale mai mici ca n si
prime cu n.
Num arul n poate descompus n factori primi sub forma:
n = p
1
1
p
2
2
...p
m
m
Not am cu A
i
mult imea numerelor naturale mai mici ca n care sunt multipli de p
i
.
Atunci avem:
[A
i
[ =
n
p
i
, [A
i
A
j
[ =
n
p
i
p
j
, [A
i
A
j
A
k
[ =
n
p
i
p
j
p
k
, ...
Rezulta:
(n) = n
m

i=1
n
p
i
+

1i<jm
n
p
i
p
j

1i<j<km
n
p
i
p
j
p
k
+... + (1)
m
n
p
1
p
2
...p
m
care este tocmai dezvoltarea produsului
(n) = n
_
1
1
p
1
__
1
1
p
2
_
...
_
1
1
p
m
_
class Euler
{
static long[] fact;
public static void main (String[]args)
{
long n=36L; // Long.MAX_VALUE=9.223.372.036.854.775.807;
long nrez=n;
long[] pfact=factori(n);
// afisv(fact);
// afisv(pfact);
int k,m=fact.length-1;
for(k=1;k<=m;k++) n/=fact[k];
for(k=1;k<=m;k++) n*=fact[k]-1;
System.out.println("f("+nrez+") = "+n);
}
static long[] factori(long nr)
{
long d, nrrez=nr;
int nfd=0; // nr. factori distincti
boolean gasit=false;
8.1. PRINCIPIUL INCLUDERII SI AL EXCLUDERII SI APLICAT II 111
while((nr!=1)&(nr%d==0)) { nr=nr/d; gasit=true; }
if(gasit) {nfd++;gasit=false;}
d=3;
while(nr!=1)
{
while((nr!=1)&(nr%d==0)) { nr=nr/d; gasit=true; }
if(gasit) {nfd++;gasit=false;}
d=d+2;
}
nr=nrrez;
fact=new long[nfd+1];
long[] pf=new long[nfd+1];
int pfc=0; // puterea factorului curent
nfd=0; // nr. factori distincti
gasit=false;
d=2;
while((nr!=1)&(nr%d==0)) { nr=nr/d; gasit=true; pfc++; }
if(gasit) {fact[++nfd]=d;pf[nfd]=pfc;gasit=false;pfc=0;}
d=3;
while(nr!=1)
{
while((nr!=1)&(nr%d==0)) { nr=nr/d; gasit=true; pfc++; }
if(gasit) {fact[++nfd]=d;pf[nfd]=pfc;gasit=false;pfc=0;}
d=d+2;
}
return pf;
}//descfact
static void afisv(long[] a)
{
for(int i=1;i<a.length;i++) System.out.print(a[i]+" ");
System.out.println();
}
}
8.1.3 Numarul funct iilor surjective
Se dau mult imile X = x
1
, x
2
, ..., x
m
si Y = y
1
, y
2
, ..., y
n
.
Fie S
m,n
num arul funct iilor surjective f : X Y .
Fie A = f[f : X Y (mult imea tuturor funct iilor denite pe X cu valori
n Y ) si A
i
= f[f : X Y, y
i
/ f(X) (mult imea funct iilor pentru care y
i
nu
este imaginea nici unui element din X).
112 CAPITOLUL 8. ALGORITMI COMBINATORIALI
Atunci
S
m,n
= [A[ [
n
_
i=1
A
i
[
Folosind principiul includerii si al excluderii, obt inem
S
m,n
= [A[
n

i=1
[A
i
[ +

1i<jn
[A
i
A
j
[ ... + (1)
n
[A
1
A
2
... A
n
[
Se poate observa usor ca [A[ = n
m
, [A
i
[ = (n 1)
m
, [A
i
A
j
[ = (n 2)
m
,
etc.
Din Y putem elimina k elemente n C
k
n
moduri, deci

1i1<i2<...<i
k
n
[
k

j=1
A
ij
[ = C
k
n
(n k)
m
Rezulta:
S
m,n
= n
m
C
1
n
(n 1)
m
+C
2
n
(n 2)
m
+... + (1)
n1
C
n1
n
Observat ii:
1. Deoarece A
1
A
2
... A
n
= si pentru c a nu poate exista o funct ie care
sa nu ia nici o valoare, ultimul termen lipseste.
2. Daca n = matunci num arul funct iilor surjective este egal cu cel al funct iilor
injective, deci S
m,n
= n! si se obt ine o formul a interesanta:
n! =
n1

k=0
(1)
k
C
k
n
(n k)
n
class Surjectii
{
public static void main (String[]args)
{
int m, n=5, k, s;
for(m=2;m<=10;m++)
{
s=0;
for(k=0;k<=n-1;k++)
s=s+comb(n,k)*putere(-1,k)*putere(n-k,m);
System.out.println(m+" : "+s);
}
System.out.println("GATA");
}
8.1. PRINCIPIUL INCLUDERII SI AL EXCLUDERII SI APLICAT II 113
static int putere (int a, int n)
{
int rez=1, k;
for(k=1;k<=n;k++) rez=rez*a;
return rez;
}
static int comb (int n, int k)
{
int rez, i, j, d;
int[] x=new int[k+1];
int[] y=new int[k+1];
for(i=1;i<=k;i++) x[i]=n-k+i;
for(j=1;j<=k;j++) y[j]=j;
for(j=2;j<=k;j++)
for(i=1;i<=k;i++)
{
d=cmmdc(y[j],x[i]);
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}
rez=1;
for(i=1;i<=k;i++) rez=rez*x[i];
return rez;
}
static int cmmdc (int a,int b)
{
int d,i,c,r;
if (a>b) {d=a;i=b;} else{d=b;i=a;}
while (i!=0) { c=d/i; r=d%i; d=i; i=r; }
return d;
}
}
8.1.4 Numarul permutarilor fara puncte xe
Fie X = 1, 2, ..., n. Daca p este o permutare a elementelor mult imii X,
spunem ca num arul i este un punct x al permutarii p, dac a p(i) = i (1 i n).
Se cere sa se determine numarul D(n) al permut arilor f ar a puncte xe, ale
mult imii X. S a not am cu A
i
mult imea celor (n1)! permut ari care admit un punct
x n i (dar nu obligatoriu numai acest punct x!). Folosind principiul includerii
114 CAPITOLUL 8. ALGORITMI COMBINATORIALI
si al excluderii, num arul permut arilor care admit cel put in un punct x este egal
cu:
[A
1
A
2
... A
n
[ =
n

i=1
[A
i
[

1i<jn
[A
i
A
j
[ +... + (1)
n1
[
n

i=1
A
i
[.
Dar
[A
i1
A
i2
... A
i
k
[ = (n k)!
deoarece o permutare din mult imea A
i1
A
i2
... A
i
k
are puncte xe n pozit iile
i
1
, i
2
, ..., i
k
, celelalte pozit ii cont in and o permutare a celor n k elemente r amase
(care pot avea sau nu puncte xe!). Cele k pozit ii i
1
, i
2
, ..., i
k
pot alese n C
k
n
moduri, deci
[A
1
A
2
... A
n
[ = C
1
n
(n 1)! C
2
n
(n 2)! +... + (1)
n1
C
n
n
.
Atunci
D(n) = n! [A
1
A
2
... A
n
[ =
= n! C
1
n
(n 1)! +C
2
n
(n 2)! +... + (1)
n
C
n
n
= n!
_
1
1
1!
+
1
2!

1
3!
+... +
(1)
n
n!
_
.
De aici rezulta ca
lim
n
D(n)
n!
= e
1
,
deci, pentru n mare, probabilitatea ca o permutare a n elemente, aleasa aleator,
sa nu aib a puncte xe, este de e
1
0.3678.
Se poate demonstra usor ca:
D(n + 1) = (n + 1)D(n) + (1)
n+1
D(n + 1) = n(D(n) +D(n 1)) .
class PermutariFixe
{
public static void main(String [] args)
{
long n=10,k,s=0L,xv,xn; // n=22 maxim pe long !
if((n&1)==1) xv=-1L; else xv=1L;
s=xv;
for(k=n;k>=3;k--) { xn=-k*xv; s+=xn; xv=xn; }
System.out.println("f("+n+") = "+s);
}
}
8.2. PRINCIPIUL CUTIEI LUI DIRICHLET SI APLICAT II 115
8.2 Principiul cutiei lui Dirichlet si aplicat ii
Acest principiu a fost formulat prima data de Dirichle (1805-1859).

In forma cea mai simpl a acest principiu se enunt a astfel:


Daca n obiecte trebuie mp art ite n mai put in de n mult imi, atunci
exist a cel put in o mult ime n care vor cel put in dou a obiecte.
Mai general, principiul lui Dirichlet se poate enunt a astfel:
Fiind date m obiecte, care trebuie mp art ite n n mult imi, si un numar
natural k astfel nc at m > kn, atunci, n cazul oric arei mp art iri, va
exista cel put in o mult ime cu cel put in k + 1 obiecte.
Pentru k = 1 se obt ine formularea anterioara.
Cu ajutorul funct iilor, principiul cutiei se poate formula astfel:
Fie A si B dou a mult imi nite cu [A[ > [B[ si funct ia f : A B.
Atunci, exista b B cu proprietatea ca [f
1
(b)[ 2. Daca not am
[A[ = n si [B[ = r atunci [f
1
(b)[
_
n
r
_
.
Demonstram ultima inegalitate. Dac a aceasta nu ar adev arat a, atunci
[f
1
(b)[ <
_
n
r
_
, b B.
Dar mult imea B are r elemente, deci
n =

bB
[f
1
(b)[ < r
n
r
= n
ceea ce este o contradict ie.
8.2.1 Problema subsecvent ei
Se d a un sir nit a
1
, a
2
, ..., a
n
de numerentregi. Exista o subsecvent a a
i
, a
i+1
, ..., a
j
cu proprietatea ca a
i
+a
i+1
+... +a
j
este un multiplu de n.
S a consider am urm atoarele sume:
s
1
= a
1
,
s
2
= a
1
+a
2
,
...
s
n
= a
1
+a
2
+... +a
n
.
Daca exista un k astfel s
k
este multiplu de n atunci i = 1 si j = k.
116 CAPITOLUL 8. ALGORITMI COMBINATORIALI
Daca nici o suma part ial a s
k
nu este multiplu de n, atunci resturile mp art irii
acestor sume part iale la n nu pot dec at n mult imea 1, 2, ..., n 1. Pentru c a
avem n sume part iale si numai n 1 resturi, nseamna ca exista cel put in dou a
sume part iale (s
k1
si s
k2
, unde k
1
< k
2
) cu acelasi rest. Atunci subsecvent a cautat a
se obt ine lu and i = k
1
+ 1 si j = k
2
.
8.2.2 Problema subsirurilor strict monotone
Se d a sirul de numere reale distincte a
1
, a
2
, ..., a
mn+1
. Atunci, sirul cont ine
un subsir crescator de m+ 1 elemente:
a
i1
< a
i2
< ... < a
im+1
unde 1 i
1
< i
2
< ... < i
m+1
mn + 1,
sau un subsir descrescator de n + 1 elemente
a
j1
< a
j2
< ... < a
jn+1
unde 1 j
1
< j
2
< ... < j
n+1
mn + 1,
sau ambele tipuri de subsiruri.
Fiec arui element al sirului i asociem perechea de numere naturale (x
i
, y
i
)
unde x
i
este lungimea maxima a subsirurilor cresc atoare care ncep cu a
i
iar y
i
este lungimea maxima a subsirurilor descrescatoare care ncep n a
i
.
Presupunem c a armat ia problemei nu este adevarat a, adic a: pentru toate
numerele naturale x
i
si y
i
avem 1 x
i
m si 1 y
i
n. Atunci perechile de
numere (x
i
, y
i
) pot avea mn elemente distincte.
Deoarece sirul are mn+1 termeni, exista un a
i
si un a
j
pentru care perechile
de numere (x
i
, y
i
) si (x
j
, y
j
) sunt identice (x
i
= x
j
, y
i
= y
j
), dar acest lucru este
imposibil (cei doi termeni a
i
si a
j
ar trebui s a coincid a), ceea ce este o contradit ie.
Deci exista un subsir crescator cu m + 1 termeni sau un subsir descrescator
cu n + 1 termeni.
8.3 Numere remarcabile
8.3.1 Numerele lui Fibonacci
Numerele lui Fibonacci se pot deni recursiv prin:
F
0
= 0, F
1
= 1, F
n
= F
n1
+F
n2
pentru n 2. (8.3.1)
Primele numere Fibonacci sunt:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, ...
8.3. NUMERE REMARCABILE 117
Se poate ar ata ca
F
n
=
1
2
n

5
__
1 +

5
_
n

_
1

5
_
n
_
.
Numerele lui Fibonacci satisfac multe identit at i interesante, ca de exemplu:
_
1 1
1 0
_
n
=
_
F
n+1
F
n
F
n
F
n1
_
(8.3.2)
F
n+1
F
n1
F
2
n
= (1)
n
(8.3.3)
F
n+m
= F
m
F
n+1
+F
m1
F
n
(8.3.4)
F
nk
= multiplu de F
k
(8.3.5)
(8.3.6)
si
F
2
+ F
4
+... +F
2n
= F
2n+1
1 (8.3.7)
F
1
+F
3
+... +F
2n1
= F
2n
(8.3.8)
F
2
1
+F
2
2
+... +F
2
n
= F
n
F
n+1
(8.3.9)
F
1
F
2
+F
2
F
3
+... +F
2n1
F
2n
= F
2
2n
(8.3.10)
F
1
F
2
+F
2
F
3
+... +F
2n
F
2n+1
= F
2
2n+1
1 (8.3.11)
Teorema 2 Orice num ar natural n se poate descompune ntr-o sum a de numere
Fibonacci. Dac a nu se folosesc n descompunere numerele F
0
si F
1
si nici dou a
numere Fibonacci consecutive, atunci aceast a descompunere este unic a abstract ie
f acand de ordinea termenilor.
Folosind aceasta descompunere, numerele naturale pot reprezentate aseman ator
reprezentarii n baza 2. De exemplu
19 = 1 13 + 0 8 + 1 5 + 0 3 + 0 2 + 1 1 = (101001)
F

In aceasta scriere nu pot exista dou a cifre 1 al aturate.


import java.io.*;
class DescFibo
{
static int n=92;
static long[] f=new long[n+1];
public static void main (String[]args) throws IOException
{
int iy, k, nrt=0;
long x,y; // x=1234567890123456789L; cel mult!
118 CAPITOLUL 8. ALGORITMI COMBINATORIALI
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
System.out.print("x = ");
x=Long.parseLong(br.readLine());
f[0]=0;
f[1]=1;
f[2]=1;
for(k=3;k<=n;k++) f[k]=f[k-1]+f[k-2];
for(k=0;k<=n;k++) System.out.println(k+" : "+f[k]);
System.out.println(" "+Long.MAX_VALUE+" = Long.MAX_VALUE");
System.out.println(" x = "+x);
while(x>0)
{
iy=maxFibo(x);
y=f[iy];
nrt++;
System.out.println(nrt+" : "+x+" f["+iy+"] = "+y);
x=x-y;
}
}
static int maxFibo(long nr)
{
int k;
for(k=1;k<=n;k++) if (f[k]>nr) break;
return k-1;
}
}
8.3.2 Numerele lui Catalan
Numerele
C
n
=
1
n + 1
C
n
2n
se numesc numerele lui Catalan. Ele apar n multe probleme, ca de exemplu:
num arul arborilor binari, num arul de parantez ari corecte, numarul drumurilor sub
diagonal a care unesc punctele (0, 0) si (n, n) formate din segmente orizontale si
verticale, num arul secvent elor cu n bit i n care num arul cifrelor 1 nu dep aseste
num arul cifrelor 0 n nici o pozit ie plecand de la st anga spre dreapta, num arul
segmentelor care unesc 2n puncte n plan f ar a sa se intersecteze, numarul sirurilor
(x
1
, x
2
, ..., x
2n
) n care x
i
1, 1 si x
1
+ x
2
+ ... + x
2n
= 0 cu proprietatea
x
1
+ x
2
+ ... + x
i
0 pentru orice i = 1, 2, ..., 2n 1, num arul modurilor de a
triangulariza un poligon, si multe altele.
8.3. NUMERE REMARCABILE 119
Numerele lui Catalan sunt solut ie a urmatoarei ecuat ii de recurent a:
C
n+1
= C
0
C
n
+C
1
C
n1
+... +C
n
C
0
, pentru n 0 si C
0
= 1.
Numerele lui Catalan veric a si relat ia:
C
n+1
=
4n + 2
n + 2
C
n
O implementare cu numere mari este:
class Catalan
{
public static void main (String[]args)
{
int n;
int[] x;
for(n=1;n<=10;n++)
{
x=Catalan(n);
System.out.print(n+" : ");
afisv(x);
}
}
static int[] inm(int[]x,int[]y)
{
int i, j, t, n=x.length, m=y.length;
int[][]a=new int[m][n+m];
int[]z=new int[m+n];
for(j=0;j<m;j++)
{
t=0;
for(i=0;i<n;i++)
{
a[j][i+j]=y[j]*x[i]+t;
t=a[j][i+j]/10;
a[j][i+j]=a[j][i+j]%10;
}
a[j][i+j]=t;
}
t=0;
for(j=0;j<m+n;j++)
{
z[j]=t;
for(i=0;i<m;i++) z[j]=z[j]+a[i][j];
120 CAPITOLUL 8. ALGORITMI COMBINATORIALI
t=z[j]/10;
z[j]=z[j]%10;
}
if(z[m+n-1]!= 0) return z;
else
{
int[]zz=new int[m+n-1];
for(i=0;i<=m+n-2;i++) zz[i]=z[i];
return zz;
}
}
static void afisv(int[]x)
{
int i;
for(i=x.length-1;i>=0;i--) System.out.print(x[i]);
System.out.print(" *** "+x.length);
System.out.println();
}
static int[] nrv(int nr)
{
int nrrez=nr;
int nc=0;
while(nr!=0) { nc++; nr=nr/10; }
int[]x=new int [nc];
nr=nrrez;
nc=0;
while(nr!=0) { x[nc]=nr%10; nc++; nr=nr/10; }
return x;
}
static int[] Catalan(int n)
{
int[] rez;
int i, j, d;
int[] x=new int[n+1];
int[] y=new int[n+1];
for(i=2;i<=n;i++) x[i]=n+i;
for(j=2;j<=n;j++) y[j]=j;
for(j=2;j<=n;j++)
for(i=2;i<=n;i++)
{
d=cmmdc(y[j],x[i]);
8.4. PROBLEME REZOLVATE 121
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}
rez=nrv(1);
for(i=2;i<=n;i++) rez=inm(rez,nrv(x[i]));
return rez;
}
static int cmmdc (int a,int b)
{
int d,i,c,r;
if (a>b) {d=a;i=b;} else{d=b;i=a;}
while (i!=0) { c=d/i; r=d%i; d=i; i=r; }
return d;
}
}
8.4 Probleme rezolvate
1. S a se determine numarul arborilor binari cu n v arfuri.
Rezolvare: Fie b(n) num arul arborilor binari cu n v arfuri. Prin convent ie
b
0
= 1. Prin desene b
1
= 1, b
2
= 2, b
3
= 5, si: dac a x am r ad acina arborelui,
ne mai raman n 1 v arfuri care pot ap area n subarborele stang sau drept; dac a
n subarborele stang sunt k v arfuri, n subarborele drept trebuie sa e n 1 k
v arfuri; cu acesti subarbori se pot forma n total b
k
b
n1k
arbori; adun and aceste
valori pentru k = 0, 1, ..., n 1 vom obt ine valoarea lui b
n
. Deci, pentru n 1
b
n
= b
0
b
n1
+b
1
b
n2
+... +b
n1
b
0
=
n1

k=0
b
k
b
n1k
Se obt ine
b
n
=
1
n + 1
C
n
2n
2. Care este num arul permut arilor a n obiecte cu p puncte xe?
Rezolvare: Deoarece cele p puncte xe pot alese n C
p
n
moduri, si cele np
puncte ramase nu mai sunt puncte xe pentru permutare, rezulta ca num arul
permut arilor cu p puncte xe este egal cu
C
p
n
D(n p)
deoarece pentru ecare alegere a celor p puncte xe exista D(np) permut ari ale
obiectelor r amase, far a puncte xe.
122 CAPITOLUL 8. ALGORITMI COMBINATORIALI
3. Fie X = 1, 2, ..., n. Daca E(n) reprezint a num arul permut arilor pare
1
ale mult imii X f ar a puncte xe, atunci
E(n) =
1
2
_
D(n) + (1)
n1
(n 1)
_
.
Rezolvare: Fie A
i
mult imea permutarilor pare p astfel nc at p(i) = i. Deoarece
num arul permut arilor pare este
1
2
n!, rezult a ca
E(n) =
1
2
n! [A
1
... A
n
[ =
=
1
2
n! C
1
n
(n 1)! +C
2
n
(n 2)! +... + (1)
n
C
n
n
.
T in and cont c a
[A
i1
A
i2
... A
i
k
[ = (n k)!
rezult a formula ceruta.
1
Daca i < j, si n permutare i apare dupa j, spunem ca avem o inversiune. O permutare este
par a daca are un numar par de inversiuni
Capitolul 9
Algoritmi de cautare
9.1 Problema cautarii
Problema caut arii este: se da un vector a cu n elemente si o valoare x de
acelasi tip cu elementele din a. S a se determine p astfel nc at x = a[p] sau 1 daca
nu exist a un element cu valoarea v n a.
O tabel a cu campuri care nu sunt de acelasi tip se poate organiza cu ajutorul
vectorilor daca num arul de coloane este sucient de mic. De exemplu, o tabela cu
trei informat ii: num ar curent, nume, telefon poate organizat a cu ajutorul a doi
vectori (nume si telefon) iar num arul curent este indicele din vector.
9.2 Cautarea secvent iala
static int cauta (String x) {
for (int i = 0; i < N; ++i)
if (x.equals(nume[i])) return telefon[i];
return 1;
}
se poate scrie si sub forma:
static int cauta (String x) {
int i = 0;
while (i < N && !x.equals(nume[i])) ++i;
if (i < N) return telefon[i];
else return 1;
}
123
124 CAPITOLUL 9. ALGORITMI DE C

AUTARE
O alt a posibilitate este de a pune o santinel a n cap atul tabelei.
static int cauta (String x) {
int i = 0;
nume[N] = x; telefon[N] = 1;
while (! x.equals(nume[i])) ++i;
return tel[i];
}
Scrierea procedurii de c autare ntr-un tabel de nume este n acest caz mai
ecienta, pentru c a nu se face decat un singur test n plus aici (n loc de dou a teste).
Cautarea secvent ial a se mai numeste si cautare linear a, pentru c a se executa N/2
operat ii n medie, si N operat ii n cazul cel mai defavorabil.

Intr-un tablou cu
10.000 elemente, cautarea executa 5.000 operat ii n medie, ceea ce nseamna un
consum de timp de aproximativ 0.005 ms (milisecunde).
Iat a un program complet care utilizeaza cautarea linear a ntr-o tabel a.
class Tabela {
final static int N = 6;
static String nume[] = new String[N+1];
static int telefon[] = new int[N+1];
static void initializare() {
nume[0] = "Paul"; telefon[0] = 2811;
nume[1] = "Robert"; telefon[1] = 4501;
nume[2] = "Laura"; telefon[2] = 2701;
nume[3] = "Ana"; telefon[3] = 2702;
nume[4] = "Tudor"; telefon[4] = 2805;
nume[5] = "Marius"; telefon[5] = 2806;
}
static int cauta(String x) {
for (int i = 0; i < N; ++i)
if (x.equals(nume[i]))
return tel[i];
return 1;
}
public static void main (String args[]) {
initializare();
if (args.length == 1)
System.out.println(cauta(args[0]));
}
}
9.3. C

AUTARE BINAR

A 125
Cel mai simplu algoritm care rezolv a aceasta problem a este cautarea liniar a,
care testeaz a elementele vectorului unul dupa altul, ncepand cu primul.
p=-1;
for(i=0;i<n;i++)
if(x==a[i]) { p=i; break; }
S a analizam complexitatea acestui algoritm pe cazul cel mai defavorabil, acela
n care v nu se gaseste n a.

In acest caz se va face o parcurgere completa a lui a.
Consider and ca operat ie elementara testul v == a[i], num arul de astfel de operat ii
elementare efectuate va egal cu numarul de elemente al lui a, adic a n. Deci
complexitatea algoritmului pentru cazul cel mai defavorabil este O(n).
9.3 Cautare binara
O alt a tehnic a de cautare n tabele este cautarea binar a. Presupunem c a
tabela de nume este sortata n ordine alfabetic a (cum este cartea de telefoane).

In loc de a cauta secvent ial, se compara cheia de cautare cu numele care se af a


la mijlocul tabelei de nume. Dac a acesta este acelasi, se returneaza num arul de
telefon din mijloc, altfel se rencepe c autarea n prima jum atate (sau n a doua)
daca numele cautat este mai mic (respectiv, mai mare) decat numele din mijlocul
tabelei.
static int cautareBinara(String x) {
int i, s, d, cmp;
s = 0; d = N1;
do {
i = (s + d) / 2;
cmp = x.compareTo(nume[i]);
if (cmp == 0)
return telefon[i];
if (cmp < 0)
d = i 1;
else
s = i + 1;
} while (s <= d);
return 1;
}
Num arul C
N
de comparat ii efectuate pentru o tabel a de dimensiune N este
C
N
= 1 +C
N/2
, unde C
0
= 1. Deci C
N
log
2
N.
De acum naninte, log
2
N va scris mai simplu log N.
Daca tabela are 10.000 elemente, atunci C
N
14. Acesta reprezinta un
beneciu considerabil n raport cu 5.000 de operat ii necesare la cautarea linear a.
126 CAPITOLUL 9. ALGORITMI DE C

AUTARE
Desigur cautarea secvent ial a este foarte usor de programat, si ea se poate
folosi pentru tabele de dimensiuni mici. Pentru tabele de dimensiuni mari, cautarea
binar a este mult mai interesanta.
Se poate obt ine un timp sub-logaritmic dac a se cunoaste distribut ia obiectelor.
De exemplu, n cartea de telefon, sau n dict ionar, se stie apriori ca un nume care
ncepe cu litera V se aa catre sf arsit. Presupunand distribut ia uniforma, putem
face o regula de trei simpl a pentru g asirea indicelui elementului de referint a
pentru comparare, n loc s a alegem mijlocul, si sa urm am restul algoritmului de
cautare binar a. Aceast a metoda se numeste cautare prin interpolare. Timpul de
cautare este O(log log N), ceea ce nseamna cam 4 operat ii pentru o tabel a de
10.000 elemente si 5 operat ii pentru 10
9
elemente n tabel a. Este spectaculos!
O implementare iterativ a a caut arii binare ntr-un vector ordonat (cresc ator
sau descrescator) este:
int st, dr, m;
boolean gasit;
st=0;
dr=n-1;
gasit=false;
while((st < dr) && !gasit)
{
m=(st+dr)/2;
if(am]==x)
gasit=true;
else if(a[m] > x)
dr=m-1;
else st=m+1;
}
if(gasit) p=m; else p=-1;
Algoritmul poate descris, foarte elegant, recursiv.
9.4 Inserare n tabela
La cautarea secvent ial a sau binar a nu am fost preocupat i de inserarea n
tabel a a unui nou element. Aceasta este destul de rar a n cazul cart ii de telefon
dar n alte aplicat ii, de exemplu lista utilizatorilor unui sistem informatic, este
frecvent utilizat a.
Vom vedea cum se realizeaza inserarea unui element ntr-o tabel a, n cazul
caut arii secvent iale si binare.
Pentru cazul secvent ial este sucient sa ad aug am la cap atul tabelei noul
element, daca are loc. Daca nu are loc, se apeleaz a o procedura error care aseaza
un mesaj de eroare.
9.5. DISPERSIA 127
void inserare(String x, int val) {
++n;
if (n >= N)
error ("Depasire tabela");
numem[n] = x;
telefon[n] = val;
}
Inserarea se face deci n timp constant, de ordinul O(1).

In cazul caut arii binare, trebuie ment inut tabelul ordonat. Pentru inserarea
unui element nou n tabel a, trebuie gasit a pozit ia sa printr-o c autare binar a (sau
secvent ial a), apoi se deplaseaza toate elementele din spatele ei spre dreapta cu o
pozit ie pentru a putea insera noul element n locul corect. Aceasta necesita log n+n
operet ii. Inserarea ntr-o tabel a ordonat a cu n elemente este deci de ordinul O(n).
9.5 Dispersia
O alt a metoda de cautare n tabele este dispersia. Se utilizeaz a o funct ie h
de grupare a cheilor (adesea siruri de caractere) ntr-un interval de numere ntregi.
Pentru o cheie x, h(x) este locul unde se af a x n tabel a. Totul este n ordine dac a
h este o aplicat ie injectiva. De asemenea, este bine ca funct ia aleasa sa permit a
evaluarea cu un num ar mic de operat ii. O astfel de funct ie este
h(x) =
_
x
1
B
m1
+x
2
B
m2
+... +x
m1
B +x
m
_
mod N.
De obicei se ia B = 128 sau B = 256 si se presupune c a dimensiunea tabelei
N este un numar prim. De ce? Pentru c a nmult irea puterilor lui 2 se poate face
foarte usor prin operat ii de deplasare pe bit i, numerele ind reprezentate n binar.

In general, la masinile (calculatoarele) moderne, aceste operat ii sunt net mai rapide
dec at nmult irea numerelor oarecare. C at despre alegerea lui N, aceasta se face
pentru a evita orice interferent a ntre ntre nmult irile prin B si mp art irile prin
N.

Intr-adev ar, dac a de exemplu B = N = 256, atunci h(x) = x(m) este funct ia h
care nu depinde dec at de ultimul caracter al lui x. Scopul este de a avea o funct ie h,
de dispersie, simplu de calculat si avand o bun a distribut ie pe intervalul [0, N1].
Calculul funct iei h se face prin funct ia h(x,m), unde m este lungimea sirului x,
static int h(String x){
int r = 0;
for (int i = 0; i < x.length(); ++i)
r = ((r * B) + x.charAt(i)) % N;
return r;
}
128 CAPITOLUL 9. ALGORITMI DE C

AUTARE
Deci funct ia h d a pentru toate cheile x o intrare posibil a n tabel a. Se poate
apoi verica dac a x = nume[h(x)]. Dac a da, c autarea este terminata. Daca nu,
nseamna ca tabela cont ine o alt a cheie astfel nc at h(x

) = h(x). Se spune atunci


ca exista o coliziune, si tabela trebuie sa gestioneze coliziunile. O metoda simpl a
este de a lista coliziunile ntr-un tabel col paralel cu tabelul nume. Tabela de
coliziuni d a o alt a intrare i n tabela de nume unde se poate g asi cheia cautat a.
Daca nu se gaseste valoarea x n aceasta nou a intrare i, se continu a cu intrarea i

dat a de i

= col[i]. Se continu a astfel cat timp col[i] ,= 1.


static int cauta(String x) {
for (int i = h(x); i != 1; i = col[i])
if (x.equals(nume[i]))
return telefon[i];
return 1;
}
Astfel, procedura de c autare consuma un timp mai mare sau egal ca lungimea
medie a claselor de echivalent a denite pe tabela de valorile h(x), adica de lungimea
medie a listei de coliziuni. Dac a funct ia de dispersie este perfect uniform a, nu apar
coliziuni si determin a toate elementele printr-o singur a comparat ie. Acest caz este
foarte put in probabil. Dac a num arul mediu de elemente av and aceeasi valoare de
dispersie este k = N/M, unde M este numarul claselor de echivalent a denite
de h, cautarea ia un timp de N/M. Dispersia nu face decat sa reduc a printr-un
factor constant timpul c aut arii secvent iale. Interesul fat a de dispersie este pentru
ca adesea este foarte ecace, si usor de programat.
Capitolul 10
Algoritmi elementari de
sortare
Tablourile sunt structuri de baz a n informatica. Un tablou reprezint a, n
funct ie de dimensiunile sale, un vector sau o matrice cu elemente de acelasi tip.
Un tablou permite accesul direct la un element, si noi vom utiliza intens aceast a
proprietate n algoritmii de sortare pe care i vom considera.
10.1 Introducere
Ce este sortarea? Presupunem ca se da un sir de N numere ntregi a
i
, si
se doreste aranjarea lor n ordine crescatoare, n sens larg. De exemplu, pentru
N = 10, sirul
18, 3, 10, 25, 9, 3, 11, 13, 23, 8
va deveni
3, 3, 8, 9, 10, 11, 13, 18, 23, 25.
Aceasta problem a este clasica n informatica si a fost studiat a n detaliu, de
exemplu, n [23].

In proctic a se nt alneste adesea aceasta problem a. De exemplu,
stabilirea clasamentului ntre student i, construirea unui dict ionar, etc. Trebuie
f acut a o distint ie ntre sortarea unui num ar mare de elemente si a unui num ar mic
de elemente.

In acest al doilea caz, metoda de sortare este put in important a.
Un algoritm amuzant consta n a vedea dac a setul de cart i de joc din m an a
este deja ordonat. Daca nu este, se da cu ele de p am ant si se rencepe. Dup a
un anumit timp, exist a riscul de a avea cart ile ordonate. Desigur, poate sa nu se
termine niciodata, pentru noi, aceasta sortare.
129
130 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE
O alt a tehnic a (discut and serios de data aceasta), frecvent utilizat a la un joc
de cart i, consta n a vedea dac a exista o transpozit ie de efectuat. Daca exista, se
face interschimbarea cart ilor de joc si se cauta o alt a transpozit ie. Procedeul se
repeta p an a cand nu mai exist a transpozit ii. Aceast a metoda funct ioneaza foarte
bine pentru o bun a distribut ie a cart ilor.
Este usor de intuit c a num arul obiectelor de sortat este important. Nu este
cazul sa se caute o metoda sosticata pentru a sorta 10 elemente. Cu alte cuvinte,
nu se trage cu tunul ntr-o musc a!
Exemplele tratate ntr-un curs sunt ntotdeauna de dimensiuni limitate, din
considerente pedagogice nu este posibil de a prezenta o sortare a mii de elemente.
10.2 Sortare prin select ie

In cele ce urmeaza, presupunem ca trebuie s a sort am un num ar de ntregi care


se gasesc ntr-un tablou (vector) a. Algoritmul de sortare cel mai simplu este prin
select ie. El const a n g asirea pozit iei n tablou a elementului cu valoarea cea mai
mic a, adic a ntregul m pentru care a
i
a
m
pentru orice i. Odat a gasita aceasta
pozit ie m, se schimba ntre ele elementele a
1
si a
m
.
Apoi se rencepe aceasta operat ie pentru sirul (a
2
, a
3
, ..., a
N
), tot la fel,
caut andu-se elementul cel mai mic din acest sir si interschimb andu-l cu a
2
. Si
asa mai departe pan a la un moment dat c and sirul va cont ine un singur element.
Cautarea celui mai mic element ntr-un tablou este un prim exercit iu de
programare. Determinarea pozit iei acestui element este foarte simpla, ea se poate
efectua cu ajutorul instruct iunilor urm atoare:
m = 0;
for (int j = 1; j < N; ++j)
if (a[j] < a[m])
m = i;
Schimbarea ntre ele a celor doua elemente necesita o variabil a temporara t
si se efectueaz a prin:
t = a[m]; a[m] = a[1]; a[1] = t;
Acest set de operat ii trebuie reluat prin nlocuirea lui 1 cu 2, apoi cu 3 si
asa mai departe pan a la N. Aceasta se realizeaza prin introducerea unei variabile
i care ia toate valorile ntre 1 si N.
Toate acestea sunt ar atate n programul care urmeaz a.
De aceasta dat a vom prezenta programul complet.
Procedurile de achizit ionare a datelor si de returnare a rezultatelor sunt
deasemenea prezentate.
Pentru alt i algoritmi, ne vom limita la descrierea efectiv a a sort arii.
10.2. SORTARE PRIN SELECTIE 131
class SortSelectie
{
final static int N = 10;
static int[] a = new int[N];
static void initializare()
{
int i;
for (i = 0; i < N; ++i)
a[i] = (int) (Math.random() * 128);
}
static void afisare()
{
int i;
for (i = 0; i < N; ++i)
System.out.print (a[i] + " ");
System.out.println();
}
static void sortSelectie()
{
int min, t;
int i, j;
for (i = 0; i < N 1; ++i)
{
min = i;
for (j = i+1; j < N; ++j)
if (a[j] < a[min])
min = j;
t = a[min];
a[min] = a[i];
a[i] = t;
}
}
public static void main (String args[])
{
initializare();
afisare();
sortSelectie();
afisare();
}
}
132 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE
Un exemplu de execut ii este:
0 1 2 3 4 5 6 7
7 12 4 30 3 4 23 14

i m

0 1 2 3 4 5 6 7
3 12 4 30 7 4 23 14

i m
0 1 2 3 4 5 6 7
3 12 4 30 7 4 23 14

i m

0 1 2 3 4 5 6 7
3 4 12 30 7 4 23 14

i m
0 1 2 3 4 5 6 7
3 4 12 30 7 4 23 14

i m

0 1 2 3 4 5 6 7
3 4 4 30 7 12 23 14

i m
0 1 2 3 4 5 6 7
3 4 4 30 7 12 23 14

i m

0 1 2 3 4 5 6 7
3 4 4 7 30 12 23 14

i m
0 1 2 3 4 5 6 7
3 4 4 7 30 12 23 14

i m

0 1 2 3 4 5 6 7
3 4 4 7 12 30 23 14

i m
0 1 2 3 4 5 6 7
3 4 4 7 12 30 23 14

i m

0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30

i m
0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30

im

0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30

im
0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30

0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30
Este usor de calculat numarul de operat ii necesare. La ecare iterat ie, se
pleaca de la elementul a
i
si se compara succesiv cu a
i+1
, a
i+2
, ..., a
N
. Se fac deci
N i comparat ii. Se ncepe cu i = 1 si se termin a cu i = N 1. Deci, se fac
(N 1) +(N 2) +... +2 +1 = N(N 1)/2 comparat ii si N 1 interschimb ari.
Sortarea prin select ie executa un num ar de comparat ii de ordinul N
2
.
O variant a a sort arii prin select ie este metoda bulelor. Principiul ei este de
a parcurge sirul (a
1
, a
2
, ..., a
N
) invers and toate perechile de elemente consecutive
(a
j
1, a
j
) neordonate. Dup a prima parcurgere, elementul maxim se va aa pe
pozit ia N. Se rencepe cu prexul (a
,
a
2
, ..., a
N1
), ..., (a
1
, a
2
).
Procedura corespunzatoare utilizeaza un indice i care marcheaz a sf arsitul
prexului n sortare, si un indice j care permite deplasarea catre marginea i.
10.2. SORTARE PRIN SELECTIE 133
Se poate calcula de asemenea foarte usor numarul de operat ii si se obt ine un
num ar de ordinul O(N
2
) comparat ii si, eventual interschimb ari (daca, de exemplu,
tabloul este init ial n ordine descrescatoare).
static void sortBule() {
int t;
for (int i = N1; i >= 0; i)
for (int j = 1; j <= i; ++j)
if (a[j1] > a[j]) {
t = a[j1]; a[j1] = a[j]; a[j] = t;
}
}
0 1 2 3 4 5 6 7
7 12 4 30 3 4 23 14

j i

0 1 2 3 4 5 6 7
7 4 12 30 3 4 23 14

j i
0 1 2 3 4 5 6 7
7 4 12 30 3 4 23 14

j i

0 1 2 3 4 5 6 7
7 4 12 3 30 4 23 14

j i
0 1 2 3 4 5 6 7
7 4 12 3 30 4 23 14

j i

0 1 2 3 4 5 6 7
7 4 12 3 4 30 23 14

j i
0 1 2 3 4 5 6 7
7 4 12 3 4 30 23 14

j i

0 1 2 3 4 5 6 7
7 4 12 3 4 23 30 14

j i
0 1 2 3 4 5 6 7
7 4 12 3 4 23 30 14

ji

0 1 2 3 4 5 6 7
7 4 12 3 4 23 14 30

ji
Dup a prima parcurgere 30 a ajuns pe locul s au (ultimul loc n vector).
0 1 2 3 4 5 6 7
7 4 12 3 4 23 14 30

j i

0 1 2 3 4 5 6 7
4 7 12 3 4 23 14 30

j i
0 1 2 3 4 5 6 7
4 7 12 3 4 23 14 30

j i

0 1 2 3 4 5 6 7
4 7 3 12 4 23 14 30

j i
134 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE
0 1 2 3 4 5 6 7
4 7 3 12 4 23 14 30

j i

0 1 2 3 4 5 6 7
4 7 3 4 12 23 14 30

j i
0 1 2 3 4 5 6 7
4 7 3 4 12 23 14 30

ji

0 1 2 3 4 5 6 7
4 7 3 4 12 14 23 30

ji
Dup a a doua parcurgere 23 a ajuns pe locul s au (penultimul loc n vector).
0 1 2 3 4 5 6 7
4 7 3 4 12 14 23 30

j i

0 1 2 3 4 5 6 7
4 3 7 4 12 14 23 30

j i
0 1 2 3 4 5 6 7
4 3 7 4 12 14 23 30

j i

0 1 2 3 4 5 6 7
4 3 4 7 12 14 23 30

j i
Dup a a treia parcurgere 14 a ajuns pe locul s au (de fapt era deja pe locul
sau de la nceputul acestei parcurgeri; s-au mai aranjat totusi cateva elemente!).
0 1 2 3 4 5 6 7
4 3 4 7 12 14 23 30

j i

0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30

j i
Dup a a patra parcurgere 12 a ajuns pe locul s au (de fapt era deja pe locul s au
de la nceputul acestei parcurgeri; oricum, la aceasta parcurgere s-au mai aranjat
cateva elemente!).
La urmatoarea parcurgere nu se efectueaza nici o interschimbare de elemente.
Vectorul este deja sortat, asa ca urm atoarele parcurgeri se fac, de asemenea, far a
sa se execute nici o interschimbare de elemente. O idee buna este introducerea unei
variabile care s a contorizeze numarul de interschimb ari din cadrul unei parcurgeri.
Daca nu s-a efectuat nici o interschimbare atunci vectorul este deja sortat asa ca
se poate ntrerupe execut ia urm atoarelor parcurgeri. Programul modicat este:
static void sortBule() {
int t, k;
for (int i = N1; i >= 0; i)
{
k=0;
for (int j = 1; j <= i; ++j)
if (a[j1] > a[j]) {t = a[j1]; a[j1] = a[j]; a[j] = t; k++;}
if(k==0) break;
}
}
10.3. SORTARE PRIN INSERT IE 135
10.3 Sortare prin insert ie
O metod a complet diferit a este sortarea prin insert ie. Aceasta este metoda
utilizat a pentru sortarea unui pachet de c art i de joc. Se ia prima carte, apoi a
doua si se aranjeaza n ordine crescatoare. Se ia a treia carte si se plaseaza pe
pozit ia corespunzatoare fat a de primele dou a cart i, si asa mai departe. Pentru
cazul general, sa presupunem c a primele i 1 cart i sun deja sortate crescator. Se
ia a i-a carte si se plaseaza pe pozit ia corespunzatoare relativ la primele i 1 cart i
deja sortate. Se continu a p an a la i = N.
10.3.1 Insert ie directa
Daca determinarea pozit iei corespunz atoare, n subsirul format de primele
i 1 elemente, se face secvent ial, atunci sortarea se numeste sortare prin insert ie
direct a. Procedura care realizeaz a aceastsortare este:
static void sortInsertie() {
int j, v;
for (int i = 1; i < N; ++i) {
v = a[i]; j = i;
while (j > 0 && a[j-1] > v) {
a[j] = a[j-1];
j;
}
a[j] = v;
}
}
Pentru plasarea elementului a
i
din vectorul nesortat (elementele de pe pozit iile
din stanga ind deja sortate) se parcurge vectorul spre st anga plec and de la pozit ia
i 1. Elementele vizitate se deplaseaza cu o pozit ie spre dreapta pentru a permite
plasarea elementului a
i
(a carui valoare a fost salvat a n variabila temporar a v) pe
pozit ia corespunzatoare. Procedura cont ine o mica eroare, daca a
i
este cel mai mic
element din tablou, caci va iesi din tablou spre st anga. Se poate remedia aceasta
situat ie plasand un element a
0
de valoare max int. Se spune c a a fost plasat a
o santinela la stanga tabloului a. Aceasta nu este ntotdeauna posibil, si atunci
trebuie adaugat un test asupra indicelui j n bucla while. Un exemplu numeric
este cel care urmeaza:
Inserarea lui 12 nu necesita deplas ari de elemente.
0 1 2 3 4 5 6 7
7 12 4 30 3 4 23 14

j i

0 1 2 3 4 5 6 7
4 7 12 30 3 4 23 14

j i
136 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE
Inserarea lui 30 nu necesita deplas ari de elemente.
0 1 2 3 4 5 6 7
4 7 12 30 3 4 23 14

j i

0 1 2 3 4 5 6 7
3 4 7 12 30 4 23 14

j i
0 1 2 3 4 5 6 7
3 4 7 12 30 4 23 14

j i

0 1 2 3 4 5 6 7
3 4 4 7 12 30 23 14

j i
0 1 2 3 4 5 6 7
3 4 4 7 12 30 23 14

j i

0 1 2 3 4 5 6 7
3 4 4 7 12 23 30 14

j i
0 1 2 3 4 5 6 7
3 4 4 7 12 23 30 14

j i

0 1 2 3 4 5 6 7
3 4 4 7 12 14 23 30

j i
Num arul de comparat ii pentru inserarea unui element n secvent a deja sortat a
este egal cu numarul de inversiuni plus 1.
Fie c
i
num arul de comparat ii. Atunci
c
i
= 1 + carda
j
[a
j
> a
i
, j < i
Pentru o permutare corespunz atoare unui sir de sortari, unde num arul de
inversiuni este inv(), num arul total de comparat ii pentru sortarea prin insert ie
este
C

=
N

i=2
c
i
= N 1 +inv().
Deci, num arul mediu de comparat ii este
C
N
=
1
N!

SN
C

= N 1 +
N(N 1)
4
=
N(N + 3)
4
1
unde S
n
reprezint a grupul permut arilor de n elemente.
Desi ordinul de crestere este tot N
2
, aceasta metoda de sortare este mai
ecienta decat sortarea prin select ie. Cea mai bun a metoda de sortare necesit a
nlog
2
n comparat ii.

In plus, ea are o proprietate foarte buna: num arul de operat ii depinde puternic
de ordinea init ial a din tablou.

In cazul n care tabloul este aproape ordonat, si drept


urmare exista put ine inversiuni si sunt necesare put ine operat ii, spre deosebire de
primele dou a metode de sortare.
Sortarea prin inserare este deci o metoda de sortare bun a dac a tabloul de
sortat are sansa de a aproape ordonat.
10.3. SORTARE PRIN INSERT IE 137
10.3.2 Insert ie binara
Metoda anterioar a se poate mbunat at i daca t inem cont de faptul c a secvent a
n care se face inserarea este deja ordonata, iar n loc sa se faca insert ia directa n
aceasta secvent a, cautarea pozit iei pe care se face inserarea se face prin cautare
binar a. Un program complet este:
import java.io.*;
class SortInsBinApl
{
static int[] a={3,8,5,4,9,1,6,4};
static void afiseaza()
{
int j;
for(j=0; j<a.length; j++)
System.out.print(a[j] + " ");
System.out.println();
}
static int pozitiaCautBin(int p, int u, int x)
{
int i=u+1;
while (p <= u)
{
i=(p+u)/2;
if (x>a[i])
p=i+1;
else if (x<a[i])
u=i-1;
else return i;
}
return p;
}
static void deplasare(int k, int i)
{
if (i != k)
{
int x=a[k];
for(int j=k; j>=i+1; j--) a[j]=a[j-1];
a[i]=x;
}
}
138 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE
static void sorteaza()
{
int N=a.length,i;
for(int k=1;k<=N-1;k++)
{
i=pozitiaCautBin(0,k-1,a[k]);
deplasare(k,i);
}
}
public static void main(String[] args)
{
afiseaza();
sorteaza();
afiseaza();
}
}
10.4 Sortare prin interschimbare
Aceasta metoda foloseste interschimbarea ca si caracteristica principal a a
metodei de sortare.

In cadrul acestei metode se compara si se interschimba perechi


adiacente de chei pana cand toate elementele sunt sortate.
static void interschimbare(int a[])
{
int x, i, n=a.length;
boolean schimb = true;
while (!schimb)
{
schimb=false;
for(i=0; i<n-1; i++)
if (a[i]>a[i+1])
{
x = a[i];
a[i] = a[i+1];
a[i+1] = x;
schimb=true;
}
}
}
10.5. SORTARE PRIN MICSORAREA INCREMENTULUI - SHELL 139
10.5 Sortare prin micsorarea incrementului - shell
Prezentam metoda pe urmatorul sir:
44, 55, 12, 42, 94, 18, 6, 67.
Se grupeaza elementele aate la 4 pozit ii distant a, si se sorteaza separat.
Acest proces este numit numit 4 sort. Rezult a sirul:
44, 18, 06, 42, 94, 55, 12, 67
Apoi se sorteaza elementele aate la 2 pozit ii distant a. Rezult a:
6, 18, 12, 42, 44, 55, 94, 97
Apoi se sorteaza sirul rezultat ntr-o singura trecere: 1 - sort
6, 12, 18, 42, 44, 55, 94, 97
Se observa urm atoarele:
un proces de sortare i sort combin a 2 grupuri sortate n procesul 2i sort
anterior
n exemplul anterior s-a folosit secventa de increment i 4, 2, 1 dar orice
secvent a, cu condit ia ca cea mai na sortare sa e 1 sort.

In cazul cel
mai defavorabil, n ultimul pas se face totul, dar cu multe comparat ii si
interschimb ari.
daca cei t incrementi sunt h
1
, h
2
, .. h
t
, h
t
= 1 si h
i+1
< h
i
, ecare h
i
-sort
se poate implementa ca si o sortate prin insertie directa.
void shell(int a[], int n)
{
static int h[] = {9, 5, 3, 1};
int m, x, i, j, k, n=a.length;
for (m=0; m<4; m++)
{
k = h[m];
/* sortare elemente aflate la distanta k in tablul a[] */
for (i=k; i<n; i++)
{
x = a[i];
for (j = i-k; (j>=0) && (a[j]>x); j-=k) a[j+k] = a[j];
a[j+k] = x;
}
}
}
140 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE
10.6 Sortare prin partitionare - quicksort
Se bazeaza pe metoda interschimb arii, ns a din nou, interschimbarea se face
pe distant e mai mari. Astfel, avand tabloul a[], se aplic a urm atorul algoritm:
1. se alege la nt amplare un element x al tabloului
2. se scaneaza tabloul a[] la st anga lui x p an a cand se gaseste un element a
i
> x
3. se scaneaza tabloul la dreapta lui x p an a cand se gaseste un element a
j
< x
4. se interschimb a a
i
cu a
j
5. se repeta pasii 2, 3, 4 p an a cand scan arile se vor nt alni pe undeva la mijlocul
tabloului.

In acel moment, tabloul a[] va partitionat n 2 astfel, la st anga
lui x se vor gasi elemente mai mici ca si x, la dreapta, elemente mai mari ca
si x. Dup a aceasta, se aplic a acelasi proces subsirurilor de la st anga si de la
dreapta lui x, p an a cand aceste subsiruri sunt sucient de mici (se reduc la
un singur element).
void quicksort(int a[])
{
int n=a.length;
int l=0, r=n-1;
int i=l, j=r;
int x, temp;
if (l<r)
{
x = a[(l+r)/2];
do
{
while (a[i]<x) i++;
while (a[j]>x) --j;
if (i<=j)
{
temp=a[i]; a[i]=a[j]; a[j]=temp;
j--;
i++;
}
} while (i<=j);
if (l<j) quicksort(a+l, j-l);
if (i<r) quicksort(a+i, r-i);
}
}
Aceasta metoda are complexitatea nlog n, n practic a (n medie)!
Capitolul 11
Liste
Scopul listelor este de a genera un ansamblu nit de elemente al c arui num ar
nu este xat apriori. Elementele acestui ansamblu pot numere ntregi sau reale,
siruri de caractere, obiecte informatice complexe, etc. Nu suntem acum interesat i
de elementele acestui ansamblu ci de operat iile care se efectueaz a asupra acestuia,
independent de natura elementelor sale.
Listele sunt obiecte dinamice, n sensul ca num arul elementelor variaza n
cursul execut iei programului, prin ad aug ari sau stergeri de elemente pe parcursul
prelucrarii. Mai precis, operat iile permise sunt:
testarea daca ansamblul este vid
ad augarea de elemente
vericarea dac a un element este n ansamblu
stergerea unui element
11.1 Liste liniare
Fiecare element al listei este cont inut ntr-o celul a care cont ine n plus adresa
elementului urm ator, numit si pointer. Java permite realizarea listelor cu ajutorul
claselor si obiectelor: celulele sunt obiecte (adica instant e ale unei clase) n care
un c amp cont ine o referint a catre celula urm atoare. Referint a catre prima celula
este cont inut a ntr-o variabil a.
class Lista {
int continut;
Lista urmator;
141
142 CAPITOLUL 11. LISTE
Lista (int x, Lista a) {
continut = x;
urmator = a;
}
}
Instruct iunea new Lista(x,a) construieste o noua celula cu campurile x si
a. Func tia Lista(x,a) este un constructor al clasei Lista (un constructor este o
funct ie nestatica care se distinge prin tipul rezultatului s au care este cel al clasei
curente, si prin absent a numelui de identicare). Obiectul null apart ine tuturor
claselor si reprezint a n cazul listelor marcajul de sf arsit de lista. De asemenea
new Lista(2, new Lista(7, new Lista(11,null))) reprezint a lista 2, 7, 11.
static boolean esteVida (Lista a) {
return a == null;
}
Procedura adauga insereaza un element n capul listei. Aceast a modalitate
de a introduce elemente n capul listei este util a pentru ca num arul de operat ii
necesare adaug arii de elemente sa e independent de marimea listei; este sucienta
modicarea valorii capului listei, ceea ce se face simplu prin:
static Lista adaug (int x, Lista a) {
return new Liste (x, a); // capul vechi se va regasi dupa x
}
e1 e2 e3 e4
cap
e1 e2 e3 e4
cap
Figura 11.1: Ad augarea unui element n lista
Funct ia cauta, care verica dac a elementul x esten lista a, efectueaza o parcurgere
a listei. Variabila a este modicata iterativ prin a=a.urmator pentru a parcurge
elementele listei pan a se gaseste x sau p an a se gaseste sfarsitul listei (a = null).
static boolean cauta (int x, Lista a) {
while (a != null) {
if (a.continut == x)
return true;
a = a.urmator;
}
return false;
}
11.1. LISTE LINIARE 143
Funct ia cauta poate scris a n mod recursiv:
static boolean cauta (int x, Lista a) {
if (a == null)
return false;
else if (a.continut == x)
return true;
else
return cauta(x, a.urmator);
}
sau sub forma:
static boolean cauta (int x, Lista a) {
if (a == null)
return false;
else return (a.continut == x) || cauta(x, a.urmator);
}
sau sub forma:
static boolean cauta (int x, Lista a) {
return a != null && (a.continut == x || cauta(x, a.urmator));
}
Aceasta sciere recursiva este sistematica pentru funct iile care opereaz a asupra
listelor. Tipul list a veric a ecuat ia urm atoare:
Lista = Lista vida Element Lista
unde este sau exclusiv iar este produsul cartezian. Toate procedurile sau
funct iile care opereaz a asupra listelor se pot scrie recursiv n aceasta manier a. De
exemplu, lungimea listei se poate determina prin:
static int lungime(Lista a) {
if (a == null) return 0;
else return 1 + longime(a.urmator);
}
care este mai eleganta decat scrierea iterativ a:
static int lungime(Lista a) {
int lg = 0;
while (a != null) {
++lg;
a = a.urmator;
}
return lg;
}
144 CAPITOLUL 11. LISTE
Alegerea ntre maniera recursiva sau iterativ a este o problema subiectiv a n
general. Pe de alta parte se spune ca scrierea iterativa este mai ecient a pentru c a
foloseste mai put in a memorie. Grat ie noilor tehnici de compilare, acest lucru este
mai put in adev arat; ocuparea de memorie suplimentara, pentru calculatoarele de
astazi, este o problema foarte put in critic a.
Eliminarea unei celule care cont ine x se face modicand valoarea campului
urmator cont inut n predecesorul lui x: succesorul predecesorului lui x devine
succesorul lui x. Un tratament particular trebuie facut daca elementul care trebuie
eliminat este primul element din lista. Procedura recursiv a de eliminare este foarte
compacta n denit ia sa:
static Lista elimina (int x, Lista a) {
if (a != null)
if (a.continut == x)
a = a.urmator;
else
a.urmator = elimina (x, a.urmator);
return a;
}
e1 e2 e3 e4
cap
e1 e2 e3 e4
cap
Figura 11.2: Eliminarea unui element din list a
O procedur a iterativ a solicita un plus de atent ie.
static Lista elimina (int x, Lista a) {
if (a != null)
if (a.continut == x)
a = a.urmator;
else {
Lista b = a ;
while (b.urmator != null && b.urmator.continut != x)
b = b.urmator;
if (b.urmator != null)
b.urmator = b.urmator.urmator;
}
return a;
}
11.1. LISTE LINIARE 145

In cadrul funct iilor anterioare, care modic a lista a, nu se dispune de doua


liste distincte, una care cont ine x si alta identic a cu precedenta dar care nu mai
cont ine x. Pentru a face acest lucru, trebuie recopiat a o parte a listei a ntr-o
nou a lista, cum face programul urmator, unde exista o utilizare put in important a
de memorie suplimentara. Oricum, spat iul de memorie pierdut este recuperat de
culeg atorul de spat iu de memorie GC (Garbage Colection) din Java, daca nu mai
este folosita lista a. Cu tehnicile actuale ale noilor versiuni ale GC, recuperarea
se efectueaza foarte rapid. Aceasta este o diferent a important a fat a de limbajele
de programare precum Pascal sau C, unde trebuie sa m preocupat i de spat iul de
memorie pierdut, daca trebuie sa- l reutiliz am.
static Lista elimina (int x, Lista a) {
if (a != null)
return null;
else if (a.continut == x)
return a.urmator;
else
return new Lista (a.continut, elimina (x, a.urmator));
}
Exist a o tehnic a, utilizat a adesea, care permite evitarea unor teste pentru
eliminarea unui element dintr-o list a si, n general, pentru simplicarea program arii
operat iilor asupra listelor. Aceasta consta n utilizarea unui fanion / santinel a
care permite tratarea omogen a a listelor indiferent dac a sunt vide sau nu.

In
reprezentarea anterioara lista vida nu a avut aceeasi structur a ca listele nevide. Se
utilizeaz a o celul a plasat a la nceputul listei, care nu are informat ie semnicativa
n c ampul continut; adresa adev aratei prime celule se aa n c ampul urmator al
acestei celule. Astfel obt inem o lista pazit a, avantajul ind c a lista vida cont ine
numai garda si prin urmare un num ar de programe, care f aceau un caz special din
lista vida sau din primul element din lista, devin mai simple. Aceast a not iune este
un pic echivalent a cu not iunea de santinel a pentru tablouri. Se utilizeaz a aceasta
tehnic a n proceduri asupra sirurilor prea lungi.
De asemenea, se pot deni liste circulare cu garda / paznic n care n prima
celul a n c ampul urmator este ultima celula din lista.
Pentru implementarea listelor se pot folosi perechi de tablouri : primul tablou,
numit continut, cont ine elementele de informat ii, iar al doilea, numit urmator,
cont ine adresa elementului urm ator din tabloul continut.
Procedura adauga efectueaza numai 3 operat ii elementare. Este deci foarte
ecienta. Procedurile cauta si elimina sunt mai lungi pentru c a trebuie s a parcurg a
ntreaga lista. Se poate estima ca num arul mediu de operat ii este jumatate din
lungimea listei. C autarea binar a efectueaza un num ar logaritmic iar cautarea cu
tabele hash (de dispersie) este si mai rapid a.
Exemplu 1. Consider am construirea unei liste de numere prime mai mici
dec at un num ar natural n dat. Pentru construirea acestei liste vom ncepe, n
146 CAPITOLUL 11. LISTE
prima faz a, prin ad augarea numerelor de la 2 la n ncep and cu n si termin and cu
2. Astfel numerele vor n lista n ordine crescatoare. Vom utiliza metoda clasic a a
ciurului lui Eratostene: consideram succesiv elementele listei n ordine crescatoare
si suprim am tot i multiplii lor. Aceasta se realizeaz a prin procedura urm atoare:
static Lista Eratostene (int n) {
Lista a=null;
int k;
for(int i=n; i>=2; --i) {
a=adauga(i,a);
}
k=a.continut;
for(Lista b=a; k*k<=n; b=b.urmator) {
k=b.continut;
for(int j=k; j<=n/k; ++j)
a=elimina(j*k,a);
}
return a;
}
Exemplu 2. Pentru ecare intrare i din intervalul [0..n 1] se construieste
o lista simplu nl ant uit a format a din toate cheile care au h(x) = i. Elementele
listei sunt intr ari care permit accesul la tabloul de nume si numere de telefon.
Procedurile de cautare si inserare a unui element x ntr-un tablou devin proceduri
de cautare si ad augare ntr-o lista. Tot astfel, dac a funct ia h este rau aleas a,
num arul de coliziuni devine important, multe liste devin de dimensiuni enorme si
f ar amit area risca sa devin a la fel de costisitoare ca si cautarea ntr-o lista simplu
nl ant uit a obisnuit a. Daca h este bine aleasa, cautarea este rapid a.
Lista al[] = new Lista[N1];
static void insereaza (String x, int val) {
int i = h(x);
al[i] = adauga (x, al[i]);
}
static int cauta (String x) {
for (int a = al[h(x)]; a != null; a = a.urmator) {
if (x.equals(nume[a.continut]))
return telefon[a.continut];
}
return -1;
}
11.2. COZI 147
11.2 Cozi
Cozile sunt liste liniare particulare utilizate n programare pentru gestionarea
obiectelor care sunt n asteptarea unei prelucrari ulerioare, de exemplu procesele
de asteptare a resurselor unui sistem, nodurile unui graf, etc. Elementele sunt
ad augate sistematic la coada si sunt eliminate din capul listei.

In engleza se spune
strategie FIFO (First In First Out), n opozit ie cu strategia LIFO (Last In Firs
Out) utilizat a la stive.
Mai formalizat, consider am o mult ime de elemente E, mult imea cozilor cu
elemente din E este notata Coada(E), coada vid a (care nu cont ine nici un element)
este C
0
, iar operat iile asupra cozilor sunt: esteV ida, adauga, valoare, elimina:
esteV ida este o aplicat ie denita pe Coada(E) cu valori n true, false,
esteV ida(C) aste egala cu true daca si numai dac a coada C este vida.
adauga este o aplicat ie denita pe E Coada(E) cu valori n Coada(E),
adauga(x, C) este coada obt inut a plec and de la coada C si inser and elementul
x la sf arsitul ei.
valoare este o aplicat ie denita pe Coada(E)C
0
cu valori n E care asociaza
unei cozi C, nevid a, elementul aat n cap.
elimina este o aplicat ie denita pe Coada(E)C
0
cu valori n Coada(E) care
asociaza unei cozi nevide C o coada obt inut a plec and de la C si elimin and
primul element.
Operat iile asupra cozilor satisfac urmatoarele relat ii:
Pentru C ,= C
0
elimina(adauga(x, C)) = adauga(x, elimina(C))
valoare(adauga(x, C)) = valoare(C)
Pentru toate cozile C
esteV ida(adauga(x, C)) = false
Pentru coada C
0
elimina(adauga(x, C
0
)) = C
0
valoare(adauga(x, C
0
)) = x
esteV ida(C
0
) = true
O prim a idee de realizare sub forma de programe a operat iilor asupra cozilor
este de a mprumuta tehnica folosita n locurile unde client ii stau la coad a pentru
a servit i, de exemplu la gar a pentru a lua bilete, sau la cas a ntr-un magazin.
148 CAPITOLUL 11. LISTE
Fiecare client care se prezint a obt ine un num ar si client ii sunt apoi chemat i de catre
funct ionarul de la ghiseu n ordinea crescatoare a numerelor de ordine primite la
sosire. Pentru gestionarea acestui sistem, gestionarul trebuie sa cunoasca dou a
numere: num arul obt inut de c atre ultimul client sosit si num arul obt inut de c atre
ultimul client servit. Not am aceste doua numere inceput si sfarsit si gestionam
sistemul n modul urm ator:
coada de asteptare este vida daca si numai dac a inceput = sfarsit,
atunci c and soseste un nou client, se incrementeaz a sfarsit si se da acest
num ar clientului respectiv,
atunci c and funct ionarul este liber el poate servi un alt client, dac a coada
nu este vid a, incrementeaz a inceput si cheama posesorul acestui numar.

In cele ce urmeaza sunt prezentate toate operat iile n Java utiliz and tehnicile
urm atoare: se reatribuie numarul 0 unui nou client atunci c and se dep aseste un
anumit prag pentru valoarea sfarsit. Se spune c a este un tablou (sau tampon)
circular, sau coad a circular a.
class Coada {
final static int MaxC = 100;
int inceput;
int sfarsit;
boolean plina, vida;
int continut[];
Coada () {
inceput = 0; sfarsit = 0;
plina= false; vida = true;
info = new int[MaxC];
}
static Coada vida(){
return new Coada();
}
static void facVida (Coada c) {
c.inceput = 0; c.sfarsit = 0;
c.plina = false; c.vida = true;
}
static boolean esteVida(Coada c) {
return c.vida;
}
11.2. COZI 149
static boolean estePlina(Coada c) {
return c.plina;
}
static int valoare(Coada c) {
if (c.vida)
erreur ("Coada Vida.");
return c.info[f.inceput];
}
private static int succesor(int i) {
return (i+1) % MaxC;
}
static void adaug(int x, Coada c) {
if (c.plina)
erreur ("Coada Plina.");
c.info[c.sfarsit] = x;
c.sfarsit = succesor(c.sfarsit);
c.vida = false;
c.plina = c.sfarsit == c.inceput;
}
static void elimina (Coada c) {
if (c.vida)
erreur ("Coada Vida.");
c.inceput = succesor(c.inceput);
c.vida = c.sfarsit == c.inceput;
c.plina = false;
}
}
e1 e2 en
inceput sfarsit
...
Figura 11.3: Coad a de asteptare implementat a ca lista
O alt a modalitate de gestionare a cozilor const a n utilizarea listelor nl ant uite cu
150 CAPITOLUL 11. LISTE
gardan care se cunosc adresele primului si ultimului element. Aceasta d a operat iile
urm atoare:
class Coada {
Lista inceput;
Lista sfarsit;
Coada (Lista a, Lista b) {
inceput = a;
sfarsit = b;
}
static Coada vida() {
Lista garda = new Lista();
return new Coada (garda, garda);
}
static void facVida (Coada c) {
Lista garda = new Lista();
c.inceput = c.sfarsit = garda;
}
static boolean esteVida (Coada c) {
return c.inceput == c.sfarsit;
}
static int valoare (Coada c) {
Lista b = c.inceput.next;
return b.info;
}
static void adauga (int x, Coada c) {
Lista a = new Lista (x, null);
c.sfarsit.next = a;
f.sfarsit = a;
}
static void elimina (Coada c) {
if (esteVida(c))
erreur ("Coada Vida.");
c.inceput = c.inceput.next;
}
}
Cozile se pot implementa e cu ajutorul tablourilor e cu ajutorul listelor sim-
11.3. STIVE 151
plu nl ant uite. Scrierea programelor consta n primul r and n alegerea structurilor
de date pentru reprezentarea cozilor. Ansamblul funct iilor care opereaza asupra
cozilor se pot plasa ntr-un modul care manipuleaz a tablouri sau liste. Utilizarea
cozilor se face cu ajutorul funct iilor vida, adauga, valoare, elimina. Aceasta este
deci interfat a cozilor care are important a n programe complexe.
11.3 Stive
Not iunea de stiv a intervine n mod curent n programare, rolul sau principal
ind la implementarea apelurilor de proceduri. O stiv a se poate imagina ca o cutie
n care sunt plasate obiecte si din care se scot n ordinea invers a fat a de cum au
fost introduse: obiectele sunt puse unul peste altul n cutie si se poate avea acces
numai la obiectul situat n v arful stivei.

In mod formalizat, se considera o mult ime


E, mult imea stivelor cu elemente din E este notata Stiva(E), stiva vida (care nu
cont ine nici un element) este S
0
, operat iile efectuate asupra stivelor sunt vida,
adauga, valoare, elimina, ca si la re. De aceasta dat a, relat iile satisf acute sunt
urm atoarele:
elimina(adauga(x, S)) = S
esteV ida(adauga(x, S)) = false
valoare(adauga(x, S)) = x
esteV ida(S
0
) = true
Cu ajutorul acestor relat ii se pot exprima toate operat iile relative la stive.
Realizarea operat iilor asupra stivelor se poate face utiliz and un tablou care
cont ine elementele si un indice care indic a pozit ia varfului stivei.
class Stiva {
final static int maxP = 100;
int inaltime;
Element continut[];
Stiva() {
inaltime = 0;
continut = new Element[maxP];
}
static Fir vid () {
return new Stiva();
}
152 CAPITOLUL 11. LISTE
static void facVida (Stiva s) {
s.inaltime = 0;
}
static boolean esteVida (Stiva s) {
return s.inaltime == 0;
}
static boolean estePlina (Stiva s) {
return s.inaltime == maxP;
}
static void adauga (Element x, Stiva s) throws ExceptionStiva {
if (estePlina (s))
throw new ExceptionStiva("Stiva plina");
s.continut[s.inaltime] = x;
++ s.inaltime;
}
static Element valoare (Stiva s) throws ExceptionStiva {
if (esteVida (s))
throw new ExceptionStiva("Stiva vida");
return s.continut[s.inaltime-1];
}
static void supprimer (Stiva s) throws ExceptionStiva {
if (esteVida (s))
throw new ExceptionStiva ("Stiva vida");
s.inaltime;
}
}
unde
class ExceptionStiva extends Exception {
String text;
public ExceptionStiva (String x) {
text = x;
}
}
Stivele se pot implementa at at cu ajutorul tablourilor (vectorilor) c at si cu
ajutorul listelor simplu nl ant uite.
11.4. EVALUAREA EXPRESIILOR ARITMETICE PREFIXATE 153
11.4 Evaluarea expresiilor aritmetice prexate
Vom ilustra utilizarea stivelor printr-un program de evaluare a expresiilor
aritmetice scrise sub o forma particular a.

In programare o expresie aritmetica
poate cont ine numere, variabile si operat ii aritmetice (ne vom limita numai la +
si *). Vom considera ca intr ari numai numere naturale.
Expresiile prexate cont in simbolurile: numere naturale, +, *, ( si ). Daca e
1
si e
2
sunt expresii prexate atunci (+e
1
e
2
) si (e
1
e
2
) sunt expresii prexate.
Pentru reprezentarea unei expresii prexate n Java, vom utiliza un tablou ale
carui elemente sunt entitat ile expresiei. Elementele tabloului sunt obiecte cu trei
campuri: primul reprezint a natura entit at ii (simbol sau num ar), al doilea reprezint a
valoarea entitat ii daca aceasta este numar, iar al treilea este este un simbol dac a
entitatea este simbol.
class Element {
boolean esteOperator;
int valoare;
char simbol;
}
Vom utiliza funct iile denite pentru stiv a si procedurile denite n cele ce
urmeaza.
static int calcul (char a, int x, int y) {
switch (a) {
case +: return x + y;
case *: return x * y;
}
return 1;
}
Procedura de evaluare const a n stivuirea rezultatelor intermediare, stiva
cont in and operatori si numere, dar niciodat a nu va cont ine consecutiv numere.
Se examineaza succesiv entitat ile expresiei daca entitatea este un operator sau un
num ar si daca v arful stivei este un operator, atunci se plaseaz a n stiv a. Daca
este un numar si varful stivei este de asemenea un numar, act ioneaza operatorul
care precede v arful stivei asupra celor dou a numere si se repeta operat ia asupra
rezultatului g asit.
De exemplu, pentru expresia
(+ (* (+ 35 36) (+ 5 6)) (* (+ 7 8) (*9 9 )))
evaluarea decurge astfel:
154 CAPITOLUL 11. LISTE
9
5 7 * *
35 + + + + 15 15
+ + 71 71 71 * * * * *
* * * * * * 781 781 781 781 781 781
+ + + + + + + + + + + + +
static void insereaza (Element x, Stiva s) throws ExceptionStiva {
Element y, op;
while (!(Stiva.esteVida(s) || x.esteOperator
|| Stiva.valoare(s).esteOperator)) {
y = Stiva.valoare(s);
Stiva.elimina(s);
op = Stiva.valoare(s);
Stiva.elimina(s);
x.valoare = calcul(op.valsimb, x.valoare, y.valoare);
}
Stiva.adauga(x,s);
}
static int calcul (Element u[]) throws ExceptionStiva {
Stiva s = new Stiva();
for (int i = 0; i < u.length ; ++i) {
insereaza(u[i], s);
}
return Stiva.valoare(s).valoare;
}

In acest caz, este utila prezentarea unui program principal care utilizeaz a
aceste funct ii.
public static void main (String args[]) {
Element exp[] = new Element [args.length];
for (int i = 0; i < args.length; ++i) {
String s = args[i];
if (s.equals("+") || s.equals("*"))
exp[i] = new Element (true, 0, s.charAt(0));
else
exp[i] = new Element (false, Integer.parseInt(s), );
}
try { System.out.println(calcul(exp)); }
catch (ExceptionStiva x) {
System.err.println("Stiva " + x.nom);
}
}
11.5. OPERATII ASUPRA LISTELOR 155
11.5 Operat ii asupra listelor

In aceasta sect iune sunt prezentat i cat iva algoritmi de manipulare a listelor.
Acestia sunt utilizat i n limbajele de programare care au listele ca structuri de
baza. Funct ia Tail este o primitiv a clasica; ea suprim a primul element al listei.
e1
e2 e3 e4
a Tail(a)
e1
e2 e3 e4
Figura 11.4: Suprimarea primului element din list a
class Lista {
Object info;
Lista next;
Lista(Object x, Lista a) {
info = x;
next = a;
}
static Lista cons (Object x, Lista a) {
return new Lista (x, a);
}
static Object head (Lista a) {
if (a == null)
erreur ("Head dune liste vide.");
return a.info;
}
static Lista tail (Lista a) {
if (a == null)
erreur ("Tail dune liste vide.");
return a.next;
}
Procedurile asupra listelor construiesc o lista plec and de la alte dou a liste,
pun o lista la cap atul celeilalte liste.

In prima procedur a append, cele doua liste
nu sunt modicate; n a doua procedur a, concat, prima lista este transformata
pentru a ret ine rezultatul. Totusi, se poate remarca faptul c a, dac a append copiaza
primul s au argument, partajeaz a nalul listei rezultat cu al doilea argument.
156 CAPITOLUL 11. LISTE
a b
a1 a2 a3 b1 b2 b3 b4
append(a,b}
a1 a2 a3
Figura 11.5: Concatenarea a dou a liste prin append
static Lista append (Lista a, Lista b) {
if (a == null)
return b;
else
return adauga(a.info, append (a.next, b)) ;
}
a b
a1 a2 a3 b1 b2 b3 b4
concat(a,b}
Figura 11.6: Concatenarea a dou a liste prin concat
static Lista concat(Lista a, Lista b)
{
if (a == null)
return b;
else
{
Lista c = a;
while (c.next != null)
c = c.next;
c.next = b;
return a;
}
}
11.5. OPERATII ASUPRA LISTELOR 157
Aceasta ultim a procedur a se poate scrie recursiv:
static Lista concat (Lista a, Lista b) {
if (a == null)
return b;
else {
a.next = concat (a.next, c);
return a;
}
}
Procedura de calcul de oglindire a unei liste a consta n construirea unei
liste n care elementele listei a sunt n ordine invers a. Realizarea acestei proceduri
este un exercit iu clasic de programare asupra listelor. D am aici dou a solut i, una
iterativ a, cealalt a recursiv a, complexitatea este O(n
2
) deci patratica, dar clasic a.
Noua metoda nReverse modica argumentul s au, n timp ce Reverse nu-l modica
ci construieste o alta lista pentru rezultat.
static Lista nReverse (Lista a) {
Lista b = null;
while (a != null) {
Lista c = a.next;
a.next = b; b = a; a = c;
}
return b;
}
a
b
e1 e2 e3 e4 e5
c
nill
Figura 11.7: Transformarea listei prin inversarea leg aturilor
static Lista Reverse (Lista a) {
if (a == null)
return a;
else
return append(Reverse (a.next), adauga(a.info, null));
}
158 CAPITOLUL 11. LISTE
Se poate scrie o versiune iterativa a versiunii recursive, grat ie unei funct ii auxiliare
care acumuleaza rezultatul ntr-unul din argumentele sale:
static Lista nReverse (Lista a) {
return nReverse1(null, a);
}
static Lista nReverse1 (Lista b, Lista a) {
if (a == null)
return b;
else
return nReverse1(adauga(a.info, b), a.next);
}
Un alt exercit iu formator consta n a administra liste n care elementele sunt
aranjate n ordine crescatoare. Procedura de ad augare devine ceva mai complexa
pentru c a trebuie g asit a pozit ia celulei care trebuie ad augat a dup a parcurgerea
unei p art i a listei.
Nu trat am acest exercit iu decat n cazul listelor circulare cu gard a. Pentru o
astfel de list a, valoarea campului info din prima celul a nu are nici o semnicat ie
(este celula de gard a). C ampul next din ultima celul a cont ine adresa primei celule.
a
a1 a2 a3 a4 a5 a6
garda
Figura 11.8: List a circular a cu gard a
static Lista insereaza (int v, Lista a) {
Lista b = a;
while (b.next != a && v > head(b.next))
b = b.next;
b.next = adauga(v, b.next);
a.info = head(a) + 1;
return a;
}
Capitolul 12
Algoritmi divide et impera
12.1 Tehnica divide et impera
Divide et impera
1
este o tehnica de elaborare a algoritmilor care const a n:
Descompunerea repetat a a problemei (subproblemei) ce trebuie rezolvat a n
subprobleme mai mici.
Rezolvarea n acelasi mod (recursiv) a tuturor subproblemelor.
Compunerea subsolut iilor pentru a obt ine solut ia problemei (subproblemei)
init iale.
Descompunerea problemei (subproblemelor) se face pan a n momentul n care
se obt in subprobleme de dimensiuni atat de mici nc at au solut ie cunoscuta sau
pot rezolvate prin tehnici elementare.
Metoda poate descrisa astfel:
procedure divideEtImpera(P, n, S)
if (n n
0
)
then rezolv a subproblema P prin tehnici elementare
else
mparte P n P
1
, ..., P
a
de dimensiuni n
1
, ..., n
a
divideEtImpera(P
1
, n
1
, S
1
)
...
divideEtImpera(P
a
, n
a
, S
a
)
combin a S
1
, ..., S
a
pentru a obt ine S
Exemplele tipice de aplicare a acestei metode sunt algoritmii de parcurgere
a arborilor binari si algoritmul de cautare binar a.
1
divide-and-conquer, n engleza
159
160 CAPITOLUL 12. ALGORITMI DIVIDE ET IMPERA
12.2 Ordinul de complexitate
Vom presupune c a dimensiunea n
i
a subproblemei i satisface relat ia n
i

n
b
,
unde b > 1. Astfel, pasul de divizare reduce o subproblem a la altele de dimensiuni
mai mici, ceea ce asigura terminarea subprogramului recursiv.
Presupunem c a divizarea problemei n subprobleme si compunerea solut iilor
subproblemelor necesita un timp de ordinul O(n
k
). Atunci, complexitatea timp
T(n) a algoritmului divideEtImpera este dat a de relat ia de recurent a:
T(n) =
_
O(1) , daca n n
0
a T(
n
b
) +O(n
k
) , daca n > n
0
(12.2.1)
Teorema 3 Daca n > n
0
atunci:
T(n) =
_

_
O(n
log
b
a
) , daca a > b
k
O(n
k
log
b
n) , daca a = b
k
O(n
k
) , daca a < b
k
(12.2.2)
Demonstrat ie: Putem presupune, f ar a a restrange generalitatea, c a n = b
m
n
0
. De
asemenea, presupunem ca T(n) = c n
k
0
daca n n
0
si T(n) = a T(
n
b
) + c n
k
daca n > n
0
. Pentru n > n
0
avem:
T(n) = aT
_
n
b
_
+cn
k
= aT
_
b
m1
n
0
_
+cn
k
= a
_
aT
_
b
m2
n
0
_
+c
_
n
b
_
k
_
+cn
k
= a
2
T
_
b
m2
n
0
_
+c
_
a
_
n
b
_
k
+n
k
_
= ...
= a
m
T (n
0
) +c
_
a
m1
_
n
b
m1
_
k
+... +a
_
n
b
_
k
+n
k
_
= a
m
cn
k
0
+c
_
a
m1
b
k
n
k
0
+... +a
_
b
m1
_
k
n
k
0
+ (b
m
)
k
n
k
0
_
= cn
k
0
a
m
_
1 +
b
k
a
+... +
_
b
k
a
_
m
_
= ca
m
m

i=0
_
b
k
a
_
i
unde am notat cn
k
0
prin c. Distingem cazurile:
1. a > b
k
. Seria

m
i=0
_
b
k
a
_
i
este convergenta si deci sirul sumelor part iale este
convergent. De aici rezult a ca T(n) = O(a
m
) = O(a
log
b
n
) = O(n
log
b
a
)
12.3. EXEMPLE 161
2. a = b
k
. Rezult a ca a
m
= b
km
= cn
k
si de aici T(n) = O(n
k
m) = O(n
k
log
b
n).
3. a < b
k
. Avem T(n) = O(a
m
(
b
k
a
)
m
) = O(b
km
) = O(n
k
).
12.3 Exemple
Dintre problemele clasice care se pot rezolva prin metoda divide et impera
ment ion am:
cautare binar a
sortare rapid a (quickSort)
problema turnurilor din Hanoi
sortare prin interclasare
dreptunghi de arie maxim a n placa cu g auri
12.3.1 Sortare prin interclasare - MergeSort
Ideea este de a utiliza interclasarea n etapa de asamblare a solut iilor.

In urma rezolvarii recursive a subproblemelor rezult a vectori ordonat i si prin


interclasarea lor obt inem vectorul init ial sortat. Vectorii de lungime 1 sunt evident
considerat i sortat i.
Pasul de divizare se face n timpul O(1). Faza de asamblare se face n timpul
O(m
1
+m
2
) unde n
1
si n
2
sunt lungimile celor doi vectori care se interclaseaza.
Din Teorema 3 pentru a = 2, b = 2 si k = 1 rezult a ca ordinul de complexitate
al algoritmului MergeSort este O(nlog
2
n) unde n este dimensiunea vectorului
init ial supus sort arii.
class sort_interclasare
{
static int x[]={3,5,2,6,4,1,8,2,4,3,5,3};
public static int[] interclasare(int st,int m,int dr,int a[])
{
int i,j,k;
int b[]=new int[dr-st+1];
i=st;
j=m+1;
k=0;
while((i<=m)&&(j<=dr))
162 CAPITOLUL 12. ALGORITMI DIVIDE ET IMPERA
{
if(a[i]<=a[j]) { b[k]=a[i]; i++;}
else { b[k]=a[j]; j++; }
k++;
}
if(i<=m) for(j=i;j<=m; j++) { b[k]=a[j]; k++; }
else for(i=j;i<=dr;i++) { b[k]=a[i]; k++; }
k=0;
for(i=st;i<=dr;i++) { a[i]=b[k]; k++; }
return a;
}//interclasare
public static int[] divide(int st,int dr,int a[])
{
int m,aux;
if((dr-st)<=1)
{
if(a[st]>a[dr]) { aux=a[st]; a[st]=a[dr]; a[dr]=aux; }
}
else
{
m=(st+dr)/2;
divide(st,m,a);
divide(m+1,dr,a);
interclasare(st,m,dr,a);
}
return a;
}
public static void main(String[] args)
{
int i;
divide(0,x.length-1,x);
for(i=0;i<x.length;i++) System.out.print(x[i]+" ");
}//main
}//class
12.3.2 Placa cu gauri
Se consider a o plac a dreptunghiular a n care exista n gauri punctiforme de
coordonate cunoscute. Sa se determine pe aceasta plac a un dreptunghi de arie
maxima, cu laturile paralele cu laturile pl acii, care sa nu cont in a g auri n interior
ci numai eventual pe laturi.
12.3. EXEMPLE 163
Vom considera placa ntr-un sistem cartezian. Consider am o subproblema de
forma urm atoare: dreptunghiul determinat de diagonala (x1, y1) (din st anga-jos)
si (x2, y2) din dreapta-sus este analizat pentru a vedea dac an interior cont ine vreo
gaur a; dac a nu cont ine, este o solut ie posibil a (se va alege cea de arie maxima); dac a
exista o gaur a n interiorul s au, atunci se consider a patru subprobleme generate
de cele 4 dreptunghiuri obt inute prin descompunerea pe orizontala si pe vertical a
de cele doua drepte paralele cu axele care trec prin punctul care reprezint a gaura.
1
2
3
4
Figura 12.1: Dreptunghi de arie maxim a n placa cu g auri
import java.io.*;
class drArieMaxima
{
static int x1,y1,x2,y2,n,x1s,y1s,x2s,y2s,amax;
static int[] x;
static int[] y;
public static void main (String[] args) throws IOException
{
int i;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("dreptunghi.in")));
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("dreptunghi.out")));
st.nextToken(); x1=(int) st.nval;
st.nextToken(); y1=(int) st.nval;
st.nextToken(); x2=(int) st.nval;
st.nextToken(); y2=(int) st.nval;
st.nextToken(); n=(int) st.nval;
x=new int [n+1];
y=new int [n+1];
for(i=1;i<=n;i++)
{
st.nextToken(); x[i]=(int) st.nval;
st.nextToken(); y[i]=(int) st.nval;
164 CAPITOLUL 12. ALGORITMI DIVIDE ET IMPERA
}
dr(x1,y1,x2,y2);
out.println(amax);
out.println(x1s+" "+y1s+" "+x2s+" "+y2s);
out.close();
}
static void dr(int x1,int y1,int x2,int y2)
{
int i,s=(x2-x1)*(y2-y1);
if(s<=amax) return;
boolean gasit=false;
for(i=1;i<=n;i++)
if((x1<x[i])&&(x[i]<x2)&&(y1<y[i])&&(y[i]<y2))
{
gasit=true;
break;
}
if(gasit1)
{
dr(x1,y1,x[i],y2);
dr(x[i],y1,x2,y2);
dr(x1,y[i],x2,y2);
dr(x1,y1,x2,y[i]);
}
else
{
amax=s;
x1s=x1;
y1s=y1;
x2s=x2;
y2s=y2;
}
}
}
Capitolul 13
Metoda optimului local -
greedy
13.1 Metoda greedy
Metoda Greedy are n vedere rezolvarea unor probleme de optim n care
optimul global se determin a din estim ari succesive ale optimului local.
Metoda Greedy se aplica urm atorului tip de problem a: dintr-o mult ime de
elemente A, se cere sa se determine o submult ime B, care ndeplineste anumite
condit ii. De exemplu, alegerea ordinii optime de efectuare a unor lucr ari, alegerea
traseului optim pentru vizitarea unor obiective turistice, etc. Deoarece este posibil
sa existe mai multe solut ii se va alege solut ia care maximizeaza sau minimizeaz a
o anumita funct ie obiectiv.
O problem a poate rezolvat a prin tehnica (metoda) Greedy dac andeplineste
proprietatea: dac a B este o solut ie, iar C este inclusan B, atunci si C este o solut ie.
Pornind de la aceast a condit ie, init ial se presupune c a B este mult imea vida
si se adauga succesiv elemente din A n B, ajung and la un optim local. Dar,
succesiunea de optimuri locale nu asigur a, n general, optimul global. Dac a se
demonstreaza ca succesiunea de optimuri locale conduce la optimul global, atunci
metoda Greedy este aplicabila.
Exist a urm atoarele variante ale metodei Greedy:
1. Se pleac a de la solut ia vid a pentru multimea B si se ia pe rand c ate un
element din mult imea A. Daca elementul ales ndeplineste condit ia de optim
local, el este introdus n mult imea B.
2. Se ordoneaza elementele mult imii Asi se veric a daca un elementndeplineste
condit ia de apartenent a la mult imea B.
165
166 CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY
13.2 Algoritmi greedy
Algoritmii greedy sunt n general simpli si sunt folosit i la rezolvarea unor
probleme de optimizare.

In cele mai multe situat ii de acest fel avem:
o mult ime de candidat i (lucr ari de executat, varfuri ale grafului, etc.)
o funct ie care verica daca o anumit a mult ime de candidat i constituie o
solut ie posibil a, nu neap arat optim a, a problemei
o funct ie care verica daca o mult ime de candidat i este fezabil a, adic a dac a
este posibil sa completam aceasta mult ime astfel ncat s a obt inem o solut ie
posibil a, nu neap arat optim a, a problemei
o funct ie de select ie care indic a la orice moment care este cel mai promit ator
dintre candidat ii nc a nefolosit i
o funct ie obiectiv care da valoarea unei solut ii (timpul necesar execut arii
tuturor lucr arilor ntr-o anumit a ordine, lungimea drumului pe care l-am
gasit, etc) si pe care urm arim sa o optimiz am (minimiz am/maximiz am)
Pentru a rezolva problema de optimizare, caut am o solut ie posibil a care sa
optimizeze valoarea funct iei obiectiv.
Un algoritm greedy construieste solut ia pas cu pas.
Init ial, mult imea candidat ilor selectati este vida.
La ecare pas, ncercamsa ad aug am la aceasta mult ime pe cel mai promit ator
candidat, conform funct iei de select ie. Daca, dup a o astfel de adaugare, mult imea
de candidat i selectat i nu mai este fezabil a, elimin am ultimul candidat ad augat;
acesta nu va mai niciodata considerat. Dac a, dup a ad augare, mult imea de
candidat i selectat i este fezabil a, ultimul candidat ad augat va r amane de acum
ncolo n ea. De ecare data cand l argim mult imea candidat ilor selectat i, veric am
daca aceasta mult ime nu constituie o solut ie posibil a a problemei. Dac a algoritmul
greedy funct ioneaza corect, prima solut ie gasita va totodat a o solut ie optim a a
problemei.
Solut ia optim a nu este n mod necesar unic a: se poate ca funct ia obiectiv sa
aib a aceeasi valoare optima pentru mai multe solut ii posibile.
Descrierea formala a unui algoritm greedy general este:
function greedy(C) // C este mult imea candidat ilor
S // S este mult imea n care construim solut ia
while not solutie(S) and C ,= do
x un element din C care maximizeaz a/minimizeaz a select(x)
C C x
if fezabil(S x) then S S x
if solutie(S) then return S
else return nu exist a solut ie
13.3. EXEMPLE 167
13.3 Exemple
Dintre problemele clasice care se pot rezolva prin metoda greedy ment ion am:
plata restului cu num ar minim de monezi, problema rucsacului, sortare prin select ie,
determinarea celor mai scurte drumuri care pleac a din acelasi punct (algoritmul lui
Dijkstra), determinarea arborelui de cost minim (algoritmii lui Prim si Kruskal),
determinarea mult imii dominante, problema color arii intervalelor, codicarea Hu-
man, etc.
13.3.1 Plata restului
Un exemplu simplu de algoritm greedy este cel folosit pentru rezolvarea
urm atoarei probleme: trebuie sa d am restul unui client, folosind un num ar c at
mai mic de monezi.

In acest caz, elementele problemei sunt:
candidat ii: mult imea init ial a de monezi de 1, 5, si 25 unit at i, pentru care
presupunem ca avem un num ar nelimitat din ecare tip de moned a
o solut ie posibil a: valoarea totala a unei astfel de mult imi de monezi selectate
trebuie sa e exact valoarea pe care trebuie sa o d am ca rest
o mult ime fezabil a: valoarea totala a monezilor selectate n aceasta mult ime
nu este mai mare decat valoarea pe care trebuie sa o d am ca rest
funct ia de select ie: se alege cea mai mare moneda din mult imea de candidat i
r amasa
funct ia obiectiv: num arul de monezi folosite n solut ie; se doreste minimizarea
acestui numar
Se poate demonstra ca algoritmul greedy va g asi n acest caz mereu solut ia
optim a (restul cu un numar minim de monezi).
Pe de alt a parte, presupun and c a exista si monezi de 12 unit at i sau ca unele
din tipurile de monezi lipsesc din mult imea init iala de candidat i, se pot gasi destul
de usor contraexemple pentru care algoritmul nu g aseste solut ia optim a, sau nu
gaseste nici o solut ie, cu toate ca exista solut ie.
Evident, solut ia optim a se poate gasi ncercand toate combin arile posibile de
monezi. Acest mod de lucru necesita nsa foarte mult timp.
Un algoritm greedy nu duce deci ntotdeauna la solut ia optim a, sau la o
solut ie. Este doar un principiu general, urm and ca pentru ecare caz n parte s a
determin am daca obt inem sau nu solut ia optim a.
168 CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY
13.3.2 Problema continua a rucsacului
Se consider a n obiecte. Obiectul i are greutatea g
i
si valoarea v
i
(1 i n).
O persoan a are un rucsac. Fie G greutatea maxima suportat a de rucsac. Persoana
n cauz a doreste sa pun a n rucsac obiecte astfel nc at valoarea celor din rucsac sa
e cat mai mare. Se permite fract ionarea obiectelor (valoarea part ii din obiectul
fract ionat ind direct proport ional a cu greutatea ei!).
Not am cu x
i
[0, 1] partea din obiectul i care a fost pus a n rucsac. Practic,
trebuie sa maximizam funct ia
f(x) =
n

i=1
x
i
c
i
.
Pentru rezolvare vom folosi metoda greedy. O modalitate de a ajunge la
solut ia optim a este de a considera obiectele n ordinea descrescatoare a valorilor
utilit at ilor lor date de raportul
vi
gi
(i = 1, ..., n) si de a le nc arca ntregi n rucsac
p an a cand acesta se umple. Din aceasta cauza presupunem n continuare c a
v
1
g
1

v
2
g
2
...
v
n
g
n
.
Vectorul x = (x
1
, x
2
, ..., x
n
) se numeste solut ie posibil a daca
_
x
i
[0, 1], i = 1, 2, ..., n

n
i=1
x
i
g
i
G
iar o solut ie posibil a este solut ie optim a daca maximizeaza funct ia f.
Vom nota G
p
greutatea permisa de a se nc arca n rucsac la un moment dat.
Conform strategiei greedy, procedura de rezolvare a problemei este urmatoarea:
procedure rucsac()
G
p
G
for i = 1, n
if G
p
> g
i
then G
p
G
p
g
i
else
x
i

Gp
gi
for j = i + 1, n
x
j
0
Vectorul x are forma x = (1, ..., 1, x
i
, 0, ..., 0) cu x
i
(0, 1] si 1 i n.
Propozit ia 1 Procedura rucsac() furnizeaz a o solut ie optim a.
Demonstrat ia se gaseste, de exemplu, n [25] la pagina 226.
Capitolul 14
Metoda backtracking
Metoda backtracking se utilizeaz a pentru determinarea unei submult imi a
unui produs cartezian de forma S
1
S
2
... S
n
(cu mult imile S
k
nite) care
are anumite propriet at i. Fiecare element (s
1
, s
2
, ..., s
n
) al submult imii produsului
cartezian poate interpretat ca solut ie a unei probleme concrete.

In majoritatea cazurilor nu oricare element al produsului cartezian este solut ie


ci numai cele care satisfac anumite restrict ii. De exemplu, problema determinarii
tuturor combin arilor de m elemente luate cate n (unde 1 n m) din mult imea
1, 2, ..., m poate reformulat a ca problema determinarii submult imii produsului
cartezian 1, 2, ..., m
n
denita astfel:
(s
1
, s
2
, ..., s
n
) 1, 2, ..., m
n
[s
i
,= s
j
, i ,= j, 1 i, j n.
Metoda se aplica numai atunci c and nu exist a nici o alt a cale de rezolvare a
problemei propuse, deoarece timpul de execut ie este de ordin exponent ial.
14.1 Generarea produsului cartezian
Pentru ncepatori, nu metoda backtracking n sine este dicil de nt eles ci
modalitatea de generare a produsului cartezian.
14.1.1 Generarea iterativa a produsului cartezian
Presupunem c a mult imile S
i
sunt formate din numere ntregi consecutive
cuprinse ntre o valoare min si o valoare max. De exemplu, dac a min = 0, max = 9
atunci S
i
= 0, 1, ..., 9. Dorim s a generam produsul cartezian S
1
S
2
... S
n
169
170 CAPITOLUL 14. METODA BACKTRACKING
(de exemplu, pentru n = 4). Folosim un vector cu n elemente a = (a
1
, a
2
, ..., a
n
)
si vom atribui ec arei componente a
i
valori cuprinse ntre min si max.
0 1 2 3 4 5
0 1 ... ... n n+1
Ne trebuie un marcaj pentru a preciza faptul c a o anumit a component a este
goal a (nu are plasat an ea o valoare valida).

In acest scop folosim variabila gol care


poate init ializata cu orice valoare ntreag a care nu este n intervalul [min, max].
Este totusi util a init ializarea gol=min-1;.
Cum ncepem generarea produsului cartezian?
O prim a variant a este sa atribuim tuturor componentelor a
i
valoarea min si
avem astfel un prim element al produsului cartezian, pe care putem sa-l asam.
0 1 2 3 4 5
0 0 0 0
0 1 ... ... n n+1
Trebuie sa construim urm atoarele elemente ale produsului cartezian. Este
util a, n acest moment, imaginea kilometrajelor de masini sau a contoarelor de
energie electica, de ap a, de gaze, etc. Urmatoarea congurat ie a acestora este
0 1 2 3 4 5
0 0 0 1
0 1 ... ... n n+1
dup a care urmeaza
0 1 2 3 4 5
0 0 0 2
0 1 ... ... n n+1
Folosim o variabil a k pentru a urm ari pozit ia din vector pe care se schimb a
valorile. La nceput k = n si, pe aceasta pozit ie k, valorile se schimba crescand de
la min catre max. Se ajunge astfel la congurat ia (k = 4 = n aici!)
0 1 2 3 4 5
0 0 0 9
0 1 ... ... n n+1
Ce se nt ampl a mai departe? Apare congurat ia
0 1 2 3 4 5
0 0 1 0
0 1 ... ... n n+1
ntr-un mod ciudat!
Ei bine, se poate spune c a aici este cheia metodei backtraching! Daca reusim
sa nt elegem acest pas de trecere de la o congurat ie la alta si sa formalizam ce
am observat, atunci am descoperit singuri aceast a metoda!
Pe pozit ia k = n, odat a cu aparit ia valorii 9 = max, s-au epuizat toate
valorile posibile, asa ca vom considera c a pozit ia k este goal a si vom trece pe o
pozit ie mai la stanga, deci k = k 1 (acum k = 3).
0 1 2 3 4 5
0 0 0
0 1 ... ... n n+1
14.1. GENERAREA PRODUSULUI CARTEZIAN 171
Pe pozit ia k = 3 se afa valoarea 0 care se va schimba cu urm atoarea valoare
(dac a exista o astfel de valoare max), deci n acest caz cu 1.
0 1 2 3 4 5
0 0 1
0 1 ... ... n n+1
Dup a plasarea unei valori pe pozit ia k, se face pasul spre dreapta (k = k+1).
0 1 2 3 4 5
0 0 1
0 1 ... ... n n+1
Aici (dac a nu am iesitn afara vectorului, n urma pasului f acut spre dreapta!),
n cazul nostru k = 4 si pozit ia este goal a, se plaseaza prima valoare valid a (deci
valoarea min).
0 1 2 3 4 5
0 0 1 0
0 1 ... ... n n+1
Cum trecerea de la o valoare la alta se face prin cresterea cu o unitate a valorii
vechi iar acum facem trecerea gol min, nt elegem de ce este util sa init ializam
variabila gol cu valoarea min 1.
S a consider am ca s-a ajuns la congurat ia
0 1 2 3 4 5
0 0 9 9
0 1 ... ... n n+1
Aici k = 4 = n si 9 = max. Pe pozit ia k nu se mai poate pune o nou a valoare
si atunci:
se pune gol pe pozit ia k
se face pasul spre stanga, deci k = k 1
0 1 2 3 4 5
0 0 9
0 1 ... ... n n+1
Aici k = 3 si 9 = max. Pe pozit ia k nu se mai poate pune o nou a valoare si
atunci:
se pune gol pe pozit ia k
se face pasul spre stanga, deci k = k 1
0 1 2 3 4 5
0 0
0 1 ... ... n n+1
Aici k = 2 si 0 < max. Pe pozit ia k se poate pune o nou a valoare si atunci:
se pune 0 + 1 = 1 = urm atoarea valoare pe pozit ia k
se face pasul spre dreapta, deci k = k + 1
0 1 2 3 4 5
0 1
0 1 ... ... n n+1
Aici k = 3 si gol=-1< max. Pe pozit ia k se poate pune o nou a valoare:
se pune gol+1= 1 + 1 = 0 = urm atoarea valoare pe pozit ia k
172 CAPITOLUL 14. METODA BACKTRACKING
se face pasul spre dreapta, deci k = k + 1
0 1 2 3 4 5
0 1 0
0 1 ... ... n n+1
Aici k = 4 si gol=-1< max. Pe pozit ia k se poate pune o nou a valoare:
se pune gol+1= 1 + 1 = 0 = urm atoarea valoare pe pozit ia k
se face pasul spre dreapta, deci k = k + 1
0 1 2 3 4 5
0 1 0 0
0 1 ... ... n n+1
iar aici k = n +1 si am ajuns n afara vectorului! Nu-i nimic! Se aseaza
solut ia 0100 si se face pasul la stanga. Aici k = 4 si 0 < max. Pe pozit ia k se
poate pune o nou a valoare:
se pune 0 + 1 = 1 = urm atoarea valoare pe pozit ia k
se face pasul spre dreapta, deci k = k + 1
0 1 2 3 4 5
0 1 0 1
0 1 ... ... n n+1
A ap arut n mod evident urmatoarea regula: ajung and pe pozit ia k n,
ncercam sa punem urm atoarea valoare (gol are ca urmatoarea valoare pe min)
pe aceasta pozit ie si
daca se poate: se pune urm atoarea valoare si se face pasul spre dreapta;
daca nu se poate: se pune gol=min 1 si se face pasul spre stanga.
Presupunem c a s-a ajuns la congurat ia
0 1 2 3 4 5
9 9 9 9
0 1 ... ... n n+1
care se aseaza ca solut ie. Ajungem la k = 4
0 1 2 3 4 5
9 9 9 9
0 1 ... ... n n+1
se goleste pozit ia si ajungem la k = 3
0 1 2 3 4 5
9 9 9
0 1 ... ... n n+1
se goleste pozit ia si ajungem la k = 2
0 1 2 3 4 5
9 9
0 1 ... ... n n+1
se goleste pozit ia si ajungem la k = 1
0 1 2 3 4 5
9
0 1 ... ... n n+1
se goleste pozit ia si ajungem la k = 0
0 1 2 3 4 5
0 1 ... ... n n+1
iar aici k = 0 si am ajuns n afara vectorului!
Nu-i nimic! Aici s-a terminat generarea produsului cartezian!
14.1. GENERAREA PRODUSULUI CARTEZIAN 173
Putem descrie acum algoritmul pentru generarea produsului cartezian S
n
,
unde S = min, min + 1, ..., max:
structuri de date:
a[1..n] vectorul solut ie
k pozit ia n vectorul solut ie, cu convent iile k = 0 precizeaza terminarea
gener arii, iar k = n + 1 precizeaza faptul c a s-a construit o solut ie care poate
asata;
proces de calcul:
1. se construieste prima solut ie
2. se plaseaza pozit ia n afara vectorului (n dreapta)
3. cat timp nu s-a terminat generarea
4. dac a este deja construita o solut ie
5. se aseaza solut ia si se face pasul spre stanga
6. altfel, dac a se poate mari valoarea pe pozit ia curent a
7. se mareste cu 1 valoarea si se face pasul spre dreapta
8. altfel, se goleste pozit ia curent a si se face pasul spre stanga
3. revenire la 3.
Implementarea acestui algoritmn Java este urmatoarea:
class ProdCartI1 // 134.000 ms pentru 8 cu 14
{
static int n=8, min=1, max=14, gol=min-1;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
public static void main (String[] args)
{
int i, k;
long t1,t2;
t1=System.currentTimeMillis();
for(i=1;i<=n;i++) a[i]=min;
k=n+1;
while (k>0)
if (k==n+1) {/* afis(a); */ --k;}
else { if (a[k]<max) ++a[k++]; else a[k--]=gol; }
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}
174 CAPITOLUL 14. METODA BACKTRACKING
O alt a variant a ar putea init ializarea vectorului solut ie cu gol si plasarea
pe prima pozit ie n vector. Nu mai avemn acest caz o solut ie gata construita dar
algoritmul este acelasi. Implementarea n acest caz este urmatoarea:
class ProdCartI2 // 134.000 ms pentru 8 cu 14
{
static int n=8, min=1, max=14, gol=min-1;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
public static void main (String[] args)
{
int i, k;
long t1,t2;
t1=System.currentTimeMillis();
for(i=1;i<=n;i++) a[i]=gol;
k=1;
while (k>0)
if (k==n+1) {/* afis(a); */ --k;}
else { if (a[k]<max) ++a[k++]; else a[k--]=gol; }
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}
14.1.2 Generarea recursiva a produsului cartezian
class ProdCartR1 // 101.750 ms pentru 8 cu 14
{
static int n=8, min=1,max=14;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
14.1. GENERAREA PRODUSULUI CARTEZIAN 175
static void f(int k)
{
int i;
if (k>n) {/* afis(a); */ return; };
for(i=min;i<=max;i++) { a[k]=i; f(k+1); }
}
public static void main (String[] args)
{
long t1,t2;
t1=System.currentTimeMillis();
f(1);
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}
class ProdCartR2 // 71.300 ms pentru 8 cu 14
{
static int n=8, min=1,max=14;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
static void f(int k)
{
int i;
for(i=min;i<=max;i++) { a[k]=i; if (k<n) f(k+1); }
}
public static void main (String[] args)
{
long t1,t2;
t1=System.currentTimeMillis();
f(1);
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}
176 CAPITOLUL 14. METODA BACKTRACKING
class ProdCartCar1
{
static char[] x;
static int n,m;
static char[] a={0,A,X};
public static void main(String[] args)
{
n=4;
m=a.length-1;
x=new char[n+1];
f(1);
}
static void f(int k)
{
int i;
for(i=1;i<=m;i++)
{
x[k]=a[i];
if(k<n) f(k+1);
else afisv();
}
}
static void afisv()
{
int i;
for(i=1; i<=n; i++)
System.out.print(x[i]);
System.out.println();
}
}//class
class ProdCartCar2
{
static char[] x;
static int n;
static int[] m;
static char[][] a = { {0},
{0,A,B},
{0,1,2,3}
};
14.2. METODA BACTRACKING 177
public static void main(String[] args)
{
int i;
n=a.length-1;
m=new int[n+1];
for(i=1;i<=n;i++) m[i]=a[i].length-1;
x=new char[n+1];
f(1);
}
static void f(int k)
{
int j;
for(j=1;j<=m[k];j++)
{
x[k]=a[k][j];
if(k<n) f(k+1); else afisv();
}
}
static void afisv()
{
int i;
for(i=1; i<=n; i++) System.out.print(x[i]);
System.out.println();
}
}// class
14.2 Metoda bactracking
Se foloseste pentru rezolvarea problemelor carendeplinesc urm atoarele condit ii:
1. nu se cunoaste o alta metoda mai rapid a de rezolvare;
2. solut ia poate pus a sub forma unui vector x = (x
1
, x
2
, ..., x
n
) cu x
i
A
i
,
i = 1, ..., n;
3. mult imile A
i
sunt nite.
Tehnica backtracking pleac a de la urm atoarea premisa:
daca la un moment dat nu mai am nici o sansa s a ajung la solut ia
cautata, renunt s a continui pentru o valoare pentru care stiu ca nu
ajung la nici un rezultat.
178 CAPITOLUL 14. METODA BACKTRACKING
Specicul metodei consta n maniera de parcurgere a spat iului solut iilor.
solut iile sunt construite succesiv, la ecare etapa ind completat a cate o
componenta;
alegerea unei valori pentru o componenta se face ntr-o anumit a ordine
astfel nc at sa e asigurat a o parcurgere sistematica a spat iului A
1
A
2
... A
n
;
la completarea componentei k se veric a daca solut ia part ial a (x
1
, x
2
, ..., x
k
)
veric a condit iile induse de restrict iile problemei (acestea sunt numite condit ii de
continuare);
daca au fost ncercate toate valorile corespunz atoare componentei k si nc a
nu a fost g a sita o solut ie, sau dacse doreste determinarea unei noi solut ii, se revine
la componenta anterioar a (k1) si sencearca urm atoarea valoare corespunz a toare
acesteia, s.a.m.d.
procesul de cautare si revenire este continuat p an a cand este gasita o solut ie
(daca este sucienta o solut ie) sau pan a cand au foste testate toate congurat iile
posibile (dac a sunt necesare toate solut iile).

In gur a este ilustrat modul de parcurgere a spat iului solut iilor n cazul
gener arii produsului cartezian0, 1
4
.

In cazul n care sunt specicate restrict ii (ca de exemplu, s a nu e dou a


componente alaturate egale) anumite ramuri ale structurii arborescente asociate
sunt abandonate nainte de a atinge lungimea n.
0 1
0
0
0
0
0
0 0
0
0 0 0 0 0 0
1
1
1
1 1
1
1
1 1 1 1 1 1 1
0
0 0
0 0
0 0
1
1
1
1
1
1 1
x1
x2
x3
x4
x1
x2
x3
x4
14.2. METODA BACTRACKING 179

In aplicarea metodei pentru o problem a concreta se parcurg urm atoarele


etape:
se alege o reprezentare a solut iei sub forma unui vector cu n componente;
se identic a mult imile A
1
, A
2
, ..., A
n
si se stabileste o ordine ntre elemente
care sa indice modul de parcurgere a ec arei mult imi;
pornind de la restrict iile problemei se stabilesc condit iile de validitate ale
solut iilor part iale (condit iile de continuare).

In aplicat iile concrete solut ia nu este obt inut a numai n cazul n care k = n
ci este posibil sa e satisfacut a o condit ie de gasire a solut iei pentru k < n (pentru
anumite probleme nu toate solut iile cont in acelasi num ar de elemente).
14.2.1 Bactracking iterativ
Structura general a a algoritmului este:
for(k=1;k<=n;k++) x[k]=gol; init iarizarea vectorului solut ie
k=1; pozit ionare pe prima component a
while (k>0) cat timp exist a componente de analizat
if (k==n+1) daca x este solut ie
as(x); k; atunci: asare solut ie si pas stanga
else altfel:

if(x[k]<max[k]) daca exista elemente de ncercat


if(posibil(1+x[k])) daca 1+x[k] este valid a
++x[k++]; atunci m aresc si fac pas dreapta
else ++x[k]; altfel m aresc si r aman pe pozitie
else x[k]=gol; altfel golesc si fac pas dreapta

Vectorul solut ie x cont ine indicii din mult imile A


1
, A
2
, ..., A
n
. Mai precis,
x[k] = i nseamna ca pe pozit ia k din solut ia x se aa a
i
din A
k
.
14.2.2 Backtracking recursiv
Varianta recursiv a a algoritmului backtracking poate realizat a dup a o
schem a aseman atoare cu varianta recursiv a. Principiul de funct ionare al functiei
f (primul apel este f(1)) corespunzator unui nivel k este urmatorul:
n situat ia n care avem o solut ie, o asam si revenim pe nivelul anterior;
n caz contrar, se init ializeaza nivelul si se cauta un succesor;
cand am g asit un succesor, vericam daca este valid.

In caz armativ,
procedura se autoapeleaz a pentru k + 1; n caz contrar urm and a se continua
cautarea succesorului;
daca nu avem succesor, se trece la nivelul inferior k 1 prin iesirea din
procedura recursiva f.
180 CAPITOLUL 14. METODA BACKTRACKING
14.3 Probleme rezolvate
14.3.1 Generarea aranjamentelor
Reprezentarea solut iilor: un vector cu n componente.
Mult imile A
k
: 1, 2, ..., m.
Restrict iile si condit iile de continuare: Daca x = (x
1
, x
2
, ..., x
n
) este o solut ie
ea trebuie s a respecte restrict iile: x
i
,= x
j
pentru oricare i ,= j. Un vector cu k
elemente (x
1
, x
2
, ..., x
k
) poate conduce la o solut ie numai dac a satisface condit iile
de continuare x
i
,= x
j
pentru orice i ,= j, unde i, j 1, 2, ..., k.
Condit ia de g asire a unei solut ii: Orice vector cu n componente care respecta
restrict iile este o solut ie. Cand k = n + 1 a fost g asita o solut ie.
Prelucrarea solut iilor: Fiecare solut ie obt inut a este asata.
class GenAranjI1
{
static int n=2, min=1,max=4, gol=min-1;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++)
System.out.print(a[i]);
System.out.println();
}
static boolean gasit(int val, int pozmax)
{
for(int i=1;i<=pozmax;i++)
if (a[i]==val) return true;
return false;
}
public static void main (String[] args)
{
int i, k;
for(i=1;i<=n;i++) a[i]=gol;
k=1;
while (k>0)
if (k==n+1) {afis(a); --k;}
else
14.3. PROBLEME REZOLVATE 181
{
if(a[k]<max)
if(!gasit(1+a[k],k-1)) ++a[k++]; // maresc si pas dreapta
else ++a[k]; // maresc si raman pe pozitie
else a[k--]=gol;
}
}
}
class GenAranjI2
{
static int n=2, min=1,max=4;
static int gol=min-1;
static int[] a=new int[n+1];
static void afis()
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
static boolean posibil(int k)
{
for(int i=1;i<k;i++) if (a[i]==a[k]) return false;
return true;
}
public static void main (String[] args)
{
int i, k;
boolean ok;
for(i=1;i<=n;i++) a[i]=gol;
k=1;
while (k>0)
{
ok=false;
while (a[k] < max) // caut o valoare posibila
{
++a[k];
ok=posibil(k);
if(ok) break;
}
if(!ok) k--;
else if (k == n) afis();
182 CAPITOLUL 14. METODA BACKTRACKING
else a[++k]=0;
}
}
}
class GenAranjR1
{
static int n=2, m=4, nsol=0;
static int[] a=new int[n+1];
static void afis()
{
System.out.print(++nsol+" : ");
for(int i=1;i<=n;i++) System.out.print(a[i]+" ");
System.out.println();
}
static void f(int k)
{
int i,j;
boolean gasit;
for(i=1; i<=m; i++)
{
if(k>1) // nu este necesar !
{
gasit=false;
for(j=1;j<=k-1;j++)
if(i==a[j])
{
gasit=true;
break; // in for j
}
if(gasit) continue; // in for i
}
a[k]=i;
if(k<n) f(k+1); else afis();
}
}
public static void main(String[] args)
{
f(1);
}
}// class
14.3. PROBLEME REZOLVATE 183
class GenAranjR2
{
static int[] a;
static int n,m;
public static void main(String[] args)
{
n=2;
m=4;
a=new int[n+1];
f(1);
}
static void f(int k)
{
boolean ok;
int i,j;
for(i=1;i<=m;i++)
{
ok=true; // k=1 ==> nu am in stanga ... for nu se executa !
for(j=1;j<k;j++)
if(i==a[j])
{
ok=false;
break;
}
if(!ok) continue;
a[k]=i;
if(k<n) f(k+1);
else afisv();
}
}
static void afisv()
{
int i;
for(i=1; i<=n; i++)
System.out.print(a[i]);
System.out.println();
}
}
184 CAPITOLUL 14. METODA BACKTRACKING
14.3.2 Generarea combinarilor
Sunt prezentate mai multe variante (iterative si recursive) cu scopul de a
vedea diferite modalit at i de a castiga timp n execut ie (mai mult sau mai put in
semnicativ!).
class GenCombI1a // 4550 ms cu 12 22 // 40 ms cu 8 14 fara afis
{ // 7640 ms cu 8 14 cu afis
static int n=8, min=1,max=14;
static int gol=min-1,nv=0;
static int[] a=new int[n+1];
static void afis(int[] a)
{
int i;
System.out.print(++nv+" : ");
for(i=1;i<=n;i++)
System.out.print(a[i]);
System.out.println();
}
public static void main (String[] args)
{
int i, k;
long t1,t2;
t1=System.currentTimeMillis();
for(i=0;i<=n;i++)
a[i]=gol; // initializat si a[0] care nu se foloseste!!
k=1;
while (k>0)
if (k==n+1) { afis(a); --k; }
else
{
if(a[k]<max)
if(1+a[k]>a[k-1])
++a[k++]; // maresc si pas dreapta (k>1) ...
else ++a[k]; // maresc si raman pe pozitie
else a[k--]=gol;
}
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}// class
14.3. PROBLEME REZOLVATE 185
class GenCombI1b // 3825 ms 12 22 sa nu mai merg la n+1 !!!!
{
static int n=12, min=1,max=22;
static int gol=min-1;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
public static void main (String[] args)
{
int i;
long t1,t2;
t1=System.currentTimeMillis();
for(i=0;i<=n;i++) a[i]=gol; // initializat si a[0] care nu se foloseste!!
int k=1;
while (k>0)
{
if(a[k]<max)
if(1+a[k]>a[k-1])
if(k<n) ++a[k++]; // maresc si pas dreapta (k>1) ...
else { ++a[k]; /* afis(a); */}// maresc, afisez si raman pe pozitie
else ++a[k]; // maresc si raman pe pozitie
else a[k--]=gol;
}
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}// class
class GenCombI2a // 1565 ms 12 22
{
static int n=12, min=1,max=22;
static int gol=min-1;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
186 CAPITOLUL 14. METODA BACKTRACKING
public static void main (String[] args)
{
int i, k;
long t1,t2;
t1=System.currentTimeMillis();
for(i=0;i<=n;i++)
a[i]=gol; // initializat si a[0] care nu se foloseste!!
k=1;
while (k>0)
if (k==n+1) {/* afis(a); */ --k;}
else
{
if(a[k]<max-(n-k)) // optimizat !!!
if(1+a[k]>a[k-1]) ++a[k++]; // maresc si pas dreapta (k>1) ...
else ++a[k]; // maresc si raman pe pozitie
else a[k--]=gol;
}
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}
class GenCombI2b // 1250 ms 12 22
{
static int n=12, min=1,max=22;
static int gol=min-1;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++)
System.out.print(a[i]);
System.out.println();
}
public static void main (String[] args)
{
int i, k; long t1,t2;
t1=System.currentTimeMillis();
for(i=0;i<=n;i++)
a[i]=gol; // initializat si a[0] care nu se foloseste!!
k=1;
while (k>0)
14.3. PROBLEME REZOLVATE 187
{
if(a[k]<max-(n-k)) // optimizat !!!
if(1+a[k]>a[k-1])
if(k<n) ++a[k++]; // maresc si pas dreapta (k>1) ...
else { ++a[k]; /* afis(a); */}// maresc, afisez si raman pe pozitie
else ++a[k]; // maresc si raman pe pozitie
else a[k--]=gol;
}
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}
class GenCombI3a // 835 ms 12 22
{
static int n=12, min=1, max=22, gol=min-1;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
public static void main (String[] args)
{
int i, k; // si mai optimizat !!!
long t1,t2; t1=System.currentTimeMillis();
for(i=0;i<=n;i++)
a[i]=i-gol-1; // initializat si a[0] care nu se foloseste!!
k=1;
while (k>0)
if (k==n+1) {/* afis(a); */ --k;}
else
{
if(a[k]<max-(n-k)) // optimizat !!!
if(1+a[k]>a[k-1]) ++a[k++]; // maresc si pas dreapta (k>1) ...
else ++a[k]; // maresc si raman pe pozitie
else a[k--]=k-gol-1; // si mai optimizat !!!
}
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}
188 CAPITOLUL 14. METODA BACKTRACKING
class GenCombI3b // 740 ms 12 22
{
static int n=12, min=1, max=22, gol=min-1;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
public static void main (String[] args)
{
int i, k;
long t1,t2;
t1=System.currentTimeMillis();
for(i=0;i<=n;i++) a[i]=i-gol-1; // si mai optimizat !!!
k=1;
while (k>0)
{
if(a[k]<max-(n-k)) // optimizat !!!
{
++a[k]; // maresc
if(a[k]>a[k-1])
if(k<n) k++; // pas dreapta
/* else afis(a); */ // afisez
}
else a[k--]=k-gol-1; // si mai optimizat !!!
}
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}
class GenCombR1a // 2640 ms 12 22
{
static int[] x;
static int n,m;
public static void main(String[] args)
{
long t1,t2;
t1=System.currentTimeMillis();
n=12; m=22;
14.3. PROBLEME REZOLVATE 189
x=new int[n+1];
f(1);
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
static void f(int k)
{
for(int i=1;i<=m;i++)
{
if(k>1) if(i<=x[k-1]) continue;
x[k]=i;
if(k<n) f(k+1);/* else afisv(); */
}
}
static void afisv()
{
for(int i=1; i<=n; i++) System.out.print(x[i]);
System.out.println();
}
}
class GenCombR1b // 2100 ms 12 22
{
static int n=12,m=22;
static int[] a=new int[n+1];
static void afis(int[] a)
{
for(int i=1;i<=n;i++) System.out.print(a[i]);
System.out.println();
}
static void f(int k)
{
for(int i=1;i<=m;i++ )
{
if (i<=a[k-1]) continue; // a[0]=0 deci merge !
a[k]=i;
if(k<n) f(k+1); /* else afis(a); */
}
}
190 CAPITOLUL 14. METODA BACKTRACKING
public static void main (String [] args)
{
long t1,t2;
t1=System.currentTimeMillis();
f(1);
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}//main
}//class
class GenCombR2a // 510 ms 12 22
{
static int n=12, m=22, nsol=0, ni=0; ;
static int[] x=new int[n+1];
static void afisx()
{
System.out.print(++nsol+" ==> ");
for(int i=1;i<=n;i++) System.out.print(x[i]+" ");
System.out.println();
}
static void afis(int k) // pentru urmarirea executiei !
{ // ni = numar incercari !!!
int i;
System.out.print(++ni+" : ");
for(i=1;i<=k;i++) System.out.print(x[i]+" ");
System.out.println();
}
static void f(int k)
{
for(int i=k; i<=m-n+k; i++) // imbunatatit !
{
if(k>1)
{
// afis(k-1);
if(i<=x[k-1]) continue;
}
x[k]=i;
if(k<n) f(k+1); /* else afisx(); */
}
}
14.3. PROBLEME REZOLVATE 191
public static void main(String[] args)
{
long t1,t2;
t1=System.currentTimeMillis();
f(1);
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}// class
class GenCombR2b // 460 ms 12 22
{
static int n=12, m=22, nsol=0,ni=0;
static int[] x=new int[n+1];
static void afisx()
{
System.out.print(++nsol+" ==> ");
for(int i=1;i<=n;i++) System.out.print(x[i]+" ");
System.out.println();
}
static void afis(int k) // pentru urmarirea executiei !
{ // ni = numar incercari !!!
System.out.print(++ni+" : ");
for(int i=1;i<=k;i++) System.out.print(x[i]+" ");
System.out.println();
}
static void f(int k)
{
for(int i=k; i<=m-n+k; i++) // imbunatatit !
{
if(i<=x[k-1]) continue;
x[k]=i;
if(k<n) f(k+1); /* else afisx(); */
}
}
public static void main(String[] args)
{
long t1,t2;
t1=System.currentTimeMillis();
192 CAPITOLUL 14. METODA BACKTRACKING
f(1);
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}// class
class GenCombR3a // 165 ms 12 22
{
static int n=12;
static int m=22;
static int[] x=new int[n+1];
static int nsol=0,ni=0;
static void afisx()
{
int i;
System.out.print(++nsol+" ==> ");
for(i=1;i<=n;i++) System.out.print(x[i]+" ");
System.out.println();
}
static void afis(int k)
{
int i;
System.out.print(++ni+" : ");
for(i=1;i<=k;i++) System.out.print(x[i]+" ");
System.out.println();
}
static void f(int k)
{
int i;
for (i=x[k-1]+1; i<=m-n+k; i++) // si mai imbunatatit !!!
{
if(k>1)
{
// afis(k-1);
if(i<=x[k-1]) continue;
}
x[k]=i;
if(k<n) f(k+1); /* else afisx(); */
}
}
14.3. PROBLEME REZOLVATE 193
public static void main(String[] args)
{
long t1,t2;
t1=System.currentTimeMillis();
f(1);
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}// class
class GenCombR3b // 140 ms 12 22
{
static int n=12;
static int m=22;
static int[] x=new int[n+1];
static int nsol=0;
static void afisx()
{
int i;
System.out.print(++nsol+" : ");
for(i=1;i<=n;i++) System.out.print(x[i]+" ");
System.out.println();
}
static void f(int k)
{
int i;
for (i=x[k-1]+1; i<=m-n+k; i++)
{
x[k]=i;
if(k<n) f(k+1); /* else afisx(); */
}
}
public static void main(String[] args)
{
long t1,t2;
t1=System.currentTimeMillis();
f(1);
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}// class
194 CAPITOLUL 14. METODA BACKTRACKING
14.3.3 Problema reginelor pe tabla de sah
O problem a clasica de generare a congurat iilor ce respecta anumite restrict ii
este cea a amplasarii damelor pe o tabl a de sah astfel nc at sa nu se atace reciproc.
Reprezentarea solut iei: un vector x unde x
i
reprezint a coloana pe care se aa
regina dac a linia este i.
Restrict ii si condit ii de continuare: x
i
,= x
j
pentru oricare i ,= j (reginele nu
se ataca pe coloan a) si [x
i
x
j
[ ,= [i j[ (reginele nu se atac a pe diagonal a).
Sunt prezentate o varianta iterativ a si una recursiv a.
class RegineI1
{
static int[] x;
static int n,nv=0;
public static void main(String[] args)
{
n=5;
regine(n);
}
static void regine(int n)
{
int k;
boolean ok;
x=new int[n+1];
for(int i=0;i<=n;i++) x[i]=i;
k=1;
x[k]=0;
while (k>0)
{
ok=false;
while (x[k] <= n-1) // caut o valoare posibila
{
++x[k];
ok=posibil(k);
if(ok) break;
}
if(!ok) k--;
else if (k == n) afis();
else x[++k]=0;
}
}//regine()
14.3. PROBLEME REZOLVATE 195
static boolean posibil(int k)
{
for(int i=1;i<=k-1;i++)
if ((x[k]==x[i])||((k-i)==Math.abs(x[k]-x[i]))) return false;
return true;
}
static void afis()
{
int i;
System.out.print(++nv+" : ");
for(i=1; i<=n; i++) System.out.print(x[i]+" ");
System.out.println();
}
}// class
class RegineR1
{
static int[] x;
static int n,nv=0;
public static void main(String[] args)
{
n=5;
x=new int[n+1];
f(1);
}
static void f(int k)
{
boolean ok;
int i,j;
for(i=1;i<=n;i++)
{
ok=true;
for(j=1;j<k;j++)
if((i==x[j])||((k-j)==Math.abs(i-x[j]))) {ok=false; break;}
if(!ok) continue;
x[k]=i;
if(k<n) f(k+1); else afisv();
}
}
196 CAPITOLUL 14. METODA BACKTRACKING
static void afisv()
{
int i;
System.out.print(++nv+" : ");
for(i=1; i<=n; i++) System.out.print(x[i]+" ");
System.out.println();
}
}
14.3.4 Turneul calului pe tabla de sah
import java.io.*;
class Calut
{
static final int LIBER=0,SUCCES=1,ESEC=0,NMAX=8;
static final int[] a={0,2,1,-1,-2,-2,-1,1,2}; // miscari posibile pe Ox
static final int[] b={0,1,2,2,1,-1,-2,-2,-1}; // miscari posibile pe Oy
static int[][] tabla=new int[NMAX+1][NMAX+1]; // tabla de sah
static int n,np,ni=0; // np=n*n
public static void main(String[] args) throws IOException
{
BufferedReader br=new BufferedReader(
new InputStreamReader(System.in));
while ((n<3)||(n>NMAX))
{
System.out.print("Dimensiunea tablei de sah [3.."+NMAX+"] : ");
n=Integer.parseInt(br.readLine());
}
np=n*n;
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++) tabla[i][j]=LIBER;
tabla[1][1]=1;
if(incerc(2,1,1)==SUCCES) afisare();
else System.out.println("\nNu exista solutii !!!");
System.out.println("\n\nNumar de incercari = "+ni);
} // main
static void afisare()
{
System.out.println("\r----------------------------------");
14.3. PROBLEME REZOLVATE 197
for(int i=1;i<=n;i++)
{
System.out.println();
for(int j=1;j<=n;j++) System.out.print("\t"+tabla[i][j]);
}
}// afisare()
static int incerc(int i, int x, int y)
{
int xu,yu,k,rezultatIncercare; ni++;
k=1;
rezultatIncercare=ESEC;
while ((rezultatIncercare==ESEC)&&(k<=8))// miscari posibile cal
{
xu=x+a[k]; yu=y+b[k];
if((xu>=1)&&(xu<=n)&&(yu>=1)&&(yu<=n))
if(tabla[xu][yu]==LIBER)
{
tabla[xu][yu]=i;
// afisare();
if (i<np)
{
rezultatIncercare=incerc(i+1,xu,yu);
if(rezultatIncercare==ESEC) tabla[xu][yu]=LIBER;
}
else rezultatIncercare=SUCCES;
}
k++;
}// while
return rezultatIncercare;
}// incerc()
}// class
Pe ecran apar urm atoarele rezultate:
Dimensiunea tablei de sah [3..8] : 5
----------------------------------
1 6 15 10 21
14 9 20 5 16
19 2 7 22 11
8 13 24 17 4
25 18 3 12 23
Numar de incercari = 8839
198 CAPITOLUL 14. METODA BACKTRACKING
14.3.5 Problema colorarii hart ilor
Se consider a o hart a cu n t ari care trebuie colorat a folosind m < n culori,
astfel nc at oricare dou a t ari vecine sa e colorate diferit. Relat ia de vecinatate
dintre t ari este ret inut a ntr-o matrice n n ale carei elemente sunt:
v
i,j
=
_
1 dac a i este vecina cu j
0 dac a i nu este vecina cu j
Reprezentarea solut iilor: O solut ie a problemei este o modalitate de col-
orare a t a rilor si poate reprezentata printru-un vector x = (x
1
, x
2
, ..., x
n
) cu
x
i
1, 2, ..., m reprezentand culoarea asociata t arii i. Mult imile de valor ale
elementelor sint A
1
= A
2
= ... = A
n
= 1, 2, ..., m.
Restrict ii si condit ii de continuare: Restrict ia ca dou a t ari vecine sa e col-
orate diferit se specica prin: x
i
,= x
j
pentru orice i si j av and proprietatea v
i,j
= 1.
Condit ia de continuare pe care trebuie s a o satisfaca solut ia part iala (x
1
, x
2
, ..., x
k
)
este: x
k
,= x
i
pentru orice i < k cu proprietatea ca v
i,k
= 1.
class ColorareHartiI1
{
static int nrCulori=3;// culorile sunt 1,2,3
static int[][] harta=
{// CoCaIaBrTu
{2,1,1,1,1},// Constanta (0)
{1,2,1,0,0},// Calarasi (1)
{1,1,2,1,0},// Ialomita (2)
{1,0,1,2,1},// Braila (3)
{1,0,0,1,2} // Tulcea (4)
};
static int n=harta.length;
static int[] culoare=new int[n];
static int nrVar=0;
public static void main(String[] args)
{
harta();
if(nrVar==0)
System.out.println("Nu se poate colora !");
} // main
static void harta()
{
int k; // indicele pentru tara
boolean okk;
14.3. PROBLEME REZOLVATE 199
k=0; // prima pozitie
culoare[k]=0; // tara k nu este colorata (inca)
while (k>-1) // -1 = iesit in stanga !!!
{
okk=false;
while(culoare[k] < nrCulori)// selectez o culoare
{
++culoare[k];
okk=posibil(k);
if (okk) break;
}
if (!okk)
k--;
else if (k == (n-1))
afis();
else culoare[++k]=0;
}
} // harta
static boolean posibil(int k)
{
for(int i=0;i<=k-1;i++)
if((culoare[k]==culoare[i])&&(harta[i][k]==1))
return false;
return true;
} // posibil
static void afis()
{
System.out.print(++nrVar+" : ");
for(int i=0;i<n;i++)
System.out.print(culoare[i]+" ");
System.out.println();
} // scrieRez
} // class
Pentru 3 culori rezultatele care apar pe ecran sunt:
1 1 2 3 2 3
2 1 3 2 3 2
3 2 1 3 1 3
4 2 3 1 3 1
5 3 1 2 1 2
6 3 2 1 2 1
200 CAPITOLUL 14. METODA BACKTRACKING
class ColorareHartiR1
{
static int[] x;
static int[][] a=
{
{0,0,0,0,0,0},
{0,2,1,1,1,1},// Constanta (1)
{0,1,2,1,0,0},// Calarasi (2)
{0,1,1,2,1,0},// Ialomita (3)
{0,1,0,1,2,1},// Braila (4)
{0,1,0,0,1,2} // Tulcea (5)
};
static int n,m,nv=0;
public static void main(String[] args)
{
n=a.length-1;
m=3; // nr culori
x=new int[n+1];
f(1);
}
static void f(int k)
{
boolean ok;
int i,j;
for(i=1;i<=m;i++)
{
ok=true;
for(j=1;j<k;j++)
if((i==x[j])&&(a[j][k]==1)) {ok=false; break;}
if(!ok) continue;
x[k]=i;
if(k<n) f(k+1); else afisv();
}
}
static void afisv()
{
System.out.print(++nv+" : ");
for(int i=1; i<=n; i++) System.out.print(x[i]+" ");
System.out.println();
}
}
14.3. PROBLEME REZOLVATE 201
14.3.6 Problema vecinilor
Un grup de n persoane sunt asezate pe un rand de scaune.

Intre oricare doi
vecini izbucnesc conicte. Rearanjat i persoanele pe scaune astfel nc at ntre oricare
doi vecini certat i sa existe una sau cel mult doua persoane cu care nu au apucat
sa se certe! Asat i toate variantele de reasezare posibile.
Vom rezolva problema prin metada backtracking. Presupunem c a persoanele
sunt numerotate la nceput, de la stanga la dreapta cu 1, 2, ..., n. Consider am ca
solut ie un vector x cu n componente pentru care x
i
reprezint a pozit ia pe care se
va aa persoana i dup a reasezare.
Pentru k > 1 dat, condit iile de continuare sunt:
a) x
j
,= x
k
, pentru j = 1, ..., k 1 (x trebuie sa e permutare)
b) [x
k
x
k1
[ = 2 sau 3 (ntre persoana k si vecinul s au anterior trebuie s a se
ae una sau dou a persoane)

In locul solutiei x vom lista permutarea y = x


1
unde y
i
reprezint a persoana
care se aseaza pe locul i.
class VeciniR1
{
static int[] x;
static int n,m,nsol=0;
public static void main(String[] args)
{
n=7, m=7;
x=new int[n+1];
f(1);
}
static void f(int k)
{
boolean ok;
int i,j;
for(i=1;i<=m;i++)
{
ok=true;
for(j=1;j<k;j++) if(i==x[j]) {ok=false; break;}
if(!ok) continue;
if((Math.abs(i-x[k-1])!=2)&&(Math.abs(i-x[k-1])!=3)) continue;
x[k]=i;
if(k<n) f(k+1); else afis();
}
202 CAPITOLUL 14. METODA BACKTRACKING
}
static void afis()
{
int i;
System.out.print(++nsol+" : ");
for(i=1; i<=n; i++) System.out.print(x[i]+" ");
System.out.println();
}
}
import java.io.*;
class Vecini
{
static int nrVar=0,n;
static int[] x,y;
public static void main(String[] args) throws IOException
{
BufferedReader br=new BufferedReader(
new InputStreamReader(System.in));
while ((n<3)||(n>50))
{
System.out.print("numar persoane [3..50] : ");
n=Integer.parseInt(br.readLine());
}
x=new int[n];
y=new int[n];
reasez(0);
if(nrVar==0) System.out.println("Nu se poate !!!");
} // main
static void reasez(int k)
{
int i,j;
boolean ok;
if (k==n) scrieSolutia();// n=in afara vectorului !!!
else for(i=0;i<n;i++)
{
ok=true;
j=0;
while ((j<k-1) && ok) ok=(i != x[j++]);
if (k>0)
14.3. PROBLEME REZOLVATE 203
ok=(ok&&((Math.abs(i-x[k-1])==2)||(Math.abs(i-x[k-1])==3)));
if (ok) { x[k]=i; reasez(k+1);}
}
} // reasez
static void scrieSolutia()
{
int i;
for(i=0;i<n;i++) y[x[i]]=i;// inversarea permutarii !!!
System.out.print(++nrVar+" : ");
for(i=0;i<n;i++)
System.out.print(++y[i]+" "); // ca sa nu fie 0,1,...,n-1
System.out.println(); // ci 1,2,...,n (la afisare)
} // scrieRez
} // class
14.3.7 Problema labirintului
Se d a un labirint sub form a de matrice cu m linii si n coloane. Fiecare element
al matricei reprezint a o camera a labirintului.

Intr-una din camere, de coordonate
x
0
si y
0
se ga seste un om. Se cere sa se ga seasca toate iesirile din labirint.
O prim a problem a care se pune este precizarea modului de codicare a
iesirilor din ecare camer a a labirintului.
Dintr-o pozit ie oarecare (i, j) din labirint, deplasarea se poate face n patru
direct ii: Nord, Est, Sud si Vest considerate n aceasta ordine (putem alege oricare
alt a ordine a celor patru direct ii, dar odat a aleasa aceasta se pastreaza p an a la
sfarsitul problemei).
Fiecare camer a din labirint poate caracterizata printr-un sir de patru cifre
binare asociate celor patru direct ii, av and semnicat ie faptul c a acea camer a are
sau nu iesiri pe direct iile considerate.
De exemplu, daca pentru camera cu pozit ia (2, 4) exist a iesiri la N si S, ei i
va corespunde sirul 1010 care reprezint a num arul 10 n baza zece.
Prin urmare, codic am labirintul printr-o matrice a[i][j] cu elemente ntre 1
sai 15. Pentru a testa usor iesirea din labirint, matricea se bordeaz a cu dou a linii
si dou a coloane de valoare egala cu 16.
Ne punem problema determin arii iesirilor pe care le are o camera.
O camera are iesirea numai spre N dac a si numai dac a a[i][j]&&8 ,= 0.
Drumul parcurs la un moment dat se ret ine ntr-o matrice cu doua linii, d,
n care:
d[1][k] reprezint a linia camerei la care s-a ajuns la pasul k;
d[2][k] reprezint a coloana camerei respective.
La gasirea unei iesiri din labirint, drumul este asat.
Principiul algoritmului este urm atorul:
204 CAPITOLUL 14. METODA BACKTRACKING
se testeaza daca s-a iesit din labiritn (adic a a[i][j] = 16);
n caz armativ se aseaza drumul g asit;
n caz contrar se procedeaz a astfel:
se ret in n matricea d coordonatele camerei vizitate;
se veric a daca drumul arcurs a mai trecut prin aceast a camera, caz n
care se iese din procedur a;
se testeaza pe r and iesirile spre N, E, S, V si acolo unde este gasita o astfel
de iesire se reapeleaz a procedura cu noile coordonate;
naintea iesirii din procedur a se decrementeaza valoarea lui k.
import java.io.*;
class Labirint
{
static final char coridor=., start=x,
gard=H, pas=*, iesire=E;
static char[][] l;
static int m,n,x0,y0;
static boolean ok;
public static void main(String[] args) throws IOException
{
int i,j,k;
StreamTokenizer st = new StreamTokenizer(
new BufferedReader(new FileReader("labirint.in")));
st.nextToken(); m=(int)st.nval;
st.nextToken(); n=(int)st.nval;
l=new char[m][n];
for(i=0;i<m;i++)
{
st.nextToken();
for(j=0;j<n;j++) l[i][j]=st.sval.charAt(j);
}
st.nextToken(); x0=(int)st.nval;
st.nextToken(); y0=(int)st.nval;
ok=false;
gi(x0,y0);
l[x0][y0]=start;
PrintWriter out = new PrintWriter(
new BufferedWriter(new FileWriter("labirint.out")));
for(i=0;i<m;i++)
{
if (i>0) out.println();
14.3. PROBLEME REZOLVATE 205
for(j=0;j<n;j++) out.print(l[i][j]);
}
if (!ok) out.println("NU exista iesire !");
out.close();
}// main()
static void gi(int x,int y)
{
if ((x==0)||(x==n-1)||(y==0)||(y==m-1)) ok=true;
else
{
l[x][y]=pas;
if(l[x][y+1]==coridor||l[x][y+1]==iesire) gi(x,y+1);
if(!ok&&(l[x+1][y]==coridor||l[x+1][y]==iesire)) gi(x+1,y);
if(!ok&&(l[x][y-1]==coridor||l[x][y-1]==iesire)) gi(x,y-1);
if(!ok&&(l[x-1][y]==coridor||l[x-1][y]==iesire)) gi(x-1,y);
l[x][y]=coridor;
}
if (ok) l[x][y]=pas;
}
}// class
De exemplu, pentru sierul de intrare: labirint.in
8 8
HHHHHHEH
H....H.H
H.HHHH.H
H.HHHH.H
H....H.H
H.HHHH.H
H......H
HHHHHHEH
2 2
se obt ine sierul de iesire: labirint.out
HHHHHHEH
H....H.H
H*xHHH.H
H*HHHH.H
H*...H.H
H*HHHH.H
H******H
HHHHHH*H
206 CAPITOLUL 14. METODA BACKTRACKING
14.3.8 Generarea partit iilor unui numar natural
S a se aseze toate modurile de descompunere a unui numar natural n ca
suma de numere naturale.
Vom folosi o procedura f care are doi parametri: componenta la care s-a
ajuns (k) si o valoare v (care cont ine diferent a care a mai r amas pan a la n).
Init ial, procedura este apelata pentru nivelul 1 si valoarea n. Imediat ce este
apelat a, procedura va apela o alta pentru asarea vectorului (init ial aseaza n).
Din valoarea care se gaseste pe un nivel, S[k], se scad pe r and valorile
1, 2, ..., S[k] 1, valori cu care se apeleaz a procedura pentru nivelul urm ator.
La revenire se reface valoarea existenta.
class PartitieNrGenerare // n = suma de numere
{
static int dim=0, nsol=0, n=6;
static int[] x=new int[n+1];
public static void main(String[] args)
{
long t1,t2;
t1=System.currentTimeMillis();
f(n,n,1);
t2=System.currentTimeMillis();
System.out.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n");
}
static void f(int val, int maxp, int poz)
{
if(maxp==1) { nsol++; dim=poz-1; afis2(val,maxp); return; }
if(val==0) { nsol++; dim=poz-1; afis1(); return; }
int maxok=min(maxp,val);
for(int i=maxok;i>=1;i--)
{
x[poz]=i;
f(val-i,min(val-i,i),poz+1);
}
}
static void afis1()
{
System.out.print("\n"+nsol+" : ");
for(int i=1;i<=dim;i++) System.out.print(x[i]+" ");
}
14.3. PROBLEME REZOLVATE 207
static void afis2(int val,int maxip)
{
int i;
System.out.print("\n"+nsol+" : ");
for(i=1;i<=dim;i++) System.out.print(x[i]+" ");
for(i=1;i<=val;i++) System.out.print("1 ");
}
static int min(int a,int b) { return (a<b)?a:b; }
}
Pentru descompunerea ca suma de numere impare, programul este:
class PartitieNrImpare1 // n = suma de numere impare
{ // nsol = 38328320 1Gata!!! timp = 8482
static int dim=0,nsol=0;
static int[] x=new int[161];
public static void main(String[] args)
{
long t1,t2;
int n=160, maxi=((n-1)/2)*2+1;
t1=System.currentTimeMillis();
f(n,maxi,1);
t2=System.currentTimeMillis();
System.out.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n");
}
static void f(int val, int maxip, int poz)
{
if(maxip==1)
{
nsol++;
// dim=poz-1;
// afis2(val,maxip);
return;
}
if(val==0)
{
nsol++;
// dim=poz-1; afis1();
return;
}
208 CAPITOLUL 14. METODA BACKTRACKING
int maxi=((val-1)/2)*2+1;
int maxiok=min(maxip,maxi);
for(int i=maxiok;i>=1;i=i-2)
{
x[poz]=i;
f(val-i,min(maxiok,i),poz+1);
}
}
static void afis1()
{
System.out.print("\n"+nsol+" : ");
for(int i=1;i<=dim;i++) System.out.print(x[i]+" ");
}
static void afis2(int val,int maxip)
{
int i;
System.out.print("\n"+nsol+" : ");
for(i=1;i<=dim;i++) System.out.print(x[i]+" ");
for(i=1;i<=val;i++) System.out.print("1 ");
}
static int max(int a,int b) { return (a>b)?a:b; }
static int min(int a,int b) { return (a<b)?a:b; }
}// class
O versiune optimizata este:
class PartitieNrImpare2 // n = suma de numere impare ;
{ // optimizat timp la jumatate !!!
static int dim=0,nsol=0; // nsol = 38328320 2Gata!!! timp = 4787
static int[] x=new int[161];
public static void main(String[] args)
{
long t1,t2;
int n=160, maxi=((n-1)/2)*2+1;
t1=System.currentTimeMillis();
f(n,maxi,1);
t2=System.currentTimeMillis();
System.out.println("nsol = "+nsol+" timp= "+(t2-t1));
}
14.3. PROBLEME REZOLVATE 209
static void f(int val, int maxip, int poz)
{
if(maxip==1)
{
nsol++;
// dim=poz-1; afis2(val);
return;
}
if(val==0)
{
nsol++;
// dim=poz-1; afis1();
return;
}
int maxi=((val-1)/2)*2+1;
int maxiok=min(maxip,maxi);
for(int i=maxiok;i>=3;i=i-2)
{
x[poz]=i;
f(val-i,i,poz+1);
}
nsol++;
// dim=poz-1;
// afis2(val);
}
static void afis1()
{
System.out.print("\n"+nsol+" : ");
for(int i=1;i<=dim;i++) System.out.print(x[i]+" ");
}
static void afis2(int val)
{
System.out.print("\n"+nsol+" : ");
for(int i=1;i<=dim;i++) System.out.print(x[i]+" ");
for(int i=1;i<=val;i++) System.out.print("1 ");
}
static int max(int a,int b) { return (a>b)?a:b; }
static int min(int a,int b) { return (a<b)?a:b; }
}// class
210 CAPITOLUL 14. METODA BACKTRACKING
14.3.9 Problema parantezelor
Problema cere generarea tuturor combinat iilor de 2n paranteze (n paranteze
de deschidere si n paranteze de nchidere) care se nchid corect.
class ParantezeGenerare // 2*n paranteze
{
static int nsol=0;
static int n=4;
static int n2=2*n;
static int[] x=new int[n2+1];
public static void main(String[] args)
{
long t1,t2;
t1=System.currentTimeMillis();
f(1,0,0);
t2=System.currentTimeMillis();
System.out.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n");
}
static void f(int k, int npd, int npi)
{
if(k>n2) afis();
else
{
if(npd<n) { x[k]=1; f(k+1,npd+1,npi); }
if(npi<npd) { x[k]=2; f(k+1,npd,npi+1); }
}
}
static void afis()
{
int k;
System.out.print(++nsol+" : ");
for(k=1;k<=n2;k++)
if(x[k]==1) System.out.print("( ");
else System.out.print(") ");
System.out.println();
}
}// class
Capitolul 15
Programare dinamica
15.1 Prezentare generala
Folosirea tehnicii programarii dinamice solicit a experient a, intuit ie si abilit at i
matematice. De foarte multe ori rezolvarile date prin aceasta tehnic a au ordin de
complexitate polinomial.

In literatura de specialitate exist a dou a variante de prezentare a acestei


tehnici. Prima dintre ele este mai degrab a de natur a deductiv a pe cand a doua
foloseste gandirea inductiv a.
Prima variant a se bazeaza pe conceptul de subproblem a. Sunt considerate
urm atoarele aspecte care caracterizeaza o rezolvare prin programare dinamic a:
Problema se poate descompune recursiv n mai multe subprobleme care
sunt caracterizate de optime part iale, iar optimul global se obt ine prin combinarea
acestor optime part iale.
Subproblemele respective se suprapun. La un anumit nivel, dou a sau mai
multe subprobleme necesita rezolvarea unei aceeasi subprobleme. Pentru a evita
risipa de timp rezultata n urma unei implement ari recursive, optimele part iale se
vor ret ine treptat, n maniera bottom-up, n anumite structuri de date (tabele).
A doua variant a de prezentare face apel la conceptele intuitive de sistem, stare
si decizie. O problem a este abordabil a folosind tehnica program arii dinamice dac a
satisface principiul de optimalitate sub una din formele prezentate n continuare.
Fie secvent a de stari S
0
, S
1
, ..., S
n
ale sistemului.
Daca d
1
, d
2
, ..., d
n
este un sir optim de decizii care duc la trecerea sistemului
din starea S
0
n starea S
n
, atunci pentru orice i (1 i n) d
i+1
, d
i+2
, ..., dn este
un sir optim de decizii care duc la trecerea sistemului din starea S
i
n starea S
n
.
211
212 CAPITOLUL 15. PROGRAMARE DINAMIC

A
Astfel, decizia d
i
depinde de deciziile anterioare d
i+1
, ..., d
n
. Spunem c a se aplica
metoda nainte.
Daca d
1
, d
2
, ..., d
n
este un sir optim de decizii care duc la trecerea sistemului
din starea S
0
n starea S
n
, atunci pentru orice i (1 i n) d
1
, d
2
, ..., d
i
este un sir
optim de decizii care duc la trecerea sistemului din starea S
0
n starea S
i
. Astfel,
decizia d
i+1
depinde de deciziile anterioare d
1
, ..., d
i
. Spunem c a se aplica metoda
napoi.
Daca d
1
, d
2
, ..., d
n
este un sir optim de decizii care duc la trecerea sistemului
din starea S
0
n starea S
n
, atunci pentru orice i (1 i n) d
i+1
, d
i+2
, ..., d
n
este
un sir optim de decizii care duc la trecerea sistemului din starea S
i
n starea S
n
si
d
1
, d
2
, ..., d
i
este un sir optim de decizii care duc la trecerea sistemului din starea
S
0
n starea S
i
. Spunem c a se aplica metoda mixt a.
Indiferent de varianta de prezentare, rezolvarea prin programare dinamica
presupune gasirea si rezolvarea unui sistem de recurent e.

In prima variant a avem
recurent e ntre subprobleme, n a doua variant a avem recurent e n sirul de decizii.

In afara cazurilor n care recurent ele sunt evidente, este necesara si o justicare
sau o demonstrat ie a faptului c a aceste recurent e sunt cele corecte.
Deoarece rezolvarea prin recursivitate duce de cele mai multe ori la ordin de
complexitate exponent ial, se folosesc tabele auxiliare pentru ret inerea optimelor
part iale iar spat iul de solut ii se parcurge n ordinea crescatoare a dimensiunilor
subproblemelor. Folosirea acestor tabele da si numele tehnicii respective.
Aseman ator cu metoda divide et impera, programarea dinamica rezolva
problemele combin and solut iile unor subprobleme. Deosebirea const a n faptul c a
divide et impera partit ioneaza probleman subprobleme independente, le rezolv a
(de obicei recursiv) si apoi combin a solut iile lor pentru a rezolva problema initial a,
n timp ce programarea dinamica se aplica problemelor ale c aror subprobleme nu
sunt independente, ele av and sub-subprobleme comune.

In aceasta situat ie, se
rezolva ecare sub-subproblema o singur a dat a si se foloseste un tablou pentru
a memora solut ia ei, evitandu-se recalcularea ei de c ate ori subproblema reapare.
Programarea dinamica se aplica problemelor care au mai multe solut ii, ecare
din ele cu c ate o valoare, si la care se doreste obt inerea unei solut ii optime (minime
sau maxime).
Algoritmul pentru rezolvarea unei probleme folosind programarea dinamica
se dezvolta n 4 etape:
1. caracterizarea unei solut ii optime (identicarea unei modalit ati optime de
rezolvare, care satisface una dintre formele principiului optimalit at ii)
2. denirea recursiv a a valorii unei solut ii optime (dar nu se implementeaz a
recursiv)
3. calculul efectiv al valorii unei solut ii optime folosind o structur a de date
(de obicei tablou)
4. reconstituirea unei solut ii pe baza informat iei calculate.
15.2. PROBLEME REZOLVATE 213
15.2 Probleme rezolvate
15.2.1 Inmult irea optimala a matricelor
Consider am n matrice A
1
, A
2
, ..., A
n
, de dimensiuni d
0
d
1
, d
1
d
2
, ...,
d
n1
d
n
. Produsul A
1
A
2
... A
n
se poate calcula n diverse moduri, aplic and
asociativitatea operat iei de nmultire a matricelor.
Numim nmult ire elementar a nmult irea a dou a elemente.

In funct ie de modul de parantezare difer a num arul de nmult iri elementare


necesare pentru calculul produsului A
1
A
2
... A
n
.
Se cere parantezare optimal a a produsului A
1
A
2
... A
n
(pentru care
costul, adic a num arul total de nmult iri elementare, s a e minim).
Exemplu:
Pentru 3 matrice de dimensiuni (10, 1000), (1000, 10) si (10, 100), produsul
A
1
A
2
A
3
se poate calcula n dou a moduri:
1. (A
1
A
2
) A
3
necesitand 1000000+10000=1010000 nmult iri elementare
2. A
1
(A
2
A
3
), necesitand 1000000+1000000=2000000 nmult iri.
Reamintim ca num arul de nmult iri elementare necesare pentru a nmult i o
matrice A, av and n linii si m coloane, cu o matrice B, av and m linii si p coloane,
este n m p.
Solutie
1. Pentru a calcula A
1
A
2
... A
n
, n nal trebuie s a nmult im dou a matrice,
deci vom paranteza produsul astfel: (A
1
A
2
... A
k
) (A
k+1
... A
n
).
Aceasta observat ie se aplica si produselor dintre paranteze. Prin urmare,
subproblemele problemei init iale constau n determinarea parantezarii opti-
male a produselor de matrice de forma A
i
A
i+1
... A
j
, 1 i j n.
Observ am ca subproblemele nu sunt independente. De exemplu, calcularea
produsului A
i
A
i+1
...A
j
si calcularea produsului A
i+1
A
i+2
...A
j+1
,
au ca subproblema comuna calcularea produsului A
i+1
... A
j
.
2. Pentru a ret ine solut iile subproblemelor, vom utiliza o matrice M, cu n linii
si n coloane, cu semnicatia:
M[i][j] = num arul minim de nmult iri elementare necesare pentru a calcula
produsul A
i
A
i+1
... A
j
, 1 i j n.
Evident, num arul minim de nmult iri necesare pentru a calcula A
1
A
2

... A
n
este M[1][n].
214 CAPITOLUL 15. PROGRAMARE DINAMIC

A
3. Pentru ca parantezarea s a e optimal a, parantezarea produselor A
1
A
2

... A
k
si A
k+1
... A
n
trebuie sa e de asemenea optimala. Prin urmare
elementele matricei M trebuie sa satisfaca urmatoarea relat ie de recurent a:
M[i][i] = 0, i = 1, 2, ..., n.
M[i][j] = min
ik<j
M[i][k] +M[k + 1][j] +d[i 1] d[k] d[j]
Cum interpret am aceasta relat ie de recurent a? Pentru a determina num arul
minim de nmult iri elementare pentru calculul produsului A
i
A
i+1
...A
j
,
x am pozit ia de parantezare k n toate modurile posibile (ntre i si j 1), si
alegem varianta care ne conduce la minim. Pentru o pozit ie k xata, costul
parantezarii este egal cu numarul de nmult iri elementare necesare pentru
calculul produsului A
i
A
i+1
...A
k
, la care se adauga num arul denmult iri
elementare necesare pentru calculul produsului A
k+1
... A
j
si costul
nmult irii celor doua matrice rezultate (d
i1
d
k
d
j
).
Observ am ca numai jum atatea de deasupra diagonalei principale din M este
utilizat a. Pentru a construi solut ia optim a este utila si ret inerea indicelui k,
pentru care se obtine minimul. Nu vom considera un alt tablou, ci-l vom
ret ine, pe pozit ia simetric a fat a de diagonala principala (M[j][i]).
4. Rezolvarea recursiva a relat iei de recurent a este inecienta, datorit a faptu-
lui c a subproblemele se suprapun, deci o abordare recursiv a ar conduce la
rezolvarea aceleiasi subprobleme de mai multe ori. Prin urmare vom rezolva
relat ia de recurent a n mod bottom-up: (determin am parantezarea optimal a
a produselor de doua matrice, apoi de 3 matrice, 4 matrice, etc).
long M[NMax][NMax];
void dinamic()
{
int nr, i, j, k, kmin;
long min, Infinit=1000000000;
for(nr=2; nr<=n; nr++) //nr=cate matrice se inmultesc
for (i=1; i<=n-nr+1; i++)
{
j=i+nr-1;
//se inmultesc nr matrice, de la Ai la Aj
for (k=i, min=Infinit; k<j; k++)
//determin minimul si pozitia sa
if (min>M[i][k]+M[k+1][j]+d[i-1]*d[k]*d[j])
{
min=M[i][k]+M[k+1][j]+d[i-1]*d[k]*d[j];
kmin=k;
}
15.2. PROBLEME REZOLVATE 215
M[i][j]=min;
M[j][i]=kmin;
}
}
Reconstituirea solut iei optime se face foarte usor n mod recursiv, utilizand
informat iile ret inute sub diagonala principal a n matricea M:
void afisare(int i, int j)
{
//afiseaza parantezarea optimala a produsului Aix...xAj
if (i==M[j][i]) cout<<"A"<<i;
else
{
cout<<"(";
afisare(i,M[j][i]);
cout<<")";
}
cout<<"x";
if (j==M[j][i]+1) cout<<"A"<<j;
else {cout<<"("; afisare(M[j][i]+1,j); cout<<")";}
}
15.2.2 Subsir crescator maximal
Fie un sir A = (a
1
, a
2
, ..., a
n
). Numim subsir al sirului A o succesiune de
elemente din A, n ordinea n care acestea apar n A:
a
i1
, a
i2
, ..., a
i
k
, unde 1 i
1
< i
2
< ... < i
k
n.
Se cere determinarea unui subsir crescator al sirului A, de lungime maxim a.
De exemplu, pentru
A = (8, 3, 6, 50, 10, 8, 100, 30, 60, 40, 80)
o solut ie poate
(3, 6, 10, 30, 60, 80).
Rezolvare
1. Fie A
i1
= (a
i1
a
i2
... a
i
k
) cel mai lung subsir crescator al
sirului A. Observ am ca el coincide cu cel mai lung subsir crescator al sirului
(a
i1
, a
i1+1
, ..., a
n
). Evident A
i2
= (a
i2
a
i3
... a
i
k
) este cel mai lung subsir
crescator al lui (a
i2
, a
i2+1
, ..., a
n
), etc. Prin urmare, o subproblema a problemei
init iale consta n determinarea celui mai lung subsir crescator care ncepe cu a
i
,
i = 1, .., n.
216 CAPITOLUL 15. PROGRAMARE DINAMIC

A
Subproblemele nu sunt independente: pentru a determina cel mai lung subsir
crescator care ncepe cu a
i
, este necesar s a determinam cele mai lungi subsiruri
crescatoare care ncep cu a
j
, a
i
a
j
, j = i + 1, .., n.
2. Pentru a ret ine solut iile subproblemelor vom considera doi vectori l si poz,
ecare cu n componente, avand semnicat ia:
l[i] =lungimea celui mai lung subsir crescator care ncepe cu a[i];
poz[i] =pozit ia elementului care urmeaza dup a a[i] n cel mai lung subsir
crescator care ncepe cu a[i], dac a un astfel de element exista, sau 1 daca un
astfel de element nu exista.
3. Relat ia de recurent a care caracterizeaza substructura optimal a a problemei
este:
l[n] = 1; poz[n] = 1;
l[i] = max
j=i+1,n
1 +l[j][a[i] a[j]
unde poz[i] = indicele j pentru care se obt ine maximul l[i].
Rezolvam relat ia de recurent a n mod bottom-up:
int i, j;
l[n]=1;
poz[n]=-1;
for (i=n-1; i>0; i--)
for (l[i]=1, poz[i]=-1, j=i+1; j<=n; j++)
if (a[i] <= a[j] && l[i]<1+l[j])
{
l[i]=1+l[j];
poz[i]=j;
}
Pentru a determina solut ia optim a a problemei, determinam valoarea maxima
din vectorul l, apoi asam solut ia, ncepand cu pozit ia maximului si utiliz and
informat iile memorate n vectorul poz:
//determin maximul din vectorul l
int max=l[1], pozmax=1;
for (int i=2; i<=n; i++)
if (max<l[i])
{
max=l[i]; pozmax=i;
}
cout<<"Lungimea celui mai lung subsir crescator: " <<max;
cout<<"\nCel mai lung subsir:\n";
for (i=pozmax; i!=-1; i=poz[i]) cout<<a[i]<< ;
Secvent ele de program de mai sus sunt scrise n c/C++.
15.2. PROBLEME REZOLVATE 217
Programele urmatoare sunt scrise n Java:
import java.io.*;
class SubsirCrescatorNumere
{
static int n;
static int[] a;
static int[] lg;
static int[] poz;
public static void main(String []args) throws IOException
{
int i,j;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("SubsirCrescatorNumere.in")));
PrintWriter out = new PrintWriter (
new BufferedWriter( new FileWriter("SubsirCrescatorNumere.out")));
st.nextToken();n=(int)st.nval;
a=new int[n+1];
lg=new int[n+1];
poz=new int[n+1];
for(i=1;i<=n;i++) { st.nextToken(); a[i]=(int)st.nval; }
int max,jmax;
lg[n]=1;
for(i=n-1;i>=1;i--)
{
max=0;
jmax=0;
for(j=i+1;j<=n;j++)
if((a[i]<=a[j])&&(max<lg[j])) { max=lg[j]; jmax=j; }
if(max!=0) { lg[i]=1+max; poz[i]=jmax; }
else lg[i]=1;
}
max=0; jmax=0;
for(j=1;j<=n;j++)
if(max<lg[j]){ max=lg[j]; jmax=j; }
out.println(max);
int k;
j=jmax;
for(k=1;k<=max;k++) { out.print(a[j]+" "); j=poz[j]; }
out.close();
}//main
}//class
218 CAPITOLUL 15. PROGRAMARE DINAMIC

A
import java.io.*;
class SubsirCrescatorLitere
{
public static void main(String[] args) throws IOException
{
int n,i,j,jmax,k,lmax=-1,pozmax=-1;
int [] l,poz;
String a;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("SubsirCrescatorLitere.in")));
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("SubsirCrescatorLitere.out")));
st.nextToken();
a=st.sval;
n=a.length();
out.println(a+" "+n);
l=new int[n];//l[i]=lg.celui mai lung subsir care incepe cu a[i]
poz=new int[n];//poz[i]=pozitia elementului care urmeaza dupa a[i]
for(i=0;i<n;i++) poz[i]=-1;
l[n-1]=1;
poz[n-1]=-1;
for(i=n-2;i>=0;i--)
{
jmax=i;
for(j=i+1;j<n;j++)
if((a.charAt(i)<=a.charAt(j))&&(1+l[j]>1+l[jmax])) jmax=j;
l[i]=1+l[jmax];
poz[i]=jmax;
if(l[i]>lmax) { lmax=l[i]; pozmax=i; }
}
out.print("Solutia ");
k=pozmax;
out.print("("+l[pozmax]+") : ");
for(j=1;j<=lmax;j++)
{
out.print(a.charAt(k));
k=poz[k];
}
out.close();
} // main
}// class
15.2. PROBLEME REZOLVATE 219
15.2.3 Suma n triunghi
S a consideram un triunghi format din n linii (1 < n 100), ecare linie
cont in and numere ntregi din domeniul [1, 99], ca n exemplul urmator:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Tabelul 15.1: Triunghi de numere
Problema const a n scrierea unui program care sa determine cea mai mare
suma de numere aate pe un drum ntre num arul de pe prima linie si un num ar
de pe ultima linie. Fiecare num ar din acest drum este situat sub precedentul, la
stanga sau la dreapta acestuia. (IOI, Suedia 1994)
Rezolvare
1. Vom ret ine triunghiul ntr-o matrice patratica T, de ordin n, sub diagonala
principal a. Subproblemele problemei date constau n determinarea sumei maxime
care se poate obt ine din numere aate pe un drumntre numarul T[i][j], p an a la un
num ar de pe ultima linie, ecare num ar din acest drum ind situat sub precedentul,
la stanga sau la dreapta sa. Evident, subproblemele nu sunt independente: pentru
a calcula suma maxima a numerelor de pe un drum de la T[i][j] la ultima linie,
trebuie sa calcul am suma maxima a numerelor de pe un drum de la T[i + 1][j] la
ultima linie si suma maxima a numerelor de pe un drum de la T[i + 1][j + 1] la
ultima linie.
2. Pentru a ret ine solut iile subproblemelor, vom utiliza o matrice S, p atratica
de ordin n, cu semnicat ia
S[i][j] = suma maxima ce se poate obt ine pe un drum de la T[i][j] la un
element de pe ultima linie, respect and condit iile problemei.
Evident, solut ia problemei va S[1][1].
3. Relat ia de recurent a care caracterizeaza substructura optimal a a problemei
este:
S[n][i] = T[n][i], i = 1, 2, ..., n
S[i][j] = T[i][j] + maxS[i + 1][j], S[i + 1][j + 1]
4. Rezolv am relat ia de recurent a n mod bottom-up:
int i, j;
for (i=1; i<=n; i++) S[n][i]=T[n][i];
for (i=n-1; i>0; i--)
for (j=1; j<=i; j++)
220 CAPITOLUL 15. PROGRAMARE DINAMIC

A
{
S[i][j]=T[i][j]+S[i+1][j];
if (S[i+1][j]<S[i+1][j+1])
S[i][j]=T[i][j]+S[i+1][j+1]);
}
Exercit iu: Asat i si drumul n triunghi pentru care se obt ine solut ia optim a.
15.2.4 Subsir comun maximal
Fie X = (x
1
, x
2
, ..., x
n
) si Y = (y
1
, y
2
, ..., y
m
) dou a siruri de n, respectiv m
numere ntregi. Determinat i un subsir comun de lungime maxima.
Exemplu
Pentru X = (2, 5, 5, 6, 2, 8, 4, 0, 1, 3, 5, 8) si Y = (6, 2, 5, 6, 5, 5, 4, 3, 5, 8) o
solut ie posibil a este: Z = (2, 5, 5, 4, 3, 5, 8).
Solutie
1. Not am cu X
k
= (x
1
, x
2
, ..., x
k
) (prexul lui X de lungime k) si cu Y
h
=
(y
1
, y
2
, ..., y
h
) prexul lui Y de lungime h. O subproblema a problemei date consta
n determinarea celui mai lung subsir comun al lui X
k
, Y
h
. Notam cu LCS(X
k
, Y
h
)
lungimea celui mai lung subsir comun al lui X
k
, Y
h
.
Utiliz and aceste notat ii, problema cere determinarea LCS(X
n
, Y
m
), precum
si un astfel de subsir.
Observat ie
1. Dac a X
k
= Y
h
atunci
LCS(X
k
, Y
h
) = 1 +LCS(X
k1
, Y
h1
).
2. Dac a Xk ,= Y h atunci
LCS(Xk, Y h) = max(LCS(X
k1
, Y
h
), LCS(X
k
, Y
h1
)).
Din observat ia precedenta deducem c a subproblemele problemei date nu sunt
independente si ca problema are substructura optimal a.
2. Pentru a ret ine solut iile subproblemelor vom utiliza o matrice cu n+1 linii
si m+1 coloane, denumita lcs. Linia si coloana 0 sunt utilizate pentru init ializare
cu 0, iar elementul lcs[k][h] va lungimea celui mai lung subsir comun al sirurilor
X
k
si Y
h
.
3. Vom caracteriza substructura optimal a a problemei prin urm atoarea relat ie
de recurent a:
lcs[k][0] = lcs[0][h] = 0, k = 1, 2, .., n, h = 1, 2, .., m
lcs[k][h] = 1 +lcs[k 1][h 1], daca x[k] = y[h]
max lcs[k][h 1], lcs[k 1][h], daca x[k] <> y[h]
Rezolvam relat ia de recurent a n mod bottom-up:
15.2. PROBLEME REZOLVATE 221
for (int k=1; k<=n; k++)
for (int h=1; h<=m; h++)
if (x[k]==y[h])
lcs[k][h]=1+lcs[k-1][h-1];
else
if (lcs[k-1][h]>lcs[k][h-1])
lcs[k][h]=lcs[k-1][h];
else
lcs[k][h]=lcs[k][h-1];
Deoarece nu am utilizat o structur a de date suplimentar a cu ajutorul c areia
sa memoram solut ia optim a, vom reconstitui solut ia optim a pe baza rezultatelor
memorate n matricea lcs. Prin reconstituire vom obt ine solut ia n ordine invers a,
din acest motiv vom memora solutia ntr-un vector, pe care l vom asa de la
sfarsit catre nceput:
cout<<"Lungimea subsirului comun maximal: " <<lcs[n][m];
int d[100];
cout<<"\nCel mai lung subsir comun este: \n";
for (int i=0, k=n, h=m; lcs[k][h]; )
if (x[k]==y[h])
{
d[i++]=x[k];
k--;
h--;
}
else
if (lcs[k][h]==lcs[k-1][h])
k--;
else
h--;
for (k=i-1;k>=0; k--)
cout<< d[k] << ;
Secvent ele de cod de mai sus sunt scrise n C/C++. Programul urm ator este
scris n Java si determin a toate solut iile. Sunt determinate cea mai mica si cea mai
mare solut ie, n ordine lexicograca. De asemenea sunt asate matricele auxiliare
de lucru pentru a se putea urm arii mai usor modalitatea de determinare recursiv a
a tuturor solut iilor.
import java.io.*; // SubsirComunMaximal
class scm
{
static PrintWriter out;
static int [][] a;
222 CAPITOLUL 15. PROGRAMARE DINAMIC

A
static char [][] d;
static String x,y;
static char[] z,zmin,zmax;
static int nsol=0,n,m;
static final char sus=|, stanga=-, diag=*;
public static void main(String[] args) throws IOException
{
citire();
n=x.length();
m=y.length();
out=new PrintWriter(new BufferedWriter( new FileWriter("scm.out")));
int i,j;
matrad();
out.println(a[n][m]);
afism(a);
afism(d);
z=new char[a[n][m]+1];
zmin=new char[z.length];
zmax=new char[z.length];
System.out.println("O solutie oarecare");
osol(n,m);// o solutie
System.out.println("\nToate solutiile");
toatesol(n,m,a[n][m]);// toate solutiile
out.println(nsol);
System.out.print("SOLmin = ");
afisv(zmin);
System.out.print("SOLmax = ");
afisv(zmax);
out.close();
System.out.println("\n... Gata !!! ...");
}
static void citire() throws IOException
{
BufferedReader br=new BufferedReader(new FileReader("scm.in"));
x=br.readLine();
y=br.readLine();
}
15.2. PROBLEME REZOLVATE 223
static void matrad()
{
int i,j;
a=new int[n+1][m+1];
d=new char[n+1][m+1];
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(x.charAt(i-1)==y.charAt(j-1)) // x.charAt(i)==y.charAt(j) !
{
a[i][j]=1+a[i-1][j-1];
d[i][j]=diag;
}
else
{
a[i][j]=max(a[i-1][j],a[i][j-1]);
if(a[i-1][j]>a[i][j-1]) d[i][j]=sus; else d[i][j]=stanga;
}
}
static void osol(int lin, int col)
{
if((lin==0)||(col==0)) return;
if(d[lin][col]==diag)
osol(lin-1,col-1);
else
if(d[lin][col]==sus)
osol(lin-1,col);
else osol(lin,col-1);
if(d[lin][col]==diag) System.out.print(x.charAt(lin-1));
}
static void toatesol(int lin, int col,int k)
{
int i,j;
if(k==0)
{
System.out.print(++nsol+" ");
afisv(z);
zminmax();
return;
}
i=lin+1;
224 CAPITOLUL 15. PROGRAMARE DINAMIC

A
while(a[i-1][col]==k)//merg in sus
{
i--;
j=col+1;
while(a[i][j-1]==k) j--;
if( (a[i][j-1]==k-1)&&(a[i-1][j-1]==k-1)&&(a[i-1][j]==k-1))
{
z[k]=x.charAt(i-1);
toatesol(i-1,j-1,k-1);
}
}//while
}
static void zminmax()
{
if(nsol==1)
{
copiez(z,zmin);
copiez(z,zmax);
}
else
if(compar(z,zmin)<0)
copiez(z,zmin);
else
if(compar(z,zmax)>0) copiez(z,zmax);
}
static int compar(char[] z1, char[] z2)//-1=<; 0=identice; 1=>
{
int i=1;
while(z1[i]==z2[i]) i++;// z1 si z2 au n componente 1..n
if(i>n)
return 0;
else
if(z1[i]<z2[i])
return -1;
else return 1;
}
static void copiez(char[] z1, char[] z2)
{
int i;
for(i=1;i<z1.length;i++) z2[i]=z1[i];
}
15.2. PROBLEME REZOLVATE 225
static int max(int a, int b)
{
if(a>b) return a; else return b;
}
static void afism(int[][]a)// difera tipul parametrului !!!
{
int n=a.length;
int i,j,m;
m=y.length();
System.out.print(" ");
for(j=0;j<m;j++) System.out.print(y.charAt(j)+" ");
System.out.println();
System.out.print(" ");
for(j=0;j<=m;j++) System.out.print(a[0][j]+" ");
System.out.println();
for(i=1;i<n;i++)
{
System.out.print(x.charAt(i-1)+" ");
m=a[i].length;
for(j=0;j<m;j++) System.out.print(a[i][j]+" ");
System.out.println();
}
System.out.println("\n");
}
static void afism(char[][]d)// difera tipul parametrului !!!
{
int n=d.length;
int i,j,m;
m=y.length();
System.out.print(" ");
for(j=0;j<m;j++) System.out.print(y.charAt(j)+" ");
System.out.println();
System.out.print(" ");
for(j=0;j<=m;j++) System.out.print(d[0][j]+" ");
System.out.println();
226 CAPITOLUL 15. PROGRAMARE DINAMIC

A
for(i=1;i<n;i++)
{
System.out.print(x.charAt(i-1)+" ");
m=d[i].length;
for(j=0;j<m;j++) System.out.print(d[i][j]+" ");
System.out.println();
}
System.out.println("\n");
}
static void afisv(char[]v)
{
int i;
for(i=1;i<=v.length-1;i++) System.out.print(v[i]);
for(i=1;i<=v.length-1;i++) out.print(v[i]);
System.out.println();
out.println();
}
}
Pe ecran apar urm atoarele rezultate:
1 3 2 4 6 5 a c b d f e
0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 1 1 1 1 1 1 1 1 1 1 1 1
2 0 1 1 2 2 2 2 2 2 2 2 2 2
3 0 1 2 2 2 2 2 2 2 2 2 2 2
4 0 1 2 2 3 3 3 3 3 3 3 3 3
5 0 1 2 2 3 3 4 4 4 4 4 4 4
6 0 1 2 2 3 4 4 4 4 4 4 4 4
a 0 1 2 2 3 4 4 5 5 5 5 5 5
b 0 1 2 2 3 4 4 5 5 6 6 6 6
c 0 1 2 2 3 4 4 5 6 6 6 6 6
d 0 1 2 2 3 4 4 5 6 6 7 7 7
e 0 1 2 2 3 4 4 5 6 6 7 7 8
f 0 1 2 2 3 4 4 5 6 6 7 8 8
1 3 2 4 6 5 a c b d f e
1 * - - - - - - - - - - -
2 | - * - - - - - - - - -
3 | * - - - - - - - - - -
15.2. PROBLEME REZOLVATE 227
4 | | - * - - - - - - - -
5 | | - | - * - - - - - -
6 | | - | * - - - - - - -
a | | - | | - * - - - - -
b | | - | | - | - * - - -
c | | - | | - | * - - - -
d | | - | | - | | - * - -
e | | - | | - | | - | - *
f | | - | | - | | - | * -
O solutie oarecare
1346acdf
Toate solutiile
1 1346acdf
2 1246acdf
3 1345acdf
4 1245acdf
5 1346abdf
6 1246abdf
7 1345abdf
8 1245abdf
9 1346acde
10 1246acde
11 1345acde
12 1245acde
13 1346abde
14 1246abde
15 1345abde
16 1245abde
SOLmin = 1245abde
SOLmax = 1346acdf
... Gata !!! ...
Press any key to continue...
228 CAPITOLUL 15. PROGRAMARE DINAMIC

A
Capitolul 16
Probleme
16.1 Probleme rezolvate
16.1.1 Poarta - OJI 2002
Se consider a harta universului ca ind o matrice cu 250 de linii si 250 de
coloane.

In ecare celul a se gaseste o asa numit a poart a stelara, iar n anumite
celule se gasesc echipaje ale port ii stelare.
La o deplasare, un echipaj se poate deplasa din locul n care se aa n oricare
alt loc n care se gaseste o a doua poart a, n cazul nostru n orice alt a pozit ie din
matrice.
Nu se permite situarea simultan a a mai mult de un echipaj ntr-o celul a. La
un moment dat un singur echipaj se poate deplasa de la o poart a stelara la alta.
Cerint a
D andu-se un num ar p (1 < p < 5000) de echipaje, pentru ecare echipaj ind
precizate pozit ia init ial a si pozit ia nal a, determinat i num arul minim de deplas ari
necesare pentru ca toate echipajele sa ajung a din pozit ia init ial a n cea nal a.
Datele de intrare
Se citesc din sierul text poarta.in n urm atorul format:
pe prima linie num arul natural p reprezentand num arul de echipaje,
pe urm atoarele p linii c ate 4 numere naturale, primele dou a reprezentand
coordonatele pozit iei init iale a unui echipaj (linie coloan a), urm atoarele doua
reprezentand coordonatele pozit iei nale a aceluiasi echipaj (linie coloan a).
Datele de iesire
Pe prima linie a sierului text poarta.out se scrie un singur numar reprezentand
num arul minim de deplas ari necesar.
229
230 CAPITOLUL 16. PROBLEME
Restrict ii si precizari
coordonatele pozit iilor init iale si nale ale echipajelor sunt numere naturale
din intervalul [1, 250];
pozit iile init iale ale celor p echipaje sunt distincte doua cate dou a;
pozit iile nale ale celor p echipaje sunt distincte doua cate dou a.
Exemplu
poarta.in poarta.out
3 4
1 2 3 4
6 5 3 9
3 4 1 2
Timp maxim de executare: 1 secunda/test
import java.io.*; // NrMinDeplasari=p+NrCicluri-NrStationare
class poarta
{
static int p,nmd,nc=0,ns=0;
static int[] xi,yi,xf,yf;
static boolean[] ea; // EsteAnalizat deja
public static void main(String[] args) throws IOException
{
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("poarta10.in")));
st.nextToken(); p=(int)st.nval;
xi=new int[p+1];
yi=new int[p+1];
xf=new int[p+1];
yf=new int[p+1];
ea=new boolean[p+1]; // EchipajAnalizat FALSE !!!
int i;
for(i=1;i<=p;i++)
{
st.nextToken(); xi[i]=(int)st.nval;
16.1. PROBLEME REZOLVATE 231
st.nextToken(); yi[i]=(int)st.nval;
st.nextToken(); xf[i]=(int)st.nval;
st.nextToken(); yf[i]=(int)st.nval;
}
for(i=1;i<=p;i++)
{
if(ea[i]) continue;
if((xf[i]==xi[i])&&(yf[i]==yi[i])) { ea[i]=true; ns++;}
else if(ciclu(i)) nc++;
}
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("poarta.out")));
nmd=p+nc-ns;
System.out.println(p+" "+nc+" "+ns+" "+nmd);
out.print(nmd);
out.close();
}
static boolean ciclu(int i)
{
int j=succesor(i);
while((j!=-1)&&(j!=i))
{
ea[j]=true;
j=succesor(j);
}
if(j==i) return true; else return false;
}
static int succesor(int j) // j --> k
{
int k;
for(k=1;k<=p;k++)
if((xf[j]==xi[k])&&(yf[j]==yi[k])) return k;
return -1;
}
}
16.1.2 Mouse - OJI 2002
Un experiment urmareste comportarea unui soricel pus ntr-o cutie dreptun-
ghiular a, mp art ita n m n camarut e egale de forma p atrat a. Fiecare c amarut a
cont ine o anumit a cantitate de hran a.
232 CAPITOLUL 16. PROBLEME
Soricelul trebuie sa porneasca din colt ul (1, 1) al cutiei si sa ajung a n colt ul
opus, m anc and c at mai mult a hran a. El poate trece dintr-o camer an una al aturat a
(dou a camere sunt alaturate dac a au un perete comun), m an anca toat a hrana din
camarut a atunci c and intr a si nu intr a niciodata ntr-o camer a f ar a hran a.
Cerint a
Stabilit i care este cantitatea maxima de hran a pe care o poate manca si
traseul pe care l poate urma pentru a culege aceast a cantitate maxima.
Datele de intrare
Fisierul de intrare mouse.in cont ine pe prima linie dou a numere m si n
reprezentand num arul de linii respectiv num arul de coloane ale cutiei, iar pe
urm atoarele m linii cele m n numere reprezentand cantitatea de hran a exis-
tent a n ecare camarut a, cate n numere pe ecare linie, separate prin spat ii.
Toate valorile din sier sunt numere naturale ntre 1 si 100.
Datele de iesire

In sierul de iesire mouse.out se vor scrie


pe prima linie dou a numere separate printr-un spat iu: num arul de c amarut e
vizitate si cantitatea de hrana maxima culeasa;
pe urm atoarele linii un traseu posibil pentru cantitatea dat a, sub form a de
perechi de numere (linie coloan a) ncepand cu 1 1 si termin and cu m n.
Exemplu
mouse.in mouse.out
2 4 7 21
1 2 6 3 1 1
3 4 1 2 2 1
2 2
1 2
1 3
1 4
2 4
Explicat ie
Timp maxim de executare: 1 secunda/test
import java.io.*;
class Mouse
16.1. PROBLEME REZOLVATE 233
{
static int m,n,imin,jmin,min,s;
static int [][]a;
static PrintWriter out;
public static void main(String[] args) throws IOException
{
int i,j;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("mouse4.in")));
out=new PrintWriter(new BufferedWriter(new FileWriter("mouse.out")));
st.nextToken();m=(int)st.nval;
st.nextToken();n=(int)st.nval;
a=new int[m+1][n+1];
for(i=1;i<=m;i++)
for(j=1;j<=n;j++) {st.nextToken(); a[i][j]=(int)st.nval;}
s=0;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++) s=s+a[i][j];
if(m%2==1) mimpar();
else if(n%2==1) nimpar();
else mnpare();
out.close();
}//main
static void mimpar()
{
int i,j;
out.println(m*n+" "+s);
i=1;
while(i+1<m)
{
for(j=1;j<=n;j++) out.println(i+" "+j);
i++;
for(j=n;j>=1;j--) out.println(i+" "+j);
i++;
}
for(j=1;j<=n;j++) out.println(m+" "+j);
}
234 CAPITOLUL 16. PROBLEME
static void nimpar()
{
int i,j;
j=1;
out.println(m*n+" "+s);
while(j+1<n)
{
for(i=1;i<=m;i++) out.println(i+" "+j);
j++;
for(i=m;i>=1;i--) out.println(i+" "+j);
j++;
}
for(i=1;i<=m;i++) out.println(i+" "+n);
}
static void mnpare()
{
int i,j;
imin=0;jmin=0;min=101;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
if((i+j)%2==1)
if(a[i][j]<min) { min=a[i][j]; imin=i; jmin=j; }
out.println((m*n-1)+" "+(s-a[imin][jmin]));
j=1;
while(j+1<jmin) // stanga
{
for(i=1;i<=m;i++) out.println(i+" "+j);
j++;
for(i=m;i>=1;i--) out.println(i+" "+j);
j++;
}
i=1;
while(i+1<imin) // sus
{
out.println(i+" " +j);
out.println(i+" " +(j+1));
out.println((i+1)+" " +(j+1));
out.println((i+1)+" " +j);
i=i+2;
}
16.1. PROBLEME REZOLVATE 235
out.println(i+" "+j); // patratel
if((i==imin)&&(j+1==jmin)) out.println((i+1)+" " +j);
else out.println(i+" " +(j+1));
out.println((i+1)+" " +(j+1));
i=i+2;
while (i<m) // jos
{
out.println(i+" " +(j+1));
out.println(i+" " +j);
out.println((i+1)+" " +j);
out.println((i+1)+" " +(j+1));
i=i+2;
}
j=j+2;
while(j+1<=n) // dreapta
{
for(i=m;i>=1;i--) out.println(i+" "+j);
j++;
for(i=1;i<=m;i++) out.println(i+" "+j);
j++;
}
}
}//class
16.1.3 Numere - OJI 2003
Gigel este un mare pasionat al cifrelor. Orice moment liber si-l petrece juc andu-
se cu numere.
Juc andu-se astfel, ntr-o zi a scris pe hartie 10 numere distincte de c ate dou a
cifre si a observat ca printre acestea exist a dou a submult imi disjuncte de suma
egal a.
Desigur, Gigel a crezut c a este o nt amplare si a scris alte 10 numere dis-
tincte de cate dou a cifre si spre surpriza lui, dup a un timp a g asit din nou dou a
submult imi disjuncte de suma egala.
Cerint a
Date 10 numere distincte de cate dou a cifre, determinat i num arul de perechi
de submult imi disjuncte de suma egala care se pot forma cu numere din cele
date, precum si una dintre aceste perechi pentru care suma numerelor din ecare
dintre cele dou a submult imi este maxima.
Date de intrare
236 CAPITOLUL 16. PROBLEME
Fisierul de intrare numere.in cont ine pe prima linie 10 numere naturale
distincte separate prin c ate un spat iu x
1
x
2
... x
10
.
Date de iesire
Fisierul de iesire numere.out cont ine trei linii. Pe prima linie se a a num arul
de perechi de submult imi de suma egala, precum si suma maxima obt inut a, sepa-
rate printr-un spat iu. Pe linia a doua se a a elementele primei submult imi, iar pe
linia a treia se a a elementele celei de a doua submult imi, separate prin cate un
spat iu.
NrSol Smax NrSol - num arul de perechi; Smax - suma maxima
x
1
... x
k
elementele primei submult imi
y
1
... y
p
elementele celei de a doua submult imi
Restrict ii si precizari
10 x
i
, y
i
99, pentru 1 i 10.
1 k, p 9.
Ordinea submult imilor n perechi nu conteaz a.
Perechea de submult imi determinat a nu este obligatoriu unica.
Exemplu
numere.in numere.out
60 49 86 78 23 97 69 71 32 10 130 276
78 97 69 32
60 49 86 71 10
Explicat ie:
130 de solut ii; suma maxima este 276; s-au folosit 9 din cele 10 numere; prima
submult ime are 4 elemente, a doua are 5 elemente.
Timp maxim de executare: 1 secunda/test
import java.io.*;
class Numere
{
static int[] x=new int[10];
static PrintWriter out;
public static void main(String[] args) throws IOException
{
int i, ia, ib, nsol=0, smax=-1, iamax=123,ibmax=123, sumaia=-1;
long t1,t2;
t1=System.currentTimeMillis();
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("numere.in")));
out=new PrintWriter(new BufferedWriter(new FileWriter("numere.out")));
for(i=0;i<10;i++) {st.nextToken(); x[i]=(int)st.nval;}
for(ia=1;ia<=1022;ia++)
16.1. PROBLEME REZOLVATE 237
for(ib=ia+1;ib<=1022;ib++) // fara ordine intre A si B
// for(ib=1;ib<=1022;ib++) // cu ordine intre A si B
// if(disjuncte(ia,ib))
if((ia&ib)==0) // 230,420 -> 80
{
//sumaia=suma(ia); // nu se castiga acum, dar ... !!!
if(suma(ia)==suma(ib)) // apel multiplu pentru suma(ia)
if(sumaia==suma(ib))
{
nsol++;
if(suma(ia)>smax)
//if(sumaia>smax)
{
smax=suma(ia);
//smax=sumaia;
iamax=ia;
ibmax=ib;
}
}
}
out.println(nsol+" "+smax);
afis(iamax);
afis(ibmax);
out.close();
t2=System.currentTimeMillis();
System.out.println(t2-t1);
}// main
static boolean disjuncte(int ia, int ib) // ia & ib == 0
{
int s=0;
while((ia!=0)&&(ib!=0))
{
if((ia%2)*(ib%2)==1) s++; // 420 milisecunde
// if((ia%2)*(ib%2)==1) { s++; break;} // 230 milisecunde !!!
ia/=2;
ib/=2;
}
if(s>0) return false; else return true;
}
static int suma(int i)
{
int s=0,k=0;
238 CAPITOLUL 16. PROBLEME
for(k=0;k<=9;k++) if( (i&(1<<k)) != 0 ) s+=x[k];
return s;
}
static void afis(int i)
{
int k=0;
while(i!=0)
{
if(i%2!=0) out.print(x[k]+" ");
k++;
i/=2;
}
out.println();
}
}// class
16.1.4 Expresie - OJI 2004
Se d a un sir de n numere naturale nenule x
1
, x
2
, ..., x
n
si un num ar natural m.
Cerint a
S a se verice daca valoarea expresiei
m

x
1
...x
n
exte un numar natural.

In caz armativ s a se aseze acest numar descompus n factori primi.


Date de intrare

In sierul exp.in se aa pe prima linie m, pe linia a doua n, iar pe linia a


treia numerele x
1
, x
2
, ..., x
n
separate ntre ele prin c ate un spat iu.
Date de iesire

In sierul exp.out se va scrie pe prima linie cifra 0, daca valoarea expresiei


nu este un num ar natural, respectiv 1 dac a este un num ar natural. Dac a valoarea
expresiei este un numar natural pe urm atoarele linii se vor scrie perechi de forma p
e (p este factor prim care apare n descompunere la puterea e 1). Aceste perechi
se vor scrie n ordine crescatoare dup a primul num ar (adic a p).
Restrict ii si precizari
n - num ar natural nenul < 5000
x
i
- num ar natural nenul < 30000, i 1, 2, ..., n
m - poate una din cifrele 2, 3, 4
Exemple
16.1. PROBLEME REZOLVATE 239
exp.in exp.out exp.in exp.out
2 0 2 1
4 4 2 4
32 81 100 19 32 81 100 18 3 3
5 1
Timp maxim de execut ie: 1 secunda/test
import java.io.*;
class expresie
{
static int[] p=new int[30000];
static int m,n;
public static void main(String[] args) throws IOException
{
int i;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("expresie.in")));
st.nextToken(); m=(int)st.nval;
st.nextToken(); n=(int)st.nval;
int[] x=new int[n+1];
for(i=1;i<=n;i++) { st.nextToken(); x[i]=(int)st.nval; }
for(i=1;i<=n;i++) descfact(x[i]);
int ok=1;
for (i=2;i<30000;i++)
if (p[i]%m==0) p[i]=p[i]/m;
else { ok=0; break; }
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("expresie.out")));
if(ok==0) out.println(0);
else
{
out.println(1);
for(i=2;i<30000;i++)
if(p[i]!=0) out.println(i+" "+p[i]);
}
out.close();
}
static void descfact(int nr)
{
int d=2;
while(nr%d==0)
{
240 CAPITOLUL 16. PROBLEME
p[d]++;
nr=nr/d;
}
d=3;
while(d<=nr)
{
while(nr%d==0) { p[d]++; nr=nr/d; }
d=d+2;
}
}
}
16.1.5 Reactivi - OJI 2004

Intr-un laborator de analize chimice se utilizeaz a N reactivi.


Se stie ca, pentru a evita accidentele sau deprecierea reactivilor, acestia tre-
buie s a e stocat i n condit ii de mediu speciale. Mai exact, pentru ecare reactiv x,
se precizeaza intervalul de temperatur a [min
x
, max
x
] n care trebuie s a sencadreze
temperatura de stocare a acestuia.
Reactivii vor plasat i n frigidere.
Orice frigider are un dispozitiv cu ajutorul c aruia putem stabili temperatura
(constant a) care va n interiorul acelui frigider (exprimat a ntr-un num ar ntreg
de grade Celsius).
Cerint a
Scriet i un program care s a determine numarul minim de frigidere necesare
pentru stocarea reactivilor chimici.
Date de intrare
Fisierul de intrare react.in cont ine:
pe prima linie num arul natural N, care reprezint a num arul de reactivi;
pe ecare dintre urm atoarele N linii se a a min max (dou a numere ntregi
separate printr-un spat iu); numerele de pe linia x + 1 reprezint a temperatura
minima, respectiv temperatura maxim a de stocare a reactivului x.
Date de iesire
Fisierul de iesire react.out va cont ine o singur a linie pe care este scris
num arul minim de frigidere necesar.
Restrict ii si precizari
1 N 8000
100 min
x
max
x
100 (numere ntregi, reprezentand grade Celsius),
pentru orice x de la 1 la N
un frigider poate cont ine un num ar nelimitat de reactivi
Exemple
16.1. PROBLEME REZOLVATE 241
react.in react.out react.in react.out react.in react.out
3 2 4 3 5 2
-10 10 2 5 -10 10
- 2 5 5 7 10 12
20 50 10 20 -20 10
30 40 7 10
7 8
Timp maxim de execut ie: 1 secunda/test
import java.io.*;
class Reactivi
{
static int n; // n=nr reactivi
static int ni=0; // ni=nr interschimbari in quickSort
static int nf=0; // n=nr frigidere
static int[] ngf; // ng=nr grade in frigider
static int[] x1,x2; // limite inferioara/superioara
public static void main(String[] args) throws IOException
{
int i,j;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("Reactivi.in")));
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("Reactivi.out")));
st.nextToken(); n=(int)st.nval;
x1=new int[n+1];
x2=new int[n+1];
ngf=new int[n+1];
for(i=1;i<=n;i++)
{
st.nextToken(); x1[i]=(int)st.nval;
st.nextToken(); x2[i]=(int)st.nval;
}
sol();
out.println(nf);
out.close();
}// main
static void sol()
{
int i;
quickSort(1,n);
i=1; nf=1; ngf[nf]=x2[i];
242 CAPITOLUL 16. PROBLEME
i++;
while(i<n)
{
while((i<=n)&&(x1[i]<=ngf[nf])) i++;
if(i<n) ngf[++nf]=x2[i++];
}
}
static void quickSort(int p, int u)
{
int i,j,aux;
i=p; j=u;
while(i<j)
{
while((i<j)&&((x2[i]<x2[j])||
((x2[i]==x2[j])&&(x1[i]>=x1[j])))) i++;
if(i!=j)
{
aux=x1[i]; x1[i]=x1[j]; x1[j]=aux;
aux=x2[i]; x2[i]=x2[j]; x2[j]=aux;
}
while((i<j)&&((x2[i]<x2[j])||
((x2[i]==x2[j])&&(x1[i]>=x1[j])))) j--;
if(i!=j)
{
aux=x1[i]; x1[i]=x1[j]; x1[j]=aux;
aux=x2[i]; x2[i]=x2[j]; x2[j]=aux;
}
}
if(p<i-1) quickSort(p,i-1);
if(i+1<u) quickSort(i+1,u);
}
}
16.1.6 MaxD - OJI 2005
Maria si Adrian Nit a
Fiind elev n clasa a IX-a, George, si propune s a studieze capitolul divizibi-
litate cat mai bine. Ajung and la num arul de divizori asociat unui num a natural,
constata ca sunt numere ntr-un interval dat, cu acelasi num ar de divizori.
De exemplu, n intervalul [1, 10], 6, 8 si 10 au acelasi num ar de divizori, egal
cu 4. De asemenea, 4 si 9 au acelasi num ar de divizori, egal cu 3 etc.
Cerint a
16.1. PROBLEME REZOLVATE 243
Scriet i un program care pentru un interval dat determin a care este cel mai
mic numar din interval ce are num ar maxim de divizori. Daca sunt mai multe
numere cu aceast a proprietate se cere sa se numere cate sunt.
Date de intrare
Fisierul de intrare maxd.in cont ine pe prima linie dou a numere a si b sepa-
rate prin spat iu (a b) reprezentand extremit at ile intervalului.
Date de iesire
Fisierul de iesire maxd.out va cont ine pe prima linie trei numere separate
prin c ate un spat iu min nrdiv contor cu semnicat ia:
min = cea mai mica valoare din interval care are num ar maxim de divizori
nrdiv = num arul de divizori ai lui min
contor = cate numere din intervalul citit mai au acelasi num ar de divizori
egal cu nrdiv
Restrict ii si precizari
1 a b 1.000.000.000
0 b a 10.000
Punctaj
Daca at i determinat corect min, obt inet i 50% din punctaj.
Daca at i determinat corect nrdiv, obt inet i 20% din punctaj
Daca at i determinat corect contor, obt inet i 30% din punctaj.
Exemple
maxd.in maxd.out Explicat ie
200 200 200 12 1 200 are 12 divizori iar in intervalul [200, 200]
exista un singur num ar cu aceast a proprietate
maxd.in maxd.out Explicat ie
2 10 6 4 3 6 este cel mai mic numar din interval care are
maxim de divizori egal cu 4 si sunt 3 astfel de
numere 6, 8, 10
Timp maxim de execut ie: 1 sec/test
import java.io.*; //timp = 5600
class expresie
{
static int[] x;
static int a,b;
public static void main(String[] args) throws IOException
{
int i;
long t1,t2;
t1=System.currentTimeMillis();
244 CAPITOLUL 16. PROBLEME
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("maxd.in")));
st.nextToken(); a=(int)st.nval;
st.nextToken(); b=(int)st.nval;
x=new int[b-a+2];
for(i=a;i<=b;i++) x[i-a+1]=descfact(i);
int max=-1,n;
int imax=-1;
for(i=1;i<=b-a+1;i++)
if(x[i]>max) { max=x[i]; imax=i; }
int nrmax=0;
for(i=1;i<=b-a+1;i++) if(x[i]==max) nrmax++;
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("maxd.out")));
out.println((imax+a-1)+" "+max+" "+nrmax);
out.close();
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1));
}
static int descfact(int nr)
{
int d;
int nrd;
int p=1;
d=2;
nrd=0;
while(nr%d==0) { nrd++; nr=nr/d; }
p=p*(nrd+1);
d=3;
while(d*d<=nr)
{
nrd=0;
while(nr%d==0) { nrd++; nr=nr/d; }
p=p*(nrd+1);
d=d+2;
}
if(nr>1) p*=2;
return p;
}
}
16.1. PROBLEME REZOLVATE 245
16.1.7 Fract ii - ONI 2001
prof. Ovidiu Domsa, Alba Iulia
O proprietate interesanta a fract iilor ireductibile este ca orice fract ie se poate
obt ine dup a urm atoarele reguli:
pe primul nivel se a a fract ia 1/1;
pe al doilea nivel, n st anga fract iei 1/1 de pe primul nivel, plas am fract ia
1/2 iar n dreapta ei fract ia 2/1;
nivelul 1: 1/1
nivelul 2: 1/2 2/1
pe ecare nivel k se plaseaza sub ecare fract ie i/j de pe nivelul de deasupra,
fract ia i/(i +j) n stanga, iar fract ia (i +j)/j n dreapta.
nivelul 1: 1/1
nivelul 2: 1/2 2/1
nivelul 3: 1/3 3/2 2/3 3/1
Cerint a
D andu-se o fract ie oarecare prin num ar atorul si numitorul s au, determinat i
num arul nivelului pe care se a a fract ia sau o fract ie echivalenta (av and aceeasi
valoare) cu aceasta.
Date de intrare
Fisier de intrare: FRACTII.IN
Linia 1: N M - dou a numere naturale nenule, separate printr-un spat iu,
reprezentand num ar atorul si numitorul unei fract ii.
Date de iesire
Fisier de iesire: FRACTII.OUT
Linia 1: niv - num ar natural nenul, reprezent and num arul nivelului care
corespunde fract iei.
Restrict ii
1 < N, M 2.000.000.000
Exemple
FRACTII.IN FRACTII.OUT FRACTII.IN FRACTII.OUT
13 8 6 12 8 3
Timp maxim de execut ie: 1 secunda/test
import java.io.*;
class Fractii
{
public static void main(String[] args) throws IOException
{
int n,m,k,d;
246 CAPITOLUL 16. PROBLEME
StreamTokenizer st=new StreamTokenizer(new BufferedReader(
new FileReader("fractii.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(
new FileWriter("fractii.out")));
st.nextToken();n=(int)st.nval;
st.nextToken();m=(int)st.nval;
k=0;
d=cmmdc(n,m);
n=n/d;
m=m/d;
while((n!=1)||(m!=1))
{
k++;
if(n>m) n=n-m; else m=m-n;
}
k++;
out.println(k);
out.close();
}
static int cmmdc(int a, int b)
{
int d,i,c,r;
if(a>b){d=a; i=b;} else {d=b; i=a;}
while(i!=0)
{
c=d/i;
r=d%i;
d=i;
i=r;
}
return d;
}
}
16.1.8 Competit ie dicila - ONI 2001
Angel Proorocu, Bucuresti
La o competit ie au participat N concurent i. Fiecare dintre ei a primit un
num ar de concurs astfel nc at sa nu existe concurent i cu acelasi num ar.
Numerele de concurs apart in mult imii 1, 2, ..., N.
Din p acate, clasamentul nal a fost pierdut, iar comisiasi poate aduce aminte
doar cateva relat ii ntre unii participant i (de genul participantul cu num arul 3 a
iesit naintea celui cu numarul 5).
16.1. PROBLEME REZOLVATE 247
Cerint a
Seful comisiei are nevoie de un clasament nal si va cere sa-l ajutat i deter-
min and primul clasament n ordine lexicograca ce respecta relat iile pe care si le
aminteste comisia.
Date de intrare
Fisier de intrare: COMPET.IN
Linia 1: N M - dou a numere naturale nenule, reprezentand num arul concurent ilor,
respectiv numarul relat iilor pe care si le aminteste comisia;
Liniile 2..M+1: i j - pe ecare din aceste M linii se a a cate dou a numere
naturale nenule i si j, av and semnicat ia: concurentul cu num arul de concurs i a
fost n clasament naintea concurentului cu num arul de concurs j.
Date de iesire
Fisier de iesire: COMPET.OUT
Linia 1: nr
1
nr
2
... nr
N
- pe aceasta linie se va scrie clasamentul sub forma
unui sir de numere naturale nenule, separate prin c ate un spat iu, reprezentand
numerele de concurs ale concurent ilor, n ordine de la primul clasat la ultimul.
Restrict ii si precizari
1 < N 1000
se garanteaza corectitudinea datelor de intrare si faptul c a exista totdeauna
o solut ie.
Exemple
COMPET.IN COMPET.OUT COMPET.IN COMPET.OUT
3 1 1 2 3 4 2 2 1 3 4
1 2 2 1
3 4
Timp maxim de execut ie: 1 secunda/test
import java.io.*;
class competitie
{
static int n,m;
static int[] a;
static int[] b;
static int[] c;
static int[] inainte;
static boolean[] finalizat;
public static void main(String[] args) throws IOException
{
int i,j,igasit=-1;
StreamTokenizer st=new StreamTokenizer(new BufferedReader(
248 CAPITOLUL 16. PROBLEME
new FileReader("competitie.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(
new FileWriter("competitie.out")));
st.nextToken();n=(int)st.nval;
st.nextToken();m=(int)st.nval;
a=new int[m+1];
b=new int[n+1];
c=new int[n+1]; // solutia
inainte=new int[n+1];
finalizat=new boolean[n+1];
for(i=1;i<=m;i++)
{
st.nextToken();a[i]=(int)st.nval; // a[i] < b[i]
st.nextToken();b[i]=(int)st.nval;
}
for(i=1;i<=m;i++) inainte[b[i]]++;
for(j=1;j<=n;j++)
{
for(i=1;i<=n;i++)
if((!finalizat[i])&&(inainte[i]==0))
{
finalizat[i]=true;
c[j]=i;
igasit=i;
break;
}
for(i=1;i<=m;i++)
if(a[i]==igasit) inainte[b[i]]--;
}
for(i=1;i<=n;i++) out.print(c[i]+" ");
out.close();
}
}
16.1.9 Pentagon - ONI 2002
lect. univ. Ovidiu Domsa, Alba Iulia

In urma bombardamentelor din 11 septembrie 2001, cladirea Pentagonului a


suferit daune la unul din peret ii cladirii. Imaginea codicat a a peretelui avariat se
reprezint a sub forma unei matrice cu m linii si n coloane, ca n gura de mai jos:
1110000111 unde 1 reprezinta zid intact
16.1. PROBLEME REZOLVATE 249
1100001111 0 zid avariat
1000000011
1111101111
1110000111
Sumele alocate de Bin Laden pentru refacerea Pentagonului vor donate celor
care vor ajuta americanii sa refaca aceasta cladire prin plasarea, pe vertical a, a
unor blocuri de n alt imi k, k = 1, ..., m, si l at ime 1, n locurile avariate.
Cerint a:
Pentru o structur a dat a a unui perete din cl adirea Pentagonului, determinat i
num arul minim al blocurilor, de n alt imi k = 1, k = 2, ..., k = m, necesare refacerii
cladirii.
Date de intrare:
Fisierul de intrare PENTAGON.IN cont ine pe prima linie dimensiunile m si
n ale peretelui cladirii, iar pe urm atoarele m linii c ate o secvent a de caractere 1
sau 0 de lungime n.
Date de iesire:
Fisierul PENTAGON.OUT va cont ine pe cate o linie, ordonate cresc ator
dup a k, secvent e:
k nr
unde k este nalt imea blocului,
iar nr este numarul de blocuri de n alt ime k, separate prin cate un spat iu.
Restrict ii si precizari
1 m, n 200
nu se vor asa blocurile de n alt ime k, a caror num ar este zero (0).
Exemplu
PENTAGON.IN PENTAGON.OUT
5 10 1 7
1110000111 2 1
1100001111 3 2
1000000011 5 1
1111101111
1110000111
Timp maxim de execut ie: 1 secunda/test
import java.io.*;
class Pentagon
{
static int m,n;
static int [][]a;
static int []f;
250 CAPITOLUL 16. PROBLEME
public static void main(String[] args) throws IOException
{
int i,j,s;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("pentagon.in")));
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("pentagon.out")));
st.nextToken();m=(int)st.nval;
st.nextToken();n=(int)st.nval;
a=new int[m+1][n+1];
f=new int[m+1];
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
{
st.nextToken();
a[i][j]=(int)st.nval;
}
for(j=1;j<=n;j++)
{
i=1;
while(i<=m)
{
s=0;
while((i<=m)&&(a[i][j]==0)) // numararea 0-urilor
{
s++;
i++;
}
if(s>0) f[s]++;
while((i<=m)&&(a[i][j]==1)) i++; // sar peste 1
}
}
for(i=1;i<=m;i++)
if(f[i]!=0) out.println(i+" "+f[i]);
out.close();
}
}
16.1.10 Suma - ONI 2002
Florin Ghet u, Bucuresti
16.1. PROBLEME REZOLVATE 251
Fie sirul tuturor numerelor naturale de la 1 la un num ar oarecare N.
Consider and asociate cate un semn (+ sau -) ec arui num ar si adun and toate
aceste numere cu semn se obt ine o suma S.
Problema const a n a determina pentru o sum a dat a S, num arul minim N
pentru care, printr-o asociere de semne tuturor numerelor de la 1 la N, se poate
obt ine S.
Cerint a:
Pentru un S dat, g asit i valoarea minim a N si asocierea de semne numerelor
de la 1 la N pentru a obt ine S n condit iile problemei.
Restrict ii:
0 < S 100.000.
Date de intrare

In sierul SUMA.IN se va aa pe prima linie unntreg pozitiv S reprezentand


suma ce trebuie obt inut a.
Date de iesire

In sierul SUMA.OUT se va scrie, pe prima linie num arul minim N pentru


care se poate obt ine suma S iar pe urm atoarele linii, p an a la sf arsitul sierului,
numerele care au semn negativ, c ate unul pe linie.
Ordinea de asare a numerelor nu are important a.
Celelalte numere care nu apar n sier se considera pozitive.
Daca exista mai multe solut ii se cere doar una.
Exemplu:
SUMA.IN SUMA.OUT
12 7
1
7
Deci suma 12 se poate obt ine din minimum 7 termeni astfel:
12 = 1 + 2 + 3 + 4 + 5 + 6 7.
Nu este singura posibilitate de asociere de semne termenilor de la 1 la 7.
Timpul de execut ie: 1 secunda/test
import java.io.*;
class Suma
{
public static void main(String[] args) throws IOException
{
int n=0,suma=0,s;
int np;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("suma.in")));
PrintWriter out=new PrintWriter(
252 CAPITOLUL 16. PROBLEME
new BufferedWriter(new FileWriter("suma.out")));
st.nextToken(); s=(int)st.nval;
while((suma<s)||((suma-s)%2==1))
{
n++;
suma=suma+n;
}
out.println(n);
np=(suma-s)/2;
if(np>0)
if(np<=n) out.println(np);
else out.println((np-n)+"\n"+n);
out.close();
}
}
16.1.11 Masina - ONI 2003
O t ara are 3 N 30000 orase, numerotate de la 1 la N, dispuse pe un cerc.
PAM tocmai si-a luat carnet de conducere si vrea sa viziteze toate orasele
t arii. Lui PAMi este frica sa conduc a prin locuri aglomerate asa ca ea si-a propus
sa mearga numai pe soselele unde tracul este mai redus.
Exist a sosele de legatur a ntre oricare dou a orase alaturate: ntre orasul 1 si
orasul 2, ... , ntre orasul i si orasul i + 1, iar orasul N este legat de orasulul 1.
Ca sa nu se r at aceasca, PAM si-a propus sa-si aleag a un oras de nceput
si sa mearga pe soselele respective n sens trigonometric p an a ajunge napoi n
orasul de unde a plecat. Daca PAM pleaca din orasul K, atunci traseul ei va :
K, K + 1, ..., N, 1, 2, ..., K.
Masina lui PAM are un rezervor foarte mare (n care poate pune oric at de
mult a benzina).

In ecare oras, PAM ia toat a cantitatea de benzina existenta n
oras, iar parcurgerea ecarei sosele necesita o anumit a cantitate de benzina.
Cerint a
Stiind c a PAM are, la nceputul c al atoriei, doar benzina existenta n orasul
de plecare, si ca, atunci c and ajunge ntr-un oras, ea va lua toata cantitatea de
benzin a disponibil a n acel oras, sa se gaseasca un oras din care PAM si poate
ncepe excursia astfel nc at sa nu r aman a f ar a benzina.
Se consider a ca PAM a r amas far a benzina dac a n momentul plec arii dintr-
un oras, nu are sucient a benzina pentru a parcurge soseaua care duce la orasul
urm ator. Dac a benzina i ajunge la x (adic a la plecare are tot at ata benzina cat a
i trebuie) se consider a ca PAM poate ajunge p an a n orasul urm ator.
Date de intrare
Fisierul de intrare masina.in cont ine
16.1. PROBLEME REZOLVATE 253
pe prima linie num arul N,
pe cea de-a doua linie se gasesc N numere naturale a[1], a[2], ..., a[N], se-
parate prin c ate un spat iu, unde a[i] reprezint a cantitatea de benzina disponibil a
n orasul i.
linia a treia cont ine un sir de N numere naturale b[1], b[2], ..., b[N], separate
prin c ate un spat iu, unde b[i] reprezint a cantitatea de benzina necesara str abaterii
soselei dintre orasele i si i + 1 (sau N si 1, dac a i = N).
Date de iesire
Fisierul de iesire masina.out va cont ine un singur numar s care reprezint a
un oras din care, dac a PAM si ncepe c al atoria, poate completa turul t arii f ar a a
face pana prostului.
Restrict ii si precizari
Daca exista mai multe solut ii, se cere una singura.
0 a[i] 30000
1 b[i] 30000
Exemplu
masina.in masina.out
6 4
0 3 2 5 10 5
7 8 3 2 1 4
Timp maxim de execut ie: 0.3 sec/test
import java.io.*;
class Masina
{
public static void main(String[] args) throws IOException
{
int i,rez;
int j,n;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("masina.in")));
PrintWriter out=new PrintWriter(
new BufferedWriter(new FileWriter("masina.out")));
st.nextToken();n=(int)st.nval;
int[] a=new int[n+1];
int[] b=new int[n+1];
for (j=1;j<=n;j++){ st.nextToken();a[j]=(int)st.nval;}
for (j=1;j<=n;j++){ st.nextToken();b[j]=(int)st.nval;}
for(i=1;i<=n;i++)
254 CAPITOLUL 16. PROBLEME
{
rez=a[i]-b[i];
j=i;
j++; if(j==n+1) j=1;
while((rez>=0)&&(j!=i))
{
rez=rez+a[j]-b[j];
j++;if(j==n+1) j=1;
}
if(rez>=0) {out.println(i); break;}
}
out.close();
}
}
16.1.12 Sir - ONI 2004
Gigel se distreaza construind siruri cresc atoare de numere din mult imea 1, 2, ..., n.
La un moment dat observ a ca unele siruri, de cel put in k termeni (k 3), au o
proprietate mai aparte: diferent a dintre doi termeni consecutivi este constanta.
Iat a cateva exemple de astfel de siruri pentru n 21:
2,3,4
1,5,9,13
7,10,13,16,19,21
Cerint a
D andu-se num arul natural n ajutat i-l pe Gigel sa numere cate astfel de siruri
poate sa construiasc a.
Date de intrare

In sierul de intrare sir.in se gaseste, pe prima linie, num arul n.


Date de iesire

In sierul de iesire sir.out se va asa, pe prima linie, num arul cerut urmat
de caracterul sfarsit de linie.
Restrict ii:
3 n 20000
3 k n
Exemple:
sir.in sir.out
3 1
4 3
5 7
Timp execut ie: 1 sec/test
16.1. PROBLEME REZOLVATE 255
import java.io.*;
class sir
{
public static void main(String []args) throws IOException
{
int ns=0,n=19999,r,k,i;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("sir.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(
new FileWriter("sir.out")));
st.nextToken(); n=(int)st.nval;
ns=0;
for(r=1;r<=(n-1)/2;r++)
for(k=3;k<=(n-1+r)/r;k++)
ns=ns+n-(k-1)*r;
System.out.println(ns);
out.println(ns);
out.close();
}
}
16.1.13 Triangulat ii - OJI 2002
O triangulat ie a unui poligon convex este o mult ime formata din diagonale
ale poligonului care nu se intersecteaz a n interiorul poligonului ci numai n v arfuri
si care mpart toat a suprafat a poligonului n triunghiuri.
Fiind dat un poligon cu n v arfuri notate 1, 2, ..., n sa se genereze toate
triangulat iile distincte ale poligonului. Dou a triangulat ii sunt distincte daca difer a
prin cel put in o diagonal a.
Date de intrare: n sierul text TRIANG.IN se a a pe prima linie un singur
num ar natural reprezent and valoarea lui n (n 11).
Date de iesire: n sierul text TRIANG.OUT se vor scrie:
- pe prima linie, num arul de triangulat ii distincte;
- pe ecare din urm atoarele linii cate o triangulat ie descrisa prin diagonalele
ce o compun. O diagonala va precizata prin dou a numere reprezentand cele dou a
v arfuri care o denesc; cele dou a numere ce denesc o diagonal a se despart prin
cel put in un spat iu, iar ntre perechile de numere ce reprezinta diagonalele dintr-o
triangulat ie se va lasa de asemenea minimum un spat iu.
Exemplu:
TRIANG.IN
5
TRIANG.OUT
5
256 CAPITOLUL 16. PROBLEME
1 3 1 4
2 4 2 5
5 2 5 3
3 5 3 1
4 2 1 4
Timp maxim de executare:
7 secunde/test pe un calculator la 133 MHz.
3 secunde/test pe un calculator la peste 500 MHz.
class Triangulatie
{
static int nv=5; // numar varfuri poligon
static int n=nv-3; // numar diagonale in triangulatie
static int m=nv*(nv-3)/2; // numarul tuturor diagonalelor
static int[] x=new int[n+1];
static int[] v1=new int[m+3],v2=new int[m+3];
static int nsol=0;
static void afisd()
{
int i;
System.out.print(++nsol+" ==> ");
for(i=1;i<=n;i++) System.out.print(v1[x[i]]+" "+v2[x[i]]+" ");
System.out.println();
}
static void afisx()
{
int i;
System.out.print(++nsol+" ==> ");
for(i=1;i<=n;i++) System.out.print(x[i]+" ");
System.out.println();
}
static void diagonale()
{
int i,j,k=0;
i=1;
for(j=3;j<=nv-1;j++) {v1[++k]=i; v2[k]=j;}
for(i=2;i<=nv-2;i++)
for(j=i+2;j<=nv;j++){v1[++k]=i; v2[k]=j;}
}// diagonale()
static boolean seIntersecteaza(int k, int i)
16.1. PROBLEME REZOLVATE 257
{
int j; // i si x[j] sunt diagonalele !!!
for(j=1;j<=k-1;j++)
if(((v1[x[j]]<v1[i])&&(v1[i]<v2[x[j]])&&(v2[x[j]]<v2[i]))||
((v1[i]<v1[x[j]])&&(v1[x[j]]<v2[i])&&(v2[i]<v2[x[j]]))
) return true;
return false;
}
static void f(int k)
{
int i;
for (i=x[k-1]+1; i<=m-n+k; i++)
{
if(seIntersecteaza(k,i)) continue;
x[k]=i;
if(k<n) f(k+1);
// else afisx();
else afisd();
}
}
public static void main(String[] args)
{
diagonale();
f(1);
}
}// class
16.1.14 Spirala - OJI 2003
Se consider a un automat de criptare format dintr-un tablou cu n linii si n
coloane, tablou ce cont ine toate numerele de la 1 la n
2
asezate serpuit pe linii,
de la prima la ultima linie, pe liniile impare pornind de la st anga catre dreapta,
iar pe cele pare de la dreapta catre stanga (ca n gur a).
1 2 3 4
8 7 6 5
9 10 11 12
16 15 14 13
Numim amestecare operat ia de desfasurare n spiral a a valorilor din tablou
n ordinea indicat a de saget i si de reasezare a acestora n acelasi tablou, serpuit
pe linii ca si n cazul precedent.
258 CAPITOLUL 16. PROBLEME
1 2 3 4
14 13 12 5
15 16 9 8
10 11 6 7
De exemplu, desfasurarea tabloului conduce la sirul: 1 2 3 4 5 12 13 14 15
16 9 8 7 6 11 10, iar reasezarea acestuia n tablou conduce la obt inerea unui nou
tablou reprezentat n cea de-a doua gura.
Dup a orice operat ie de amestecare se poate relua procedeul, efectuand o nou a
amestecare. S-a observat un fapt interesant: c a dup a un num ar de amestecari, unele
valori ajung din nou n pozit ia init ial a (pe care o aveau n tabloul de pornire). De
exemplu, dup a dou a amestecari, tabloul de 4x4 cont ine 9 dintre elementele sale n
exact aceeasi pozit ie n care se aau init ial (vezi elemente marcate din gura).
1 2 3 4
6 7 8 5
11 10 15 14
16 9 12 13
Cerint a
Pentru n si k citite, scriet i un program care s a determine numarul minim de
amestecari ale unui tablou de n linii necesar pentru a ajunge la un tablou cu exact
k elemente aate din nou n pozit ia init ial a.
Date de intrare
Fisierul de intrare spirala.in cont ine pe prima linie cele doua numere n si k
despart ite printr-un spat iu.
Date de iesire
Fisierul de iesire spirala.out cont ine o singur a linie pe care se a a num arul
de amestecari determinat.
Restrict ii si precizari
3 N 50
Datele de intrare sunt alese astfel nc at num arul minim de amestecari necesare
sa nu dep aseasca 2 10
9
Exemple
spirala.in spirala.out spirala.in spirala.out
4 9 2 6 36 330
Timp maxim de executare/test: 1 secunda
import java.io.*;
class spirala
{
static int n,k;
static int[] pr;
static int[] p;
public static void main(String[] args) throws IOException
{
16.1. PROBLEME REZOLVATE 259
int r=0,i;
StreamTokenizer st=new StreamTokenizer(new BufferedReader(
new FileReader("spirala.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(
new FileWriter("spirala.out")));
st.nextToken();n=(int)st.nval;
st.nextToken();k=(int)st.nval;
p=new int[n*n+1];
pr=new int[n*n+1];
constrp();
r=1;
for(i=1;i<=n*n;i++) pr[i]=p[i]; // p^1
// afisv(p); afisv(pr); System.out.println("------------");
while(!arekfixe()&&(r<21))
{
r++;
pr=inmp(pr,p);
// System.out.print("\nr="+r); afisv(pr);
}
out.println(r);
out.close();
}
static void constrp()
{
int i,j,k,v,kp=0;
int[][] a=new int[n+1][n+1];
i=1; v=0;
for(k=1;k<=n/2;k++)
{
for(j=1;j<=n;j++) a[i][j]=++v;
for(j=n;j>=1;j--) a[i+1][j]=++v;
i=i+2;
}
if(n%2==1)
for(j=1;j<=n;j++) a[n][j]=++v;
// afism(a);
for(k=1;k<=n/2;k++) // dreptunghiul k
{
i=k;
for(j=k;j<=n+1-k;j++) p[++kp]=a[i][j];
j=n+1-k;
for(i=k+1;i<=n-k;i++) p[++kp]=a[i][j];
260 CAPITOLUL 16. PROBLEME
i=n+1-k;
for(j=n+1-k;j>=k;j--) p[++kp]=a[i][j];
j=k;
for(i=n-k;i>=k+1;i--) p[++kp]=a[i][j];
}
// afisv(p);
}
static int[] inmp(int[]a,int[]b)
{
int k;
int[] c=new int[n*n+1];
for(k=1;k<=n*n;k++) c[k]=a[b[k]];
return c;
}
static boolean arekfixe()
{
int i, s=0;
for(i=1;i<=n*n;i++) if(pr[i]==i) s++;
System.out.println("s="+s);
if(s==k) return true; else return false;
}
static void afism(int[][] x)
{
int i,j;
for(i=1;i<=n;i++)
{
System.out.println();
for(j=1;j<=n;j++) System.out.print(x[i][j]+"\t");
}
System.out.println();
}
static void afisv(int[] x)
{
int i;
System.out.println();
for(i=1;i<=n*n;i++) System.out.print(x[i]+" ");
System.out.println();
}
}
16.1. PROBLEME REZOLVATE 261
16.1.15 Partit ie - ONI 2003
Se deneste o partit ie a unui num ar natural n ca ind o scriere a lui n sub forma:
n = n
1
+n
2
+...nk, (k 1)
unde n
1
, n
2
, ..., n
k
sunt numere naturale care veric a urm atoarea relat ie:
n
1
n
2
... n
i
... n
k
1
Cerint a: Fiind dat un num ar natural n, sa se determine cate partit ii ale lui se
pot scrie, conform cerint elor de mai sus, stiind c a oricare num ar n
i
dintr-o partit ie
trebuie sa e un num ar impar.
Date de intrare
Fisierul partitie.in cont ine pe prima linie num arul n
Date de iesire
Fisierul partitie.out va cont ine pe prima linie num arul de partit ii ale lui n
conform cerint elor problemei.
Exemplu:
partitie.in partitie.out
7 5
Explicat ii:
Cele cinci partit ii sunt:
1+1+1+1+1+1+1
1+1+1+1+3
1+1+5
1+3+3
7
Restrict ii:
1 n 160
Timp maxim de executare: 3 secunde/test.
class PartitieNrImpare2 // n = suma de numere impare ;
{ // optimizat timp la jumatate !!!
static int dim=0,nsol=0; // nsol = 38328320 2Gata!!! timp = 4787
static int[] x=new int[161];
public static void main(String[] args)
{
long t1,t2;
int n=160;
int maxi=((n-1)/2)*2+1;
t1=System.currentTimeMillis();
f(n,maxi,1);
t2=System.currentTimeMillis();
262 CAPITOLUL 16. PROBLEME
System.out.println("nsol = "+nsol+" timp= "+(t2-t1));
}
static void f(int val, int maxip, int poz)
{
if(maxip==1)
{
nsol++;
// dim=poz-1;
// afis2(val);
return;
}
if(val==0)
{
nsol++;
// dim=poz-1;
// afis1();
return;
}
int maxi=((val-1)/2)*2+1;
int maxiok=min(maxip,maxi);
int i;
for(i=maxiok;i>=3;i=i-2)
{
x[poz]=i;
f(val-i,i,poz+1);
}
nsol++;
// dim=poz-1;
// afis2(val);
}
static void afis1()
{
int i;
System.out.print("\n"+nsol+" : ");
for(i=1;i<=dim;i++) System.out.print(x[i]+" ");
}
static void afis2(int val)
{
int i;
System.out.print("\n"+nsol+" : ");
16.1. PROBLEME REZOLVATE 263
for(i=1;i<=dim;i++) System.out.print(x[i]+" ");
for(i=1;i<=val;i++) System.out.print("1 ");
}
static int max(int a,int b) { return (a>b)?a:b; }
static int min(int a,int b) { return (a<b)?a:b; }
}// class
264 CAPITOLUL 16. PROBLEME
Bibliograe
[1] Aho, A.; Hopcroft, J.; Ullman, J.D.; Data strutures and algorithms, Addison
Wesley, 1983
[2] Aho, A.; Hopcroft, J.; Ullman, J.D.; The Random Access Machine, 1974
[3] Andonie R., G arbacea I.; Algoritmi fundamentali, o perspectiv a C++, Ed.
Libris, 1995
[4] Apostol C., Rosca I. Gh., Rosca V., Ghilic-Micu B., Introducere n progra-
mare. Teorie si aplicatii, Editura ... Bucuresti, 1993
[5] Atanasiu, A.; Concursuri de informatic a. Editura Petrion, 1995
[6] Atanasiu, A.; Ordinul de complexitate al unui algoritm. Gazeta de Informatic a
nr.1/1993
[7] - Bell D., Perr M.: Java for Students, Second Edition, Prentice Hall, 1999
[8] Calude C.; Teoria algoritmilor, Ed. Universit at ii Bucuresti, 1987
[9] Cerchez, E.; Informatica - Culegere de probleme pentru liceu, Ed. Polirom,
Iasi, 2002
[10] Cerchez, E., Serban, M.; Informatic a - manual pentru clasa a X-a., Ed.
Polirom, Iasi, 2000
[11] Cori, R.; Levy, J.J.; Algorithmes et Programmation, Polycopie, version 1.6;
http://w3.edu.polytechnique.fr/informatique/
[12] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Introducere n Algoritmi, Ed
Agora, 2000
[13] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Pseudo-Code Language, 1994
[14] Cristea, V.; Giumale, C.; Kalisz, E.; Paunoiu, Al.; Limbajul C standard, Ed.
Teora, Bucuresti, 1992
[15] Erickson J.; Combinatorial Algorithms; http://www.uiuc.edu/~jeffe/
265
266 BIBLIOGRAFIE
[16] Flanagan, D.; Java in a Nutshell, OReilly, 1997.
[17] Flanagan, D.; Java examples in a Nutshell, OReilly, 1997.
[18] Giumale, C.; Introducere n Analiza Algoritmilor, Ed.Polirom, 2004
[19] Giumale C., Negreanu L., C alinoiu S.; Proiectarea si analiza algoritmilor.
Algoritmi de sortare, Ed. All, 1997
[20] Gosling, J.; Joy, B.; Steele, G.; The Java Language Specication, Addison
Wesley, 1996.
[21] Knuth, D.E.; Arta program arii calculatoarelor, vol. 1: Algoritmi fundamentali,
Ed. Teora, 1999.
[22] Knuth, D.E.; Arta programarii calculatoarelor, vol. 2: Algoritmi seminumerici,
Ed. Teora, 2000.
[23] Knuth, D.E.; Arta programarii calculatoarelor, vol. 3: Sortare si cautare, Ed.
Teora, 2001.
[24] Lambert,K. A., Osborne,M.; Java. A Framework for Programming and Prob-
lem Solving, PWS Publishing, 1999
[25] Livovschi, L.; Georgescu H.; Analiza si sinteza algoritmilor. Ed. Enciclopedic a,
Bucuresti, 1986.
[26] Niemeyer, P.; Peck J.; Exploring Java, OReilly, 1997.
[27] Od agescu, I.; Smeureanu, I.; Stefanescu, I.; Programarea avansata a calcula-
toarelor personale, Ed. Militar a, Bucuresti 1993
[28] Od agescu, I.; Metode si tehnici de programare, Ed. Computer Lobris Agora,
Cluj, 1998
[29] Popescu Anastasiu, D.; Puncte de articulat ie si punt i n grafuri, Gazeta de
Informatica nr. 5/1993
[30] Rotariu E.; Limbajul Java, Computer Press Agora, Tg. Mures, 1996
[31] Tomescu, I.; Probleme de combinatorica si teoria grafurilor, Editura Didactic a
si Pedagogica, Bucuresti, 1981
[32] Tomescu, I.; Leu, A.; Matematic a aplicat a n tehnica de calcul, Editura Di-
dactica si Pedagogica, Bucuresti, 1982
[33] Vaduva, C.M.; Programarea in JAVA. Microinformatica, 1999
[34] ii nescu, R.;; Vii nescu, V.; Programare dinamic a - teorie si aplicat ii; GInfo nr.
15/4 2005
BIBLIOGRAFIE 267
[35] Vlada, M.; Conceptul de algoritm - abordare modern a, GInfo, 13/2,3 2003
[36] Vlada, M.; Grafuri neorientate si aplicat ii. Gazeta de Informatica, 1993
[37] Weis, M.A.; Data structures and Algorithm Analysis, Ed. The Ben-
jamin/Cummings Publishing Company. Inc., Redwoods City, California, 1995.
[38] Winston, P.H., Narasimhan, S.: On to JAVA, Addison-Wesley, 1996
[39] Wirth N., Algorithms + Data Structures = Programs, Prentice Hall, Inc 1976
[40] *** - Gazeta de Informatica, Editura Libris, 1991-2005

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