ALGORITMI S ¸ I PROGRAMARE Note de curs

(uz intern - draft v1.1)

Prefat ¸˘ a
Cˆ and dorim s˘ a reprezent˘ am obiectele din lumea real˘ aˆ ıntr-un program pe calculator, trebuie s˘ a avem ˆ ın vedere: • modelarea obiectelor din lumea real˘ a sub forma unor entit˘ a¸ ti matematice abstracte ¸ si tipuri de date, • operat ¸iile pentru ˆ ınregistrarea, accesul ¸ si utilizarea acestor entit˘ a¸ ti, • reprezentarea acestor entit˘ a¸ ti ˆ ın memoria calculatorului, ¸ si • algoritmii pentru efectuarea acestor operat ¸ii. Primele dou˘ a elemente sunt ˆ ın esent ¸˘ a de natur˘ a matematic˘ a¸ si se refer˘ a la ”ce” structuri de date ¸ si operat ¸ii trebuie s˘ a folosim, iar ultimile dou˘ a elemente implic˘ a faza de implementare ¸ si se refer˘ a la ”cum” s˘ a realiz˘ am structurile de date ¸ si operat ¸iile. Algoritmica ¸ si structurile de date nu pot fi separate. De¸ si algoritmica ¸ si programarea pot fi separate, noi nu vom face acest lucru, ci vom implementa algoritmii ˆ ıntr-un limbaj de programare (Pascal, C/C++, Java). Acest curs este ¸ si o init ¸iere ˆ ın algoritmi ¸ si structuri de date. Scopul cursului este subordonat scopului specializ˘ arii (informatic˘ a, ˆ ın cazul nostru) care este s˘ a preg˘ ateasc˘ a speciali¸ sti competent ¸i, cu ˆ ınalt˘ a calificare ˆ ın domeniul informaticii, cadre didactice competente ˆ ın acest domeniu (profesor de informatic˘ aˆ ın gimnaziu ¸ si liceu), informaticieni ˆ ın diverse domenii cu profil tehnic, economic, etc. ce pot ˆ ıncepe lucrul imediat dup˘ a absolvirea facult˘ a¸ tii.Dezideratul final este deci competent ¸a. Competent ¸a ˆ ıntr-un domeniu de activitate implic˘ a experient ¸˘ aˆ ın rezolvarea problemelor din acel domeniu de activitate. Atˆ at competent ¸a cˆ at ¸ si experient ¸a ˆ ın rezolvarea problemelor se pot obt ¸ine numai dac˘ a permanent se ˆ ıntreprind eforturi pentru ˆ ınsu¸ sirea de noi cuno¸ stint ¸e. De exemplu, orice informatician (programator sau profesor) care elaboreaz˘ a programe pentru rezolvarea unor probleme diverse, trebuie s˘ a aib˘ a competent ¸e conform schemei1 :
PROBLEMA (model fizic)

ALGORITMICA (model virtual)

PROGRAMARE

Gandire algoritmica

Experienta (rezolvarea de probleme)

Cursul de Algoritmi ¸ si programare este util pentru formarea competent ¸elor ¸ si abilit˘ a¸ tilor unui bun programator sau profesor de informatic˘ a. Pentru a vedea care sunt aceste competent ¸e ¸ si abilit˘ a¸ ti putem, de exemplu, s˘ a citim Programa
1 M. Vlada; E-Learning ¸ si Software educat ¸ional; Conferint ¸a Nat ¸ional˘ a de ˆ Inv˘ a¸ ta ˘mˆ ant Virtual, Bucure¸ sti, 2003

pentru informatic˘ a - Concursul nat ¸ional unic pentru ocuparea posturilor didactice declarate vacante ˆ ın ˆ ınv˘ a¸ ta ˘mˆ antul preuniversitar.2 ˆ Intr-un fel, cursul Algoritmi ¸ si programare este echivalent cu ceea ce se pred˘ a la informatic˘ aˆ ın clasele a X-a ¸ si a XI-a la specializarea matematic˘ a-informatic˘ aintensiv informatic˘ a. Diferent ¸a este dat˘ a de dificultatea problemelor abordate de c˘ atre noi ˆ ın cadrul acestui curs. Din aceast˘ a cauz˘ a vom avea ˆ ın vedere ¸ si Pograma ¸ solar˘ a pentru clasa a X-a, Profil real, Specializarea: Matematic˘ a-informatic˘ a, intensiv informatic˘ a¸ si Pograma ¸ solar˘ a pentru clasa a XI-a, Profil real, Specializarea: Matematic˘ a-informatic˘ a, intensiv informatic˘ a. 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 verific˘ a validitatea indicilor tablourilor (programele nu se pot termina printr-o violare de memorie sau eroare de sistem). Java realizeaz˘ a gestiunea automat˘ a a memoriei (recupereaz˘ a automat memoria care nu mai este necesar˘ a programului) ceea ce simplific˘ a scrierea programelor ¸ si permite programatorului s˘ a se concentreze asupra esent ¸ei algoritmului. Exist˘ a documentat ¸ie pe internet. Compilatorul de Java este gratuit. Un program scris ˆ ın Java poate fi executat pe orice calculator (indiferent de arhitectur˘ a sau sistem de operare). Se poate trece foarte u¸ sor la C#. Student ¸ii nu sunt obligat ¸i s˘ a realizeze implement˘ arile algoritmilor ˆ ın Java; ei pot folosi Pascal sau C/C++. Algoritmii prezentat ¸i ˆ ın curs sunt descri¸ si ˆ ı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 aceast˘ a particularitate. Sunt prezentate toate elementele limbajului de programare Java necesare pentru acest curs dar ecesta nu este un curs de programare ˆ ın Java. Cuno¸ stint ¸ele minimale acceptate la sfˆ ar¸ situl cursului rezult˘ a din Legea nr. 288 din 24 iunie 2004 privind organizarea studiilor universitare ¸ si, de exemplu, din Ghidul calit˘ a¸ tii ˆ ın ˆ ınv˘ a¸ ta ˘mˆ antul superior3 . Aici se precizeaz˘ a faptul c˘ a diploma de licent ¸˘ a se acord˘ a unui absolvent al programului de studii care: demonstreaz˘ a acumulare de cuno¸ stint ¸e ¸ si capacitatea de a ˆ ınt ¸elege aspecte din domeniul de studii ˆ ın care s-a format, poate folosi atˆ at cuno¸ stint ¸ele acumulate precum ¸ si capacitatea lui de ˆ ınt ¸elegere a fenomenelor printr-o abordare profesional˘ a ˆ ın domeniul de activitate, a acumulat competent ¸e necesare demonstr˘ arii, argument˘ arii ¸ si rezolv˘ arii problemelor din domeniul de studii considerat, ¸ si-a dezvoltat deprinderi de ˆ ınv˘ a¸ tare necesare procesului de educat ¸ie continu˘ a.

prin O.M:Ed.C. nr.5287/15.11.2004 Universit˘ a¸ tii din Bucure¸ sti, 2004; Capitolul 4, Calitatea programelor de studii universitare, Prof.univ.dr. Gabriela M. Atanasiu - Universitatea Tehnic˘ a ”Gh.Asachi” din Ia¸ si
3 Editura

2 Aprobat˘ a

. . . . . . . . . . . . 1. . . . 1. . .3 Metoda optimului local . . . . . . . . . .4. . . . .2. . . . . . . . . . . . . . . . . 1. . . .2. . .4. . . . . . . . . . . . . . . . . . . .9 Problema alegerii taxelor . . . . 2.4. . . .Cuprins 1 Algoritmi divide et impera ¸ si greedy 1. . . . . . . . .2 Generarea recursiv˘ a a produsului cartezian 2. . . . . . . . . . . . . . 2. . . . . .1 Problema continu˘ a a rucsacului . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . .3 Problema reginelor pe tabla de ¸ sah . . . . . . . . . . . . . . . . . . . . . . . . .3 Placa cu g˘ auri . .quicksort . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. 1. . . . . . . . . . . . . . . . . . . . . . 1. 1. . . . . . . . . . . . . . . . . . 1. . 1. . . . . . .1 Generarea aranjamentelor .2. . . . 2. . . . . . . . . .4. . . .3 Problema plas˘ arii textelor pe m benzi 1. 2. . . . . . . . . . . . . . . . . . .6 Problema cutiilor . . . . . . . . . . . . . . . . .5 Problema stat ¸iilor . . . . . .4 Maximizarea unei sume de produse . . . . . . . . . . . . . . . v . . . . . . . 2. . .8 Problema intervalelor disjuncte . . 1. . . . .4. . . . .4 Exemple de algoritmi greedy . . . 1.2. . . . .4. . . 1. . .3. . . 1. . . .1 Generarea iterativ˘ a a produsului cartezian . . . . . . . 1 1 3 3 4 6 7 11 14 15 16 17 17 18 18 19 20 20 20 21 23 23 23 28 31 33 33 34 34 38 48 2 Metoda backtracking 2. . . 1. .1 Bactracking iterativ .4. . .3 Probleme rezolvate . . .4. . . . . . . . . . . . 2.2 Problema plas˘ arii textelor pe o band˘ a 1. . . . . . . . . .3. . . . . . . .5 ˆ Injum˘ at˘ a¸ tire repetat˘ a . . .1 Generarea produsului cartezian .greedy . . . . . 2. . . . . . . .1. . . .1 Sortare prin partitionare . . . . . . . . . . . . . . . . . . . . . . 1. . . . . . . . . .4. . . . . . . . .MergeSort . . .3. . . . .2 Backtracking recursiv .10 Problema acoperirii intervalelor . .2 Sortare prin interclasare . 1.1 Tehnica divide et impera . . . . . . . . . . . . . .4 Turnurile din Hanoi . . . . . . . . . . . . . . . . 2. . .2 Generarea combin˘ arilor . . . .7 Problema sub¸ sirurilor . .2. . . . . . . 1. . . . . . . . . . . . .2 Metoda bactracking . . . .2 Exemple de algoritmi divide et impera .

. . . . . . . . . . . . . . . . . . . . .9 Turneul calului pe tabla de ¸ sah . . . . . .1 Determinarea orient˘ arii . .2. . . . .6 2. . . . .2 Testarea convexit˘ a¸ tii poligoanelor . . . .3. . . . . . . . 3. . . . . . .3 Sum˘ a maxim˘ aˆ ın triunghi de numere . . . . . . . 3. . . . . . . . . . 3. . . . . . .ONI2004 clasa a IX-a . . . 4.9. . . . .3. . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . 3. . Runda 6 . . . . .3. . . . .3 Aria poligoanelor convexe . . . . . . . . . . . . . . . . . . . . . .4 Partit ¸ie . 5. . .6. . . . . . .9 Probleme rezolvate . .3. . . . . . . . 50 52 55 57 60 64 65 65 67 67 70 74 75 82 88 89 90 92 95 97 97 99 104 104 106 109 109 110 110 110 111 112 112 116 126 127 127 127 141 146 147 3 Programare dinamic˘ a 3. . . . . . . . . . . . . . . . . . . . . . . . .OJI2004 clasa a XI-a 5.2 Probleme rezolvate . . . 3. . Problema color˘ arii h˘ art ¸ilor . . . . . . . . . . . . .Campion 2003-2004 4. . . . . . . . . . . . . . . . . . . 5. . .4 2. .2 Un algoritm eficient . . . . . . . . .2. . . . . . . . . . . .ONI2006 baraj . . . . . . . . . . . . . . 4. . . . . . 5. 5. . . . . . . . . .2. . . . . . .2. . . . . . . . .10 Triangularizarea poligoanelor convexe 4 Potrivirea ¸ sirurilor 4. . . . . . . . . . . . . . . . . . . . . . . .KMP . . . . . . 3. . .9. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Seceta . .2. . . . . . . .2. . . .6. . . . . . 3. . . .6 Problema rucsacului (0 − 1) . 5 Geometrie computat ¸ional˘ a 5. . 5. . . .8 2. . . . .5 Pozit ¸ia unui punct fat ¸˘ a de un poligon concav . . . 5. . .6 ˆ Inf˘ a¸ sur˘ atoarea convex˘ a. . . . . . . . . . . . .5 2. . . . .4 Pozit ¸ia unui punct fat ¸˘ a de un poligon convex . . . . . . . . . . . . . . . . . . .7 Dreptunghi minim de acoperire a punctelor . . . . . . . . .1 Circular . . .7 Problema schimbului monetar . . . . . . . . . . . Generarea partit ¸iilor unui num˘ ar natural Problema parantezelor . . . . . . . . .9 Problema segment˘ arii vergelei . . . . . . . . . . . . . . . .5 Distant ¸a minim˘ a de editare . . . . . .1 Un algoritm ineficient . .2. . . . . . . . .4 Sub¸ sir comun maximal .1 ˆ Impachetarea Jarvis . . . . . . . . . . . . . . 3. . . . . . 5. . 5. .ONI2006 baraj . . . . . . . . . . . . . . .1 Inmult ¸irea optimal˘ a a matricelor . . . . . . . . . . . . . . .3. . Problema labirintului . . . .2 Sub¸ sir cresc˘ ator maximal . . . . . . . . . . . . . . . . . . . . . .2. . .3. . .9. . . .8 Problema travers˘ arii matricei .2 Antena . . . . . . . 5. . . 3. . . .3 Probleme rezolvate . . . . . . . . . . .7 2. . 5. . . . . . . . . . . . . . . . . . . . . . . 3. . . 5. . . . . . . . .1 Prezentare general˘ a. . . .9. . . . . . . . . 3. . . . . . 5. . .2. . . . . . . . . . . . . . . 5. . . . 4. .2 Cifru . .ONI2005 clasa a X-a . . . . Problema vecinilor . . . . . . . . . . . . . .2 Scanarea Craham . . . . . . . . . . . . . . . . . . .3 Mo¸ sia lui P˘ acal˘ a . . . . . . . . . . .2. . .3. . . . . . .2. . . . .8 Cerc minim de acoperire a punctelor .

. . . . 153 153 154 155 162 165 165 167 171 171 173 174 175 176 178 180 182 185 186 187 200 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6. .1 Reprezentarea grafurilor . . . . . .4 Muchii de separare . . . . . 6. . . . . . . . . . . . . . . 6. . . . 215 . . 6. . . 6. .6 Distant ¸e minime ˆ ın grafuri . . . . . . . . . . . . . . . . .1 Folosind parcurgerea ˆ ın adˆ ancime 6.2 Folosind gradele interioare . . . . . . . . . . . . 6. . 6. 6. . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Algoritmul lui Kruskal . . . . . . . . . . 7 Fluxuri ˆ ın ret ¸ele 211 7. . . . . . . . . . . . . . . . . . . . . . . 6.5 Componente biconexe . . . .1 Algoritmul Roy-Floyd-Warshall . 6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Parcurgeri ˆ ın grafuri . . . . . . . . . . . . . 6. . . . .5. . . .5. . . . . . . . . . .4. . . . . . . .6. . . . . 6. .5 Componente conexe ¸ si tare conexe . . . . . 6. . . 6.2 Parcurgerea ˆ ın l˘ a¸ time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6. . .5. . . . .3 Algoritmul Belmann-Ford . .vii 6 Grafuri 6. . . . .2 Algoritmul lui Dijkstra . . .BFS . .2. . .1 Algoritmul lui Prim . . .2. . . . . . . . . . .4 Sortare topologic˘ a . .4. . . .5. . . . . . . .2 Arbore minim de acoperire . . . .2 Componente tare conexe .1 Componente conexe . . . . . . . .1 Algoritmul Edmonds-Karp . . . . . . . . . . . . . . . 6. . . . . . . . . . . . .6. . . . . . . . . . . . . . . . . . . .2 Cuplaj maxim . . . . .3 Noduri de separare . . . . . . . . . . 6. .DFS . . . . . . .3. 212 7. . . . . . .1 Parcurgerea ˆ ın adˆ ancime . . . . . . . . . . . . . . .6. . . 6. . . .5. . . . . . . . . . . . . . . . . . . . . .

viii .

S1 ) .. Metoda poate fi descris˘ a astfel: procedure divideEtImpera(P. • Rezolvarea ˆ ın acela¸ si mod (recursiv) a tuturor subproblemelor.. Sk pentru a obt ¸ine S 1 divide-and-conquer.. nk .. . Descompunerea problemei (subproblemelor) se face pˆ an˘ an ˆ momentul ˆ ın care se obt ¸in subprobleme de dimensiuni atˆ at de mici ˆ ıncˆ at au solut ¸ie cunoscut˘ a sau pot fi rezolvate prin tehnici elementare. n. .1 Tehnica divide et impera Divide et impera1 este o tehnic˘ a de elaborare a algoritmilor care const˘ aˆ ın: • Descompunerea repetat˘ a a problemei (subproblemei) ce trebuie rezolvat˘ aˆ ın subprobleme mai mici.. ˆ ın englez˘ a 1 .. n1 . nk divideEtImpera(P1 . Pk de dimensiuni n1 . Sk ) combin˘ a S1 . S ) if (n ≤ n0 ) then rezolv˘ a subproblema P prin tehnici elementare else ˆ ımparte P ˆ ın P1 ..... divideEtImpera(Pa . .Capitolul 1 Algoritmi divide et impera ¸ si greedy 1. • Compunerea subsolut ¸iilor pentru a obt ¸ine solut ¸ia problemei (subproblemei) init ¸iale..

.. a > bk . Seria este convergent˘ a¸ si deci ¸ sirul sumelor part ¸iale este convergent. ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY Exemplele tipice de aplicare a acestei metode sunt algoritmii de parcurgere a arborilor binari ¸ si algoritmul de c˘ autare binar˘ a.. unde b > 1. dac˘ a n ≤ n0 .2 CAPITOLUL 1. De k k asemenea. Vom presupune c˘ a dimensiunea ni a subproblemei i satisface relat ¸ia ni ≤ n b. ceea ce asigur˘ a terminarea subprogramului recursiv.. Presupunem c˘ a divizarea problemei ˆ ın subprobleme ¸ si compunerea solut ¸iilor subproblemelor necesit˘ a un timp de ordinul O(nk ). + a k + nk m −1 k k = am cnk b n0 + . De aici rezult˘ a c˘ a T (n) = O(am ) = O(alogb n ) = O(nlogb a ) .. Distingem cazurile: m i=0 bk a i 1. dac˘ a a < bk (1. complexitatea timp T (n) a algoritmului divideEtImpera este dat˘ a de relat ¸ia de recurent ¸˘ a: T (n) = O(1) k a · T(n b ) + O (n ) . presupunem c˘ a T (n) = c · n0 dac˘ a n ≤ n0 ¸ si T (n) = a · T ( n b)+c·n dac˘ a n > n0 .1. dac˘ a a = bk .1) Teorema 1 Dac˘ a n > n0 atunci:  log a  O(n b ) T (n) = O(nk logb n)   O(nk ) . + a i bk a = cam i=0 bk a unde am notat cnk 0 prin c. pasul de divizare reduce o subproblem˘ a la altele de dimensiuni mai mici.. Astfel. dac˘ a a > bk . c˘ a n = bm · n0 . + a bm−1 0 +c a m 1+ = cnk 0a m m k k nk 0 + (b ) n0 bk + .. f˘ ar˘ a a restrˆ ange generalitatea.1. = am T (n0 ) + c am−1 n b m −1 m k n b n +c a b k + cnk + nk n b k k + .2) Demonstrat ¸ie: Putem presupune. Pentru n > n0 avem: T (n) n + cnk b = aT bm−1 n0 + cnk = aT = a aT bm−2 n0 + c = a2 T bm−2 n0 = .. dac˘ a n > n0 (1. Atunci.

Merge sort • dreptunghi de arie maxim˘ aˆ ın placa cu g˘ auri. se interschimb˘ a ai cu aj 5.1. public static void main(String[]args) { .6.3}. class QuickSort { static int x[]={3. se scaneaz˘ a tabloul a[] la stˆ anga lui x pˆ an˘ a cˆ and se g˘ ase¸ ste un element ai > x 3.5. se aplic˘ a urm˘ atorul algoritm: 1. se alege la ˆ ıntˆ amplare un element x al tabloului 2.2. k 1. etc 1. a < bk .4. Rezult˘ a c˘ a am = bkm = cnk ¸ si de aici T (n) = O(nk m) = O(nk logb n).5.quicksort Se bazeaz˘ a pe metoda interschimb˘ arii. Dup˘ a aceasta. EXEMPLE DE ALGORITMI DIVIDE ET IMPERA 3 2. avˆ and tabloul a[].2. la dreapta. interschimbarea se face pe distant ¸e mai mari. 4 pˆ an˘ a cˆ and scan˘ arile se vor ˆ ıntˆ alni pe undeva la mijlocul tabloului. ˆ ıns˘ a din nou. elemente mai mari ca ¸ si x.4. se scaneaz˘ a tabloul la dreapta lui x pˆ an˘ a cˆ and se g˘ ase¸ ste un element aj < x 4. la stˆ anga lui x se vor g˘ asi elemente mai mici ca ¸ si x. se repet˘ a pa¸ sii 2. se aplic˘ a acela¸ si proces sub¸ sirurilor de la stˆ anga ¸ si de la dreapta lui x.1.8. 3. Avem T (n) = O(am ( ba )m ) = O(bkm ) = O(nk ). ˆ In acel moment.2 Exemple de algoritmi divide et impera Dintre problemele clasice care se pot rezolva prin metoda divide et impera ment ¸ion˘ am: • c˘ autare binar˘ a • sortare rapid˘ a (quickSort) • problema turnurilor din Hanoi • sortare prin interclasare .2. tabloul a[] va fi partitionat ˆ ın 2 astfel.2. a = bk .1 Sortare prin partitionare . Astfel.3. 3. pˆ an˘ a cˆ and aceste sub¸ siruri sunt suficient de mici (se reduc la un singur element).

4 CAPITOLUL 1.a).2. Din Teorema 1 pentru a = 2. ˆ ın practic˘ a (ˆ ın medie)! 1. class MergeSort { static int x[]={3. ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY quickSort(0. Pasul de divizare se face ˆ ın timpul O(1).int a[]) { int i=st. ˆ In urma rezolv˘ arii recursive a subproblemelor rezult˘ a vectori ordonat ¸i ¸ si prin interclasarea lor obt ¸inem vectorul init ¸ial sortat. } } while(i<=j).1.dr. while (a[j]>x) --j.3}.length-1. x.x. j=dr. if(i<=j) { temp=a[i].2 Sortare prin interclasare . }// quickSort(.4.3.print(x[i]+" "). x=a[(st+dr)/2]..i<x. Faza de asamblare se face ˆ ın timpul O(m1 + m2 ) unde n1 ¸ si n2 sunt lungimile celor doi vectori care se interclaseaz˘ a.6.5..2.8.5. for(int i=0. do { while (a[i]<x) i++. i++.) }//class Aceasta metoda are complexitatea n log n.a).int dr.out.4. if(i<dr) quickSort(i.i++) System.j. Vectorii de lungime 1 sunt evident considerat ¸i sortat ¸i. temp. a[j]=temp. if(st<j) quickSort(st. }//main static void quickSort(int st. public static void main(String[]args) .2. b = 2 ¸ si k = 1 rezult˘ a c˘ a ordinul de complexitate al algoritmului MergeSort este O(n log2 n) unde n este dimensiunea vectorului init ¸ial supus sort˘ arii. a[i]=a[j].length.x).MergeSort Ideea este de a utiliza interclasarea ˆ ın etapa de asamblare a solut ¸iilor. j--.

i<=dr. mergeSort(st. while((i<=m)&&(j<=dr)) { if(a[i]<=a[j]) { b[k]=a[i].m.) }//class 5 k++.m cu m+1.m.i++) System.dr.length. EXEMPLE DE ALGORITMI DIVIDE ET IMPERA { mergeSort(0.dr.j<=m.. k++. j++) { b[k]=a[j].. k++.j..dr int i.i<=dr.out. }//main public static int[] mergeSort(int st.2.k.i<x.} else { b[k]=a[j].. } if(i<=m) for(j=i. k=0. }// mergeSort(.. } else for(i=j.x. } .i++) { b[k]=a[i].int a[]) { // interclasare pozitii st....a). i++. i=st. mergeSort(m+1. } k++.. for(int i=0. int b[]=new int[dr-st+1].int dr.a[dr]=aux. return a.print(x[i]+" "). interclasare(st. } return a.int a[]) { int m. } k=0.) public static int[] interclasare(int st. j=m+1.a).. for(i=st. if((dr-st)<=1) { if(a[st]>a[dr]) { aux=a[st]. j++.} } else { m=(st+dr)/2.int m.length-1.i++) { a[i]=b[k]...1. }//interclasare(.x).a[st]=a[dr].a).aux.int dr.

1: Dreptunghi de arie maxim˘ aˆ ın placa cu g˘ auri import java. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dreptunghi.x2. y2=(int) st.nextToken(). y1=(int) st.y1.6 CAPITOLUL 1.nextToken(). y 2) din dreapta-sus este analizat pentru a vedea dac˘ aˆ ın interior cont ¸ine vreo gaur˘ a.in"))).x1s.3 Placa cu g˘ auri Se consider˘ a o plac˘ a dreptunghiular˘ aˆ ın care exist˘ a n g˘ auri punctiforme de coordonate cunoscute. n=(int) st. public static void main (String[] args) throws IOException { int i.nextToken().nval. st. dac˘ a nu cont ¸ine. st. st.nval.n. y 1) (din stˆ anga-jos) ¸ si (x2. este o solut ¸ie posibil˘ a (se va alege cea de arie maxim˘ a).y1s. st.2. S˘ a se determine pe aceast˘ a plac˘ a un dreptunghi de arie maxim˘ a. dac˘ a exist˘ a o gaur˘ aˆ ın interiorul s˘ au. class drArieMaxima { static int x1. Consider˘ am o subproblem˘ a de forma urm˘ atoare: dreptunghiul determinat de diagonala (x1. x1=(int) st.x2s. atunci se consider˘ a patru subprobleme generate de cele 4 dreptunghiuri obt ¸inute prin descompunerea pe orizontal˘ a¸ si pe vertical˘ a de cele dou˘ a drepte paralele cu axele care trec prin punctul care reprezint˘ a gaura.nval.nextToken(). 1 2 3 4 Figura 1. . static int[] x.amax. ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY 1. cu laturile paralele cu laturile pl˘ acii. x2=(int) st.*.nextToken().out"))). StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dreptunghi.nval.nval. st.y2s. static int[] y.y2. care s˘ a nu cont ¸in˘ a g˘ auri ˆ ın interior ci numai eventual pe laturi. Vom considera placa ˆ ıntr-un sistem cartezian.io.

for(i=1. x[i]=(int) st.x2.nval.y1. Se cere s˘ a se g˘ aseasc˘ a o strategie de mutare a discurilor de pe tija A pe tija C respectˆ and urm˘ atoarele reguli: .i++) if((x1<x[i])&&(x[i]<x2)&&(y1<y[i])&&(y[i]<y2)) { gasit=true.y[i]).y[i]. } } } 1.x2.y2).close().i<=n. } dr(x1.println(x1s+" "+y1s+" "+x2s+" "+y2s).nextToken().int y2) { int i. } static void dr(int x1.int x2. y2s=y2.2.y2). break. dr(x1. dr(x1.y1. EXEMPLE DE ALGORITMI DIVIDE ET IMPERA 7 x=new int [n+1].println(amax).y2). aranjate ˆ ın ordine descresc˘ atoare a diametrelor de la baz˘ a spre vˆ arf.x[i]. boolean gasit=false. if(s<=amax) return.4 Turnurile din Hanoi Se dau trei tije verticale A. B2 ¸ si C.nval.nextToken().x2.s=(x2-x1)*(y2-y1). } else { amax=s.x2. y=new int [n+1].1.y1. st. for(i=1.int y1.i<=n.2. x1s=x1.y1. out. Pe tija A se g˘ asesc n discuri de diametre diferite. out. out. y1s=y1.y2).i++) { st. } if(gasit) { dr(x1. x2s=x2. y[i]=(int) st. dr(x[i].

System. char tijaSursa. ’C’). char tijaIntermediara. BufferedReader br = new BufferedReader(new InputStreamReader(System.parseInt(br.print("Introduceti numarul discurilor: ").out. }// main() static void solutiaHanoi(int nrDiscuri.*. nrDiscuri=Integer. ’B’.io. ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY • la un moment dat se va muta un singur disc (cel care se afl˘ a deasupra celorlalte discuri pe o tij˘ a).8 CAPITOLUL 1. solutiaHanoi(nrDiscuri.in)). ˆ Imp˘ art ¸im problema astfel: • se mut˘ a primele n − 1 discuri de pe tija surs˘ a A pe tija intermediar˘ aB • se mut˘ a ultimul disc de pe tija surs˘ a A pe tija destinat ¸ie C • se mut˘ a cele n − 1 discuri de pe tija intermediar˘ a B pe tija destinat ¸ie C pasul 1 A B a) C A B b) pasul 2 C pasul 3 A B d) C A B c) C import java.readLine()). ’A’. class Hanoi { static int nrMutare=0. • un disc poate fi a¸ sezat doar peste un disc de diametru mai mare decˆ at al s˘ a sau pe o tij˘ a goal˘ a. public static void main(String[] args) throws IOException { int nrDiscuri. char tijaDestinatie) { if(nrDiscuri==1) .

tijaDestinatie). class HanoiDisc { static int nrMutare=0.*. solutiaHanoi(nrDiscuri-1.tijaIntermediara.nc.io. else { solutiaHanoi(nrDiscuri-1.out. static final int MAX_NR_DISCURI=9. b=new int[MAX_NR_DISCURI].println(++nrMutare + " Disc 1 " + tijaSursa + " ==> "+ tijaDestinatie). . // na = nr. static int discMutat. discuri pe tija A. c=new int[MAX_NR_DISCURI].tijaDestinatie.tijaIntermediara). static int[] a=new int[MAX_NR_DISCURI].nb.1.out.in)). EXEMPLE DE ALGORITMI DIVIDE ET IMPERA 9 System. static int na.. .2. public static void main(String[] args) throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System. etc.tijaSursa. System.println(++nrMutare + " Disk " + nrDiscuri + " " + tijaSursa + " --> "+ tijaDestinatie). } }// solutiaHanoi() }// class Introduceti 1 Disc 1 A 2 Disk 2 A 3 Disc 1 B 4 Disk 3 A 5 Disc 1 C 6 Disk 2 C 7 Disc 1 A 8 Disk 4 A 9 Disc 1 B 10 Disk 2 B 11 Disc 1 C 12 Disk 3 B 13 Disc 1 A 14 Disk 2 A 15 Disc 1 B numarul discurilor: 4 ==> B --> C ==> C --> B ==> A --> B ==> B --> C ==> C --> A ==> A --> C ==> B --> C ==> C import java.tijaSursa..

’C’). nrDiscuri = Integer.out. afisareDiscuri(). char tijaIntermediara. System.k++) a[k]=na-k.print("Numar discuri"+"[1<=.print(++nrMutare + " Disc " + nrDiscuri + " " + tijaSursa + " --> "+ tijaDestinatie + " ").out. case ’B’: discMutat=b[(nb--)-1]. solutiaHanoi(nrDiscuri-1.out.out. for(int k=0. } else { solutiaHanoi(nrDiscuri-1. afisareDiscuri(). System. break.. System. tijaSursa. } na=nrDiscuri. case ’C’: discMutat=c[(nc--)-1]. tijaDestinatie).println(). tijaIntermediara.println(). mutaDiscul(tijaSursa. System.tijaIntermediara).out. nc=0.10 CAPITOLUL 1. ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY int nrDiscuri=0. char tijaSursa. }// main() static void solutiaHanoi(int nrDiscuri..out. ’A’.parseInt(br. solutiaHanoi(nrDiscuri. nb=0. char tijaDestinatie) { switch (tijaSursa) { case ’A’: discMutat=a[(na--)-1]. char tijaDestinatie) { if (nrDiscuri==1) { mutaDiscul(tijaSursa. tijaDestinatie).<="+MAX_NR_DISCURI+"]: "). } .print(++nrMutare + " Disc " + "1" + " " + tijaSursa + " ==> "+ tijaDestinatie + " "). System. while((nrDiscuri<1)||(nrDiscuri>MAX_NR_DISCURI)) { System.k<na.println(). break.tijaDestinatie. ’B’. afisareDiscuri(). tijaDestinatie).readLine()). } } // solutiaHanoi() static void mutaDiscul(char tijaSursa.tijaSursa.

print(") "). }// afisareDiscuri() }// class Numar discuri[1<=.print(" B(").out.out.i++) System.print(a[i]). case ’B’: b[(++nb)-1]=discMutat.<=9]: 4 A(4321) 1 Disc 1 A ==> B A(432) 2 Disc 2 A --> C A(43) 3 Disc 1 B ==> C A(43) 4 Disc 3 A --> B A(4) 5 Disc 1 C ==> A A(41) 6 Disc 2 C --> B A(41) 7 Disc 1 A ==> B A(4) 8 Disc 4 A --> C A() 9 Disc 1 B ==> C A() 10 Disc 2 B --> A A(2) 11 Disc 1 C ==> A A(21) 12 Disc 3 B --> C A(21) 13 Disc 1 A ==> B A(2) 14 Disc 2 A --> C A() 15 Disc 1 B ==> C A() B() B(1) B(1) B() B(3) B(3) B(32) B(321) B(321) B(32) B(3) B(3) B() B(1) B(1) B() C() C() C(2) C(21) C(21) C(2) C() C() C(4) C(41) C(41) C(4) C(43) C(43) C(432) C(4321) 1.5 ˆ Injum˘ at˘ a¸ tire repetat˘ a .out.print(") ").out. case ’C’: c[(++nc)-1]=discMutat.1. break.i<nb.2.print(b[i]). System.2.print(" C(").print(") ").i<nc.print(c[i]). for(int i=0.i++) System.out.out. System. System.print(" A("). for(int i=0.out.out.. System. break. System.i++) System. EXEMPLE DE ALGORITMI DIVIDE ET IMPERA 11 switch (tijaDestinatie) { case ’A’: a[(++na)-1]=discMutat. for(int i=0. } }// mutaDiscul() static void afisareDiscuri() { System.i<na.out..

. unde di ∈ {S. "+dr+" a ramas! \n").. xn ¸ si o succesiune de decizii d1 .. class Injumatatire1 { static int n=10. if(impar) dr--. una dup˘ a alta..’S’}. Se cere acest element. } i++... while(st<dr) { System.’S’. 10 a ramas! 6 .dr=n.println(st+" . "+dr+" elimin "+d[i]). System. .out.. . if(d[i]==’S’) // elimin jumatatea stanga { st=m+1. x2 . Deciziile se aplic˘ aˆ ın ordine. if((dr-st+1)%2==1) impar=true.out. ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY Se consider˘ a un ¸ sir de numere naturale x1 .. ¸ sirul r˘ amas ˆ ın acel moment se ˆ ımparte ˆ ın dou˘ a sub¸ siruri cu num˘ ar egal elemente (dac˘ a num˘ arul de elemente este impar..12 CAPITOLUL 1.m.. 10 elimin S 6 ... } else // elimin jumatatea dreapta { dr=m.’S’. else impar=false. elementul situat la mijloc se elimin˘ a) ¸ si se elimin˘ a jum˘ atatea din partea stˆ ang˘ aa¸ sirului dac˘ a di = S (dac˘ a di = D se elimin˘ a jum˘ atatea din partea drept˘ a). D} au urm˘ atoarea semnificat ¸ie: la pasul i.. boolean impar.’D’. 10 elimin D . m=(st+dr)/2. ! public static void main(String[]args) { int st=1.. // nu folosesc d_0 . pˆ an˘ a cˆ and r˘ amˆ ane un singur element. d2 .i=1.println(st+" .... } }//main }//class 1 . static char[] d={’X’.

System.) }//class 13 .dr0.m.n.1).println(" --> "+st0). boolean impar.. }// divide(. if(impar) dr--.int dr0. 7 a ramas! 6 . return. if(st0<=dr) divide(st0.. if((dr0-st0+1)%2==1) impar=true. public static void main(String[]args) { divide(1. else impar=false.out.print(d[m]). int i) { int st.2. EXEMPLE DE ALGORITMI DIVIDE ET IMPERA 6 .. 7 a ramas! Toate elementele care pot r˘ amˆ ane: class Injumatatire2 { static int n=10.dr. // elimin jumatatea dreapta dr=m.i+1)..m<=i. static char[] d=new char[10].dr. } m=(st0+dr0)/2.i+1).1..out. // elimin jumatatea stanga st=m+1.. d[i]=’D’.. }//main static void divide(int st0.m++) System.. if(st0==dr0) { for(m=1. d[i]=’S’. if(st<=dr0) divide(st. 7 elimin S 7 .

Se pleac˘ a de la solut ¸ia vid˘ a pentru multimea S ¸ si se ia pe rˆ and cˆ ate un element din mult ¸imea C . a problemei . (solut ¸ia problemei) care ˆ ındepline¸ ste anumite condit ¸ii. iar S ′ este inclus˘ aˆ ın S . nu neap˘ arat optim˘ a. optimul global. Metoda Greedy se aplic˘ a urm˘ atorului tip de problem˘ a: dintr-o mult ¸ime de elemente C (candidat ¸i la construirea solut ¸iei problemei). atunci metoda Greedy este aplicabil˘ a cu succes. adic˘ a dac˘ a este posibil s˘ a complet˘ am aceast˘ a mult ¸ime astfel ˆ ıncˆ at s˘ a obt ¸inem o solut ¸ie posibil˘ a. etc. vˆ arfuri ale grafului. Dac˘ a elementul ales ˆ ındepline¸ ste condit ¸ia de optim local. Exist˘ a urm˘ atoarele variante ale metodei Greedy: 1.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. ˆ In cele mai multe situat ¸ii de acest fel avem: • o mult ¸ime de candidat ¸i (lucr˘ ari de executat.14 SSS SSD SDS SDD DSS DSD DDS DDD --> --> --> --> --> --> --> --> CAPITOLUL 1. ˆ ın general. O problem˘ a poate fi rezolvat˘ a prin tehnica (metoda) Greedy dac˘ aˆ ındepline¸ ste proprietatea: dac˘ a S este o solut ¸ie. ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY 10 9 7 6 5 4 2 1 1. nu neap˘ arat optim˘ a. atunci ¸ si S ′ este o solut ¸ie. init ¸ial se presupune c˘ a S este mult ¸imea vid˘ a ¸ si se adaug˘ a succesiv elemente din C ˆ ın S . se cere s˘ a se determine o submult ¸ime S . Dac˘ a se demonstreaz˘ a c˘ a succesiunea de optimuri locale conduce la optimul global. Algoritmii greedy sunt ˆ ın general simpli ¸ si sunt folosit ¸i la rezolvarea unor probleme de optimizare.) • o funct ¸ie care verific˘ a dac˘ a o anumit˘ a mult ¸ime de candidat ¸i constituie o solut ¸ie posibil˘ a. 2. ajungˆ and la un optim local. a problemei • o funct ¸ie care verific˘ a dac˘ a o mult ¸ime de candidat ¸i este fezabil˘ a. Se ordoneaz˘ a elementele mult ¸imii C ¸ si se verific˘ a dac˘ a un element ˆ ındepline¸ ste condit ¸ia de apartenent ¸˘ a la mult ¸imea S . Pornind de la aceast˘ a condit ¸ie.3 Metoda optimului local . Succesiunea de optimuri locale nu asigur˘ a. el este introdus ˆ ın mult ¸imea S . Deoarece este posibil s˘ a existe mai multe solut ¸ii se va alege solut ¸ia care maximizeaz˘ a sau minimizeaz˘ a o anumit˘ a funct ¸ie obiectiv.

codificarea Huffman. dup˘ a ad˘ augare. determinarea celor mai scurte drumuri care pleac˘ a din acela¸ si punct (algoritmul lui Dijkstra). ˆ ıncerc˘ am s˘ a ad˘ aug˘ am la aceast˘ a mult ¸ime pe cel mai promit ¸a ˘tor candidat. Dac˘ a algoritmul greedy funct ¸ioneaz˘ a corect. ultimul candidat ad˘ augat va r˘ amˆ ane de acum ˆ ıncolo ˆ ın ea. mult ¸imea candidat ¸ilor selectat ¸i este vid˘ a. Solut ¸ia optim˘ a nu este ˆ ın mod necesar unic˘ a: se poate ca funct ¸ia obiectiv s˘ a aib˘ a aceea¸ si valoare optim˘ a pentru mai multe solut ¸ii posibile. Init ¸ial. mult ¸imea de candidat ¸i selectat ¸i este fezabil˘ a. conform funct ¸iei de select ¸ie. problema color˘ arii intervalelor. etc) ¸ si pe care urm˘ arim s˘ a o optimiz˘ am (minimiz˘ am/maximiz˘ am) Pentru a rezolva problema de optimizare. Descrierea formal˘ a 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 f ezabil(S ∪ {x}) then S ← S ∪ {x} if solutie(S ) then return S else return ”nu exist˘ a solut ¸ie” 1. . La fiecare pas. etc. Un algoritm greedy construie¸ ste solut ¸ia pas cu pas. mult ¸imea de candidat ¸i selectat ¸i nu mai este fezabil˘ a.4. dup˘ a o astfel de ad˘ augare. elimin˘ am ultimul candidat ad˘ augat.1. sortare prin select ¸ie. Dac˘ a. De fiecare dat˘ a cˆ and l˘ argim mult ¸imea candidat ¸ilor selectat ¸i. lungimea drumului pe care l-am g˘ asit. determinarea arborelui de cost minim (algoritmii lui Prim ¸ si Kruskal). problema rucsacului. c˘ aut˘ am o solut ¸ie posibil˘ a care s˘ a optimizeze valoarea funct ¸iei obiectiv. verific˘ am dac˘ a aceast˘ a mult ¸ime constituie o solut ¸ie posibil˘ a a problemei.4 Exemple de algoritmi greedy Dintre problemele clasice care se pot rezolva prin metoda greedy ment ¸ion˘ am: plata restului cu num˘ ar minim de monezi. EXEMPLE DE ALGORITMI GREEDY 15 • 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 d˘ a valoarea unei solut ¸ii (timpul necesar execut˘ arii tuturor lucr˘ arilor ˆ ıntr-o anumit˘ a ordine. acesta nu va mai fi niciodat˘ a considerat. Dac˘ a. determinarea mult ¸imii dominante. prima solut ¸ie g˘ asit˘ a va fi considerat˘ a solut ¸ie optim˘ aa problemei.

. 1] partea din obiectul i care a fost pus˘ aˆ ın rucsac.. x2 . de exemplu. 2.. ˆ ın [25] la pagina 226.. Demonstrat ¸ia se g˘ ase¸ ste.16 CAPITOLUL 1. Obiectul i are greutatea gi ¸ si valoarea vi (1 ≤ i ≤ n). xi . 1]. Vom nota Gp greutatea permis˘ a de a se ˆ ınc˘ arca ˆ ın rucsac la un moment dat. Practic. n if Gp > gi then Gp ← Gp − gi else Gp xi ← gi for j = i + 1. . Din aceast˘ a cauz˘ a presupunem ˆ ın continuare c˘ a v1 v2 vn ≥ ≥ . 1] ¸ si 1 ≤ i ≤ n. . . procedura de rezolvare a problemei este urm˘ atoarea: procedure rucsac() Gp ← G for i = 1. 1.. Not˘ am cu xi ∈ [0.. Propozit ¸ia 1 Procedura rucsac() furnizeaz˘ a o solut ¸ie optim˘ a. Fie G greutatea maxim˘ a suportat˘ a de rucsac.. O modalitate de a ajunge la solut ¸ia optim˘ a este de a considera obiectele ˆ ın ordinea descresc˘ atoare a valorilor i utilit˘ a¸ tilor lor date de raportul v si de a le ˆ ınc˘ arca ˆ ıntregi ˆ ın rucsac gi (i = 1. ∀i = 1. 0) cu xi ∈ (0.. Se permite fract ¸ionarea obiectelor (valoarea p˘ art ¸ii din obiectul fract ¸ionat fiind direct proport ¸ional˘ a cu greutatea ei!). Pentru rezolvare vom folosi metoda greedy.. n n i=1 xi gi ≤ G iar o solut ¸ie posibil˘ a este solut ¸ie optim˘ a dac˘ a maximizeaz˘ a funct ¸ia f . n) ¸ pˆ an˘ a cˆ and acesta se umple.. trebuie s˘ a maximiz˘ am funct ¸ia n f (x) = i=1 xi ci .1 Problema continu˘ a a rucsacului Se consider˘ a n obiecte. Conform strategiei greedy. . ... ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY 1. g1 g2 gn Vectorul x = (x1 . ≥ .. 0. O persoan˘ a are un rucsac..... . Persoana ˆ ın cauz˘ a dore¸ ste s˘ a pun˘ aˆ ın rucsac obiecte astfel ˆ ıncˆ at valoarea celor din rucsac s˘ a fie cˆ at mai mare. n xj ← 0 Vectorul x are forma x = (1. xn ) se nume¸ ste solut ¸ie posibil˘ a dac˘ a xi ∈ [0.4.

4.. Aplicˆ and de un num˘ ar finit de ori acest rat ¸ionament. trecem de la o permutare optim˘ a la alt˘ a permutare optim˘ a pˆ an˘ a ajungem la permutarea identic˘ a.Tp(n) . Rezolvarea problemei este foarte simpl˘ a: • se sorteaz˘ a cresc˘ ator textele ˆ ın funct ¸ie de lungimea lor ¸ si • se plaseaz˘ a pe band˘ aˆ ın ordinea dat˘ a de sortare.1. Cum p este o plasare optim˘ a¸ si f (p′ ) ≤ f (p) rezult˘ a c˘ a f (p′ ) = f (p) deci ¸ s i p′ este o plasare optim˘ a.2 Problema plas˘ arii textelor pe o band˘ a S˘ a presupunem c˘ a trebuie s˘ a plas˘ am n texte T1 . T2 . p2 . de lungimi date L1 . Atunci f (p′ ) − f (p) = (n − j + 1)(Lp(i) − Lp(j ) ) + (n − i + 1)(Lp(j ) − Lp(i) ) deci f (p′ ) − f (p) = (Lp(j ) − Lp(i) )(j − i) ≤ 0... .4. Dac˘ a exist˘ a i < j cu Lp(i) ≥ Lp(j ) atunci consider˘ am permutarea p′ obt ¸inut˘ a din p prin permutarea elementelor de pe pozit ¸iile i ¸ si j ... T2 . Atunci cˆ and este necesar˘ a citirea unui text sunt citite toate textele situate ˆ ınaintea lui pe band˘ a.. Tn .. . ˆ Intr-o astfel de aranjare a textelor pe band˘ a... Propozit ¸ia 2 Dac˘ a L1 ≤ L2 ≤ . . pe o singur˘ a band˘ a suficient de lung˘ a. Se dore¸ ste determinarea unei plas˘ ari a celor n texte pe cele m benzi care s˘ a asigure o valoare minim˘ a a timpului mediu de citire.... . . L2 . EXEMPLE DE ALGORITMI GREEDY 17 1.. Ln . L2 . Tn . Rezult˘ a c˘ a permutarea identic˘ a corespunde unei plas˘ ari optime. 1. n}... Atunci cˆ and este necesar˘ a citirea unui text sunt citite toate textele situate ˆ ınaintea lui pe banda respectiv˘ a. pn ) o plasare optim˘ a. textele fiind a¸ sezate pe band˘ aˆ ın ordinea Tp(1) Tp(2) . timpul mediu de citire a unui text este: k n 1 f (p) = Lp(i) n i=1 k=1 Se dore¸ ste determinarea unei permut˘ ari care s˘ a asigure o valoare minim˘ aa timpului mediu de citire. Urm˘ atoarea propozit ¸ie ([25] pagina 99) ne permite s˘ a fim siguri c˘ aˆ ın acest mod ajungem la o plasare optim˘ a. Demonstrat ¸ie: Fie p = (p1 .4. .... Modalitatea de plasare a celor n texte pe band˘ a corespunde unei permut˘ ari p a mult ¸imii {1. Rezolvarea problemei este foarte simpl˘ a: . ≤ Ln atunci plasarea textelor corespunz˘ atoare permutarii identice este optim˘ a..3 Problema plas˘ arii textelor pe m benzi S˘ a presupunem c˘ a trebuie s˘ a plas˘ am n texte T1 .. pe m benzi suficient de lungi. 2.. de lungimi date L1 . Ln ..

.4.. a2 .. se poate observa c˘ a textul Ti va fi plasat 1 pe banda i − i− · m ˆ ın continuarea celor aflate deja pe aceast˘ a band˘ a iar dup˘ a m textul Ti .. b2 . x2 = b2 . xn1 = bn1 ¸ 1.. ≤ an ¸ si b1 ≤ b2 ≤ . unde n ≤ m. Rezolvarea problemei este urm˘ atoarea: • se alege stat ¸ia 1. ≤ bm .18 CAPITOLUL 1. ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY • se sorteaz˘ a cresc˘ ator textele ˆ ın funct ¸ie de lungimea lor ¸ si • plasarea textelor pe benzi se face ˆ ın ordinea dat˘ a de sortare. .4.... an } care reprezint˘ a coordonatele a n stat ¸ii pe axa real˘ a. x2 .. . ¸ si a¸ sa mai departe. vor fi plasate textele i + m.. ˆ ın [25]). ≤ Ln .. Vom presupune a1 < a2 < . pe aceea¸ si band˘ a cu el. ¸ si a¸ sa mai departe. xn } a lui B astfel ˆ ıncˆ at valoarea expresiei E = a1 · x1 + a2 · x2 + an · xn s˘ a fie maxim˘ a. < an . se repet˘ a acest pas pˆ an˘ a cˆ and s-au verificat toate stat ¸iile. Dac˘ a primele n1 elemente din A sunt strict negative ¸ si ultimile n2 elemente din A sunt pozitive sau nule (n1 + n2 = n) atunci vom lua x1 = b1 .. xn−1 = bm−1 . Dac˘ a toate elementele din A sunt pozitive vom lua xn = bm .. . Propozit ¸ia 3 Exist˘ a o solut ¸ie optim˘ a care cont ¸ine stat ¸ia 1. a2 .. m 1.. xn−n2 +1 = bm−n2 +1 . deci ˆ ınc˘ a n−i texte (demonstrat ¸iile se pot consulta. . Vom sorta cresc˘ ator elementele celor dou˘ a mult ¸imi. S˘ a se determine o submult ¸ime B ′ = {x1 . ˆ ıncepˆ and cu primul text (cu cea mai mic˘ a lungime) care se plaseaz˘ a pe prima band˘ a..5 Problema stat ¸iilor Se consider˘ a o mult ¸ime de numere naturale A = {a1 . Presupunˆ and c˘ a L1 ≤ L2 ≤ . an } ¸ si B = {b1 . .. iar mai departe • fiecare text se plaseaz˘ a pe banda urm˘ atoare celei pe care a fost plasat textul anterior.. bm }.. si xn = bm . xn−1 = bm−1 . Putem presupune acum c˘ a a1 ≤ a2 ≤ . • se parcurge ¸ sirul stat ¸iilor ¸ si se alege prima stat ¸ie ˆ ıntˆ alnit˘ a care este la distant ¸˘ a cel put ¸in d fat ¸˘ a de stat ¸ia aleas˘ a anterior. i + 2m..... x2 = b2 .. S˘ a se determine o submult ¸ime cu num˘ ar maxim de stat ¸ii cu proprietatea c˘ a distant ¸a dintre dou˘ a stat ¸ii al˘ aturate este cel putin d (o distant ¸˘ a dat˘ a). Dac˘ a toate elementele din A sunt negative vom lua x1 = b1 . ... ..4 Maximizarea unei sume de produse Se dau mult ¸imile de numere ˆ ıntregi A = {a1 .. . de exemplu..

aim } o solut ¸ia i1 se poate ˆ ınlocui cu stat ¸ia stat ¸ia 1 (deci ai1 > a1 ).4. Evident |ai2 − ai1 | ≥ d. Stat 1 pentru c˘ a |ai2 − a1 | = |ai2 − ai1 + ai1 − a1 | = |ai2 − ai1 | + |ai1 − a1 | > d. ai2 . Pentru aceste stat ¸ii repet˘ am strategia sugerat˘ a de propozit ¸ia anterioar˘ a. se afl˘ a num˘ arul 3. vom repeta acest rat ¸ionament pˆ an˘ a cˆ and pozit ¸iile cutiilor goale. . 1 2 3 4 5 6 7 modificat → 1 3 2 6 5 4 final → 1 2 3 4 5 6 rezolvat → × × 1 2 3 4 5 6 7 modificat → 1 3 2 6 5 4 final → 1 2 3 4 5 6 rezolvat → × × 1 2 3 4 5 6 7 modificat → 1 2 3 6 5 4 final → 1 2 3 4 5 6 rezolvat → × × × × Acum vom c˘ auta o cutie nerezolvat˘ a¸ si vom muta num˘ arul din acea cutie ˆ ın cutia goal˘ a. cutia goal˘ a se afl˘ a pe pozit ¸ia 1. EXEMPLE DE ALGORITMI GREEDY 19 ¸ie a problemei care nu cont ¸ine Demonstrat ¸ie: Fie B = {ai1 . ˆ ın configurat ¸ia final˘ a.1. Dup˘ a selectarea stat ¸ie 1 se pot selecta (pentru obt ¸inerea unei solut ¸ii optime) numai stat ¸ii situate la distant ¸e cel put ¸in d fat˘ a de stat ¸ia 1. ˆ ın cele dou˘ a configurat ¸ii.. O mutare se poate face dintr-o anumit˘ a pozit ¸ie numai ˆ ın pozit ¸ia liber˘ a. acum.. c˘ aut˘ am num˘ arul 3 din configurat ¸ia init ¸ial˘ a (ˆ ıl g˘ asim pe pozit ¸ia 1) ¸ si ˆ ıl mut˘ am pe pozit ¸ia cutiei goale.4. 1. 1 2 3 4 5 6 7 modificat → 1 2 3 6 5 4 final → 1 2 3 4 5 6 rezolvat → × × × × . coincid. Prezent˘ am algoritmul de rezolvare pe baza urm˘ atorului exemplu: 1 2 3 4 5 6 7 init ¸ial → 3 1 2 6 5 4 final → 1 2 3 4 5 6 rezolvat → × Vom proceda astfel: cutia goal˘ a din configurat ¸ia init ¸ial˘ a se afl˘ a pe pozit ¸ia 3 dar pe aceast˘ a pozit ¸ie.6 Problema cutiilor Se dore¸ ste obt ¸inerea unei configurat ¸ii de n numere plasate pe n + 1 pozit ¸ii (o pozit ¸ie fiind liber˘ a) dintr-o configurat ¸ie init ¸ial˘ a dat˘ aˆ ın care exist˘ a o pozit ¸ie liber˘ a ¸ si cele n numere plasate ˆ ın alt˘ a ordine..

[an . x2n ) ¸ si y = (y1 . . ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY Repet˘ am rat ¸ionamentul prezentat la ˆ ınceput ¸ si vom continua pˆ an˘ a cˆ and toate numerele ajung pe pozit ¸iile din configurat ¸ia final˘ a. z2n ).9 Problema alegerii taxelor Se dau dou˘ a¸ siruri cu cˆ ate 2n numere ˆ ıntregi fiecare. z2 .... x2 . b1 ].. 1 2 3 4 5 6 7 modificat → 1 2 3 6 4 5 final → 1 2 3 4 5 6 rezolvat → × × × × × 1 2 3 4 5 6 7 modificat → 1 2 3 4 5 6 final → 1 2 3 4 5 6 rezolvat → × × × × × × 1. . Observat ¸ie: Ultimile elemente din fiecare sub¸ sir (care sunt ¸ si elemente maxime din aceste sub¸ siruri) formeaz˘ a un ¸ sir descresc˘ ator. el devenind astfel ”ultimul interval ales”. . Acest fapt permite utilizarea c˘ aut˘ arii binare pentru determinarea sub¸ sirului potrivit pentru elementul xi atunci cˆ and prelucr˘ am acest element..4. Propozit ¸ia 4 Exist˘ a o solut ¸ie optim˘ a care cont ¸ine primul interval dup˘ a sortare. .20 CAPITOLUL 1.. se parcurg intervalele. y2 .... [a2 . y2n ) reprezentˆ and taxe... x = (x1 . acest procedeu continu˘ a pˆ an˘ a cˆ and nu mai r˘ amˆ an intervale de analizat.4. se va alege acela care are cel mai mare ultim element. dac˘ a exist˘ a mai multe sub¸ siruri la care se poate ad˘ auga xi .4. Metota de rezolvare este urm˘ atoarea: se parcurg elementele ¸ sirului init ¸ial unul dup˘ a altul ¸ si pentru fiecare element xi se caut˘ a un sub¸ sir existent la care xi se poate ad˘ auga ls sfˆ ar¸ sit. Metoda de rezolvare este urm˘ atoarea: se sorteaz˘ a intervalele cresc˘ ator dup˘ a cap˘ atul din dreapta.7 Problema sub¸ sirurilor S˘ a se descompun˘ a un ¸ sir de n numere ˆ ıntregi ˆ ın sub¸ siruri strict cresc˘ atoare astfel ˆ ıncˆ at numerele lor de ordine din ¸ sirul init ¸ial s˘ a fie ordonate cresc˘ ator ˆ ın sub¸ sirurile formate ¸ si num˘ arul sub¸ sirurilor s˘ a fie minim. unul dup˘ a altul. S˘ a se determine ¸ sirul z = (z1 . b2 ].. bi ] disjunct cu ultimul interval ales ¸ si se adaug˘ a acest interval la solut ¸ie.8 Problema intervalelor disjuncte Se consider˘ a n intervale ˆ ınchise pe axa real˘ a [a1 . bn ]. 1. Se cere selectarea unor intervale disjuncte astfel ˆ ıncˆ at num˘ arul acestora s˘ a fie maxim. 1. pˆ an˘ a se g˘ ase¸ ste un interval [ai . dac˘ a nu exist˘ a un astfel de sub¸ sir se creeaz˘ a un sub¸ sir nou care va cont ¸ine ca prim element pe xi . . se selecteaz˘ a primul interval.

b1 ].. Metoda de rezolvare este urm˘ atoarea: se sorteaz˘ a intervalele cresc˘ ator dup˘ a cap˘ atul din dreapta. Primul punct ales este c1 = b1 . [an ..1. bk ])... astfel ˆ ıncˆ at suma tuturor elementelor din ¸ sirul z s˘ a fie minim˘ a¸ si acest ¸ sir s˘ a cont ¸in˘ a exact n elemente din ¸ sirul x ¸ si n elemente din ¸ sirul y .. Presupunem c˘ a b1 ≤ b2 ≤ . Metoda de rezolvare este urm˘ atoarea: se construie¸ ste ¸ sirul t = x − y (ˆ ın care ti = xi − yi ) ¸ si se sorteaz˘ a cresc˘ ator.10 Problema acoperirii intervalelor Se consider˘ a n intervale ˆ ınchise [a1 . se aleg din ¸ sirul x elementele corespunz˘ atoare primelor n elemente din ¸ sirul t sortat iar celelalte n elemente se iau din ¸ sirul y . . . Repet˘ am acest procedeu pˆ an˘ a cˆ and termin˘ am de parcurs toate intervalele. 1. bn ]. b2 ]. bk ] dac˘ a ci ∈ [ak . bj ] cu aj > c2 ¸ si alegem c3 = bj . S˘ a se determine o mult ¸ime cu num˘ ar minim de alemente C = {c1 . Parcurgem mai departe intervalele pˆ an˘ a cand g˘ asim un interval [aj .4. ≤ bn . yi } (1 ≤ i ≤ 2n). cm } care s˘ a ”acopere” toate cele n intervale date (spunem c˘ a ci ”acoper˘ a” intervalul [ak .. EXEMPLE DE ALGORITMI GREEDY 21 unde zi ∈ {xi . .. [a2 . bi ] cu ai > c1 ¸ si alegem c2 = bi .4.. Parcurgem intervalele pˆ an˘ a cand g˘ asim un interval [ai . c2 .

ALGORITMI DIVIDE ET IMPERA S ¸ I GREEDY .22 CAPITOLUL 1.

ˆ In majoritatea cazurilor nu oricare element al produsului cartezian este solut ¸ie ci numai cele care satisfac anumite restrict ¸ii.. De exemplu. 2. sn ) ∈ {1... ∀i = j...... s2 .. 2. .1. 2. max = 9 atunci Si = {0... 1 ≤ i.. nu metoda backtracking ˆ ın sine este dificil de ˆ ınt ¸eles ci modalitatea de generare a produsului cartezian.1 Generarea produsului cartezian Pentru ˆ ıncep˘ atori.. . × Sn (cu mult ¸imile Sk finite) care are anumite propriet˘ a¸ ti.. sn ) al submult ¸imii produsului cartezian poate fi interpretat ca solut ¸ie a unei probleme concrete. . 2. .. 1.1 Generarea iterativ˘ a a produsului cartezian Presupunem c˘ a mult ¸imile Si sunt formate din numere ˆ ıntregi consecutive cuprinse ˆ ıntre o valoare min ¸ si o valoare max. s2 . Dorim s˘ a gener˘ am produsul cartezian S1 × S2 × . .. problema determin˘ arii tuturor combin˘ arilor de m elemente luate cˆ ate n (unde 1 ≤ n ≤ m) din mult ¸imea {1. m}n |si = sj .Capitolul 2 Metoda backtracking Metoda backtracking se utilizeaz˘ a pentru determinarea unei submult ¸imi a unui produs cartezian de forma S1 × S2 × . m} poate fi reformulat˘ a ca problema determin˘ arii submult ¸imii produsului cartezian {1. Metoda se aplic˘ a numai atunci cˆ and nu exist˘ a nici o alt˘ a cale de rezolvare a problemei propuse. × Sn 23 . De exemplu.. 9}. Fiecare element (s1 .. 2.. j ≤ n}. deoarece timpul de execut ¸ie este de ordin exponent ¸ial. m}n definit˘ a astfel: {(s1 ... dac˘ a min = 0.. .

... 0 1 2 3 4 5 0 1 . .. ˆ ın acest moment. a2 .. Se ajunge astfel la configurat ¸ia (k = 4 = n aici!) 0 1 2 3 4 5 0 0 0 9 0 1 .. Este totu¸ si util˘ a init ¸ializarea gol=min-1.. Cum ˆ ıncepem generarea produsului cartezian? O prim˘ a variant˘ a este s˘ a atribuim tuturor componentelor ai valoarea min ¸ si avem astfel un prim element al produsului cartezian. de ap˘ a.. La ˆ ınceput k = n ¸ si. . Este util˘ a. s-au epuizat toate valorile posibile. . . Folosim un vector cu n elemente a = (a1 . imaginea kilometrajelor de ma¸ sini sau a contoarelor de energie electic˘ a. deci k = k − 1 (acum k = 3).. n n+1 Trebuie s˘ a construim urm˘ atoarele elemente ale produsului cartezian.... n n+1 Ei bine. n n+1 Ce se ˆ ıntˆ ampl˘ a mai departe? Apare configurat ¸ia 0 1 2 3 4 5 0 0 1 0 ˆ ıntr-un mod ciudat! 0 1 .. etc. valorile se schimb˘ a crescˆ and de la min c˘ atre max.. n n+1 Ne trebuie un marcaj pentru a preciza faptul c˘ a o anumit˘ a component˘ a este goal˘ a (nu are plasat˘ aˆ ın ea o valoare valid˘ a)... pe aceast˘ a pozit ¸ie k . max]. n n+1 Folosim o variabil˘ a k pentru a urm˘ ari pozit ¸ia din vector pe care se schimb˘ a valorile. odat˘ a cu aparit ¸ia valorii 9 = max. atunci am descoperit singuri aceast˘ a metod˘ a! Pe pozit ¸ia k = n... n n+1 0 1 2 3 4 5 0 0 0 2 0 1 .. METODA BACKTRACKING (de exemplu. 0 1 2 3 4 5 0 0 0 0 1 .... pe care putem s˘ a-l afi¸ sa ˘m. a¸ sa c˘ a vom considera c˘ a pozit ¸ia k este goal˘ a¸ si vom trece pe o pozit ¸ie mai la stˆ anga.. . ˆ In acest scop folosim variabila gol care poate fi init ¸ializat˘ a cu orice valoare ˆ ıntreag˘ a care nu este ˆ ın intervalul [min.. de gaze. se poate spune c˘ a aici este cheia metodei backtraching! Dac˘ a reu¸ sim s˘ aˆ ınt ¸elegem acest pas de trecere de la o configurat ¸ie la alta ¸ si s˘ a formaliz˘ am ce am observat... 0 1 2 3 4 5 0 0 0 0 0 1 . n n+1 . an ) ¸ si vom atribui fiec˘ arei componente ai valori cuprinse ˆ ıntre min ¸ si max.....24 CAPITOLUL 2. . pentru n = 4).. Urm˘ atoarea configurat ¸ie a acestora este 0 1 2 3 4 5 0 0 0 1 dup˘ a care urmeaz˘ a 0 1 ... .

0 1 2 3 4 5 0 0 1 0 0 1 ..... . S˘ a consider˘ am c˘ a s-a ajuns la configurat ¸ia 0 1 2 3 4 5 0 0 9 9 0 1 .. ... deci k = k − 1 0 1 2 3 4 5 0 0 0 1 . 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 stˆ anga. se face pasul spre dreapta (k = k + 1).. 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.2. . .. GENERAREA PRODUSULUI CARTEZIAN 25 Pe pozit ¸ia k = 3 se af˘ a valoarea 0 care se va schimba cu urm˘ atoarea valoare (dac˘ a exist˘ a o astfel de valoare ≤ max).. n n+1 Aici k = 2 ¸ si 0 < max.. n n+1 Aici k = 4 = n ¸ si 9 = max.1.... 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 ..... 0 1 2 3 4 5 0 0 1 0 1 .. .. 0 1 2 3 4 5 0 0 1 0 1 . n n+1 Dup˘ a plasarea unei valori pe pozit ¸ia k . ˆ ın urma pasului f˘ acut spre dreapta!). se plaseaz˘ a prima valoare valid˘ a (deci valoarea min). deci k = k − 1 0 1 2 3 4 5 0 0 9 0 1 . deci ˆ ın acest caz cu 1. n n+1 Aici (dac˘ a nu am ie¸ sit ˆ ın afara vectorului. n n+1 Cum trecerea de la o valoare la alta se face prin cre¸ sterea cu o unitate a valorii vechi iar acum facem trecerea gol → min. .. .... ˆ ın cazul nostru k = 4 ¸ si pozit ¸ia este goal˘ a.. ˆ ınt ¸elegem de ce este util s˘ a init ¸ializ˘ am variabila gol cu valoarea min − 1. deci k = k + 1 0 1 2 3 4 5 0 1 0 1 . 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 stˆ anga.. n n+1 Aici k = 3 ¸ si gol=-1< max.. n n+1 Aici k = 3 ¸ si 9 = max..

• dac˘ a nu se poate: se pune gol=min − 1 ¸ si se face pasul spre stˆ anga. n n+1 0 1 2 3 4 5 9 9 9 se gole¸ ste pozit ¸ia ¸ si ajungem la k = 2 0 1 .. Ajungem la k = 4 0 1 .... . n n+1 0 1 2 3 4 5 9 9 9 9 se gole¸ ste pozit ¸ia ¸ si ajungem la k = 3 0 1 .. .. . n n+1 Aici k = 4 ¸ si gol=-1< max..... n n+1 A ap˘ arut ˆ ın mod evident urm˘ atoarea regul˘ a: ajungˆ and pe pozit ¸ia k ≤ n.... n n+1 0 1 2 3 4 5 9 9 se gole¸ ste pozit ¸ia ¸ si ajungem la k = 1 0 1 .. deci k = k + 1 0 1 2 3 4 5 0 1 0 0 0 1 . n n+1 0 1 2 3 4 5 9 se gole¸ ste pozit ¸ia ¸ si ajungem la k = 0 0 1 . deci k = k + 1 0 1 2 3 4 5 0 1 0 0 1 .... 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. METODA BACKTRACKING • se face pasul spre dreapta.. .. n n+1 Nu-i nimic! Aici s-a terminat generarea produsului cartezian! . . Presupunem c˘ a s-a ajuns la configurat ¸ia 0 1 2 3 4 5 9 9 9 9 care se afi¸ seaz˘ a ca solut ¸ie... n n+1 0 1 2 3 4 5 iar aici k = 0 ¸ si am ajuns ˆ ın afara vectorului! 0 1 . .. ˆ ıncerc˘ am s˘ a punem urm˘ atoarea valoare (gol are ca ”urm˘ atoarea valoare” pe min) pe aceast˘ a pozit ¸ie ¸ si • dac˘ a se poate: se pune urm˘ atoarea valoare ¸ si se face pasul spre dreapta. .... . n n+1 iar aici k = n + 1 ¸ si am ajuns ˆ ın afara vectorului! Nu-i nimic! Se afi¸ seaz˘ a solut ¸ia 0100 ¸ si se face pasul la stˆ anga....26 CAPITOLUL 2.. deci k = k + 1 0 1 2 3 4 5 0 1 0 1 0 1 .... Aici k = 4 ¸ si 0 < 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..

print(a[i]).i<=n. static void afis(int[] a) { for(int i=1. */ --k. altfel. se gole¸ ste pozit ¸ia curent˘ a¸ si se face pasul spre stˆ anga 3’.currentTimeMillis(). k. long t1. revenire la 3. t1=System.000 ms pentru 8 cu 14 { static int n=8. dac˘ a este deja construit˘ a o solut ¸ie 5. se afi¸ seaz˘ a solut ¸ia ¸ si se face pasul spre stˆ anga 6.1.println("Timp = "+(t2-t1)+" ms"). k=n+1. max}: • structuri de date: − a[1. max=14.i<=n. min + 1. gol=min-1.} else { if (a[k]<max) ++a[k++].out. while (k>0) if (k==n+1) {/* afis(a).. cu convent ¸iile k = 0 precizeaz˘ a terminarea gener˘ arii.println().i++) System. se construie¸ ste prima solut ¸ie 2.out. System.t2..i++) a[i]=min.n] vectorul solut ¸ie − k ”pozit ¸ia” ˆ ın vectorul solut ¸ie. unde S = {min. se m˘ are¸ ste cu 1 valoarea ¸ si se face pasul spre dreapta 8. . System.currentTimeMillis(). • proces de calcul: 1. static int[] a=new int[n+1]. for(i=1.out.. dac˘ a se poate m˘ ari valoarea pe pozit ¸ia curent˘ a 7. GENERAREA PRODUSULUI CARTEZIAN 27 Putem descrie acum algoritmul pentru generarea produsului cartezian S n . min=1. } } . cˆ at timp nu s-a terminat generarea 4. else a[k--]=gol. iar k = n + 1 precizeaz˘ a faptul c˘ a s-a construit o solut ¸ie care poate fi afi¸ sat˘ a.. altfel. Implementarea acestui algoritm ˆ ın Java este urm˘ atoarea: class ProdCartI1 // 134. } public static void main (String[] args) { int i. } t2=System.2. se plaseaz˘ a pozit ¸ia ˆ ın afara vectorului (ˆ ın dreapta) 3.

println(). while (k>0) if (k==n+1) {/* afis(a).currentTimeMillis().print(a[i]).out. static int[] a=new int[n+1]. min=1.000 ms pentru 8 cu 14 { static int n=8. Nu mai avem ˆ ın acest caz o solut ¸ie gata construit˘ a dar algoritmul este acela¸ si. k. static void afis(int[] a) { for(int i=1.28 CAPITOLUL 2. } public static void main (String[] args) { int i.} else { if (a[k]<max) ++a[k++].out. max=14. for(i=1.750 ms pentru 8 cu 14 { static int n=8. static void afis(int[] a) { for(int i=1.println(). System.out.i<=n. } } 2.currentTimeMillis(). long t1.out.2 Generarea recursiv˘ a a produsului cartezian class ProdCartR1 // 101.t2. METODA BACKTRACKING O alt˘ a variant˘ a ar putea fi init ¸ializarea vectorului solut ¸ie cu gol ¸ si plasarea pe prima pozit ¸ie ˆ ın vector.out. static int[] a=new int[n+1]. System. } t2=System. gol=min-1.i++) System. System.i++) System.max=14.1.println("Timp = "+(t2-t1)+" ms"). else a[k--]=gol. k=1. t1=System.print(a[i]).i<=n.i<=n. min=1.i++) a[i]=gol. Implementarea ˆ ın acest caz este urm˘ atoarea: class ProdCartI2 // 134. */ --k. } .

currentTimeMillis().1.i<=max.println("Timp = "+(t2-t1)+" ms").i++) System.i<=n. System.2. } } public static void main (String[] args) { long t1.currentTimeMillis(). } } . System.t2. t2=System. f(1). } } class ProdCartR2 // 71.i<=max. if (k>n) {/* afis(a).println("Timp = "+(t2-t1)+" ms").currentTimeMillis().out.out.out. } } public static void main (String[] args) { long t1.300 ms pentru 8 cu 14 { static int n=8. static void afis(int[] a) { for(int i=1. t1=System.println(). t2=System. for(i=min. t1=System. min=1. } static void f(int k) { int i.out. static int[] a=new int[n+1].i++) { a[k]=i. GENERAREA PRODUSULUI CARTEZIAN 29 static void f(int k) { int i.t2. System. */ return. f(1).max=14. }.currentTimeMillis().print(a[i]). if (k<n) f(k+1). f(k+1).i++) { a[k]=i. for(i=min.

print(x[i]). f(1).’X’}.’A’.i<=m.30 CAPITOLUL 2. if(k<n) f(k+1). public static void main(String[] args) { n=4.i++) { x[k]=a[i].’1’. x=new char[n+1]. static char[][] a = { {0}.out. for(i=1.println(). else afisv().length-1. m=a. System. static int n. METODA BACKTRACKING class ProdCartCar1 { static char[] x.’B’}. {0. i<=n.’A’. static char[] a={0. } static void f(int k) { int i. {0.’2’.m.’3’} }. } } static void afisv() { int i. . static int[] m. i++) System. } }//class class ProdCartCar2 { static char[] x. for(i=1. static int n.out.

. for(i=1. i = 1.. System. x=new char[n+1].j++) { x[k]=a[k][j]. f(1).length-1. for(i=1. mult ¸imile Ai sunt finite. solut ¸ia poate fi pus˘ a sub forma unui vector x = (x1 . n=a.2. 3.print(x[i]). else afisv().i++) m[i]=a[i].. .2. METODA BACTRACKING public static void main(String[] args) { int i.2 Metoda bactracking Se folose¸ ste pentru rezolvarea problemelor care ˆ ındeplinesc urm˘ atoarele condit ¸ii: 1.out.length-1.j<=m[k]. i<=n.. } } static void afisv() { int i. for(j=1.. if(k<n) f(k+1). } static void f(int k) { int j.i<=n. renunt ¸ s˘ a continui pentru o valoare pentru care ¸ stiu c˘ a nu ajung la nici un rezultat.. n. .println(). } }// class 31 2. 2. Tehnica backtracking pleac˘ a de la urm˘ atoarea premis˘ a: dac˘ a la un moment dat nu mai am nici o ¸ sans˘ a s˘ a ajung la solut ¸ia c˘ autat˘ a. . nu se cunoa¸ ste o alt˘ a metod˘ a mai rapid˘ a de rezolvare. m=new int[n+1]. x2 . i++) System.out. xn ) cu xi ∈ Ai .

ˆ In figur˘ a este ilustrat modul de parcurgere a spat ¸iului solut ¸iilor ˆ ın cazul gener˘ arii produsului cartezian{0..a.. • la completarea componentei k se verific˘ a dac˘ a solut ¸ia part ¸ial˘ a (x1 . sau dac˘ se dore¸ ste determinarea unei noi solut ¸ii..d. 1}4 . × An . ˆ In cazul ˆ ın care sunt specificate restrict ¸ii (ca de exemplu. 0 1 x1 0 1 0 1 x2 0 1 0 1 0 1 0 1 x3 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 x4 0 1 x1 0 1 0 1 x2 0 1 0 1 x3 0 1 0 1 x4 . xk ) verific˘ a condit ¸iile induse de restrict ¸iile problemei (acestea sunt numite condit ¸ii de continuare). • solut ¸iile sunt construite succesiv.32 CAPITOLUL 2. se revine la componenta anterioar˘ a (k −1) ¸ si se ˆ ıncearc˘ a urm˘ atoarea valoare corespunz˘ a toare acesteia. x2 ..m. METODA BACKTRACKING Specificul metodei const˘ aˆ ın maniera de parcurgere a spat ¸iului solut ¸iilor. . • alegerea unei valori pentru o component˘ a se face ˆ ıntr-o anumit˘ a ordine astfel ˆ ıncˆ at s˘ a fie asigurat˘ a o parcurgere sistematic˘ a a spat ¸iului A1 × A2 × . ¸ s. • dac˘ a au fost ˆ ıncercate toate valorile corespunz˘ atoare componentei k ¸ si ˆ ınc˘ a nu a fost g˘ a sit˘ a o solut ¸ie.. • procesul de c˘ autare ¸ si revenire este continuat pˆ an˘ a cˆ and este g˘ asit˘ a o solut ¸ie (dac˘ a este suficient˘ a o solut ¸ie) sau pˆ an˘ a cˆ and au foste testate toate configurat ¸iile posibile (dac˘ a sunt necesare toate solut ¸iile). la fiecare etap˘ a fiind completat˘ a cˆ ate o component˘ a. s˘ a nu fie dou˘ a componente al˘ aturate egale) anumite ramuri ale structurii arborescente asociate sunt abandonate ˆ ınainte de a atinge lungimea n.

2 Backtracking recursiv Varianta recursiv˘ a a algoritmului backtracking poate fi realizat˘ a dup˘ a o schem˘ a asem˘ an˘ atoare cu varianta recursiv˘ a. A2 . METODA BACTRACKING 33 ˆ In aplicarea metodei pentru o problem˘ a concret˘ a se parcurg urm˘ atoarele etape: • se alege o reprezentare a solut ¸iei sub forma unui vector cu n componente. se trece la nivelul inferior k − 1 prin ie¸ sirea din procedura recursiv˘ a f. ˆ In caz afirmativ. Principiul de funct ¸ionare al functiei f (primul apel este f (1)) corespunz˘ ator unui nivel k este urm˘ atorul: −ˆ ın situat ¸ia ˆ ın care avem o solut ¸ie. • pornind de la restrict ¸iile problemei se stabilesc condit ¸iile de validitate ale solut ¸iilor part ¸iale (condit ¸iile de continuare).2. An . ˆ ın caz contrar urmˆ and a se continua c˘ autarea succesorului. init ¸iarizarea vectorului solut ¸ie k=1. • se identific˘ a mult ¸imile A1 .2. atunci m˘ aresc ¸ si fac pas dreapta else ++x[k].2. se init ¸ializeaz˘ a nivelul ¸ si se caut˘ a un succesor. verific˘ am dac˘ a este valid. An ¸ si se stabile¸ ste o ordine n ˆtre elemente care s˘ a indice modul de parcurgere a fiec˘ arei mult ¸imi. ˆ In aplicat ¸iile concrete solut ¸ia nu este obt ¸inut˘ a numai ˆ ın cazul ˆ ın care k = n ci este posibil s˘ a fie satisf˘ acut˘ a o condit ¸ie de g˘ asire a solut ¸iei pentru k < n (pentru anumite probleme nu toate solut ¸iile cont ¸in acela¸ si num˘ ar de elemente). altfel golesc ¸ si fac pas dreapta } Vectorul solut ¸ie x cont ¸ine indicii din mult ¸imile A1 .. .2. Mai precis. pozit ¸ionare pe prima component˘ a while (k>0) cˆ at timp exist˘ a componente de analizat if (k==n+1) dac˘ a x este solut ¸ie {afis(x)... A2 . − dac˘ a nu avem succesor.k++) x[k]=gol. –k. −ˆ ın caz contrar. 2.. . 2. procedura se autoapeleaz˘ a pentru k + 1.} atunci: afi¸ sare solut ¸ie ¸ si pas stˆ anga else altfel: { if(x[k]<max[k]) dac˘ a exist˘ a elemente de ˆ ıncercat if(posibil(1+x[k])) dac˘ a 1+x[k] este valid˘ a ++x[k++]. x[k ] = i ˆ ınseamn˘ a c˘ a pe pozit ¸ia k din solut ¸ia x se afl˘ a ai din Ak .1 Bactracking iterativ Structura general˘ a a algoritmului este: for(k=1.. altfel m˘ aresc ¸ si r˘ amˆ an pe pozitie else x[k–]=gol. o afi¸ sa ˘m ¸ si revenim pe nivelul anterior. ..k<=n. − cˆ and am g˘ asit un succesor.

k}. xn ) este o solut ¸ie ea trebuie s˘ a respecte restrict ¸iile: xi = xj pentru oricare i = j ....max=4.i<=pozmax..} else . while (k>0) if (k==n+1) {afis(a).. Mult ¸imile Ak : {1. min=1. return false... x2 .1 Generarea aranjamentelor Reprezentarea solut ¸iilor: un vector cu n componente. xk ) poate conduce la o solut ¸ie numai dac˘ a satisface condit ¸iile de continuare xi = xj pentru orice i = j . k=1. 2.3. k. . gol=min-1.. 2. System. ..i++) System.println(). . for(i=1. Condit ¸ia de g˘ asire a unei solut ¸ii: Orice vector cu n componente care respect˘ a restrict ¸iile este o solut ¸ie. --k. x2 . Restrict ¸iile ¸ si condit ¸iile de continuare: Dac˘ a x = (x1 . m}. Un vector cu k elemente (x1 . static int[] a=new int[n+1]. class GenAranjI1 { static int n=2. METODA BACKTRACKING 2..out. } static boolean gasit(int val..i<=n.i++) a[i]=gol.print(a[i]). Prelucrarea solut ¸iilor: Fiecare solut ¸ie obt ¸inut˘ a este afi¸ sat˘ a. } public static void main (String[] args) { int i. j ∈ {1.i++) if (a[i]==val) return true. Cˆ and k = n + 1 a fost g˘ asit˘ a o solut ¸ie.34 CAPITOLUL 2. static void afis(int[] a) { for(int i=1.. .3 Probleme rezolvate 2.out. unde i.i<=n. int pozmax) { for(int i=1.

i++) if (a[i]==a[k]) return false. } if(!ok) k--. // maresc si raman pe pozitie else a[k--]=gol. static void afis() { for(int i=1. while (k>0) { ok=false. } static boolean posibil(int k) { for(int i=1. static int gol=min-1. min=1.out. . for(i=1. } } } class GenAranjI2 { static int n=2. k=1.2. PROBLEME REZOLVATE { 35 if(a[k]<max) if(!gasit(1+a[k]. System.i<k. // maresc si pas dreapta else ++a[k].out. while (a[k] < max) // caut o valoare posibila { ++a[k]. else if (k == n) afis().max=4. } public static void main (String[] args) { int i. k.i<=n. boolean ok.i++) a[i]=gol.print(a[i]).println(). if(ok) break. ok=posibil(k).k-1)) ++a[k++].3. static int[] a=new int[n+1].i<=n. return true.i++) System.

36 else a[++k]=0; } } }

CAPITOLUL 2. METODA BACKTRACKING

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

2.3. PROBLEME REZOLVATE 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(); } }

37

38

CAPITOLUL 2. METODA BACKTRACKING

2.3.2

Generarea combin˘ arilor

Sunt prezentate mai multe variante (iterative ¸ si recursive) cu scopul de a vedea diferite modalit˘ a¸ ti de a cˆ a¸ stiga timp ˆ ın execut ¸ie (mai mult sau mai put ¸in semnificativ!). class GenCombI1a // 4550 ms cu 12 22 // { // 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 40 ms cu 8 14 fara afis

2.3. PROBLEME REZOLVATE 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(); }

39

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(); }

print(a[i]).i<=n.i<=n. static int gol=min-1. k. while (k>0) .t2. */ --k.i++) System.currentTimeMillis().40 CAPITOLUL 2.println(). // initializat si a[0] care nu se foloseste!! k=1.} else { if(a[k]<max-(n-k)) // optimizat !!! if(1+a[k]>a[k-1]) ++a[k++]. for(i=0. long t1. k..max=22. t1=System.i++) a[i]=gol. // maresc si raman pe pozitie else a[k--]=gol.currentTimeMillis(). static int[] a=new int[n+1]. long t1.currentTimeMillis(). // maresc si pas dreapta (k>1) . static void afis(int[] a) { for(int i=1. System. min=1. // initializat si a[0] care nu se foloseste!! k=1.println("Timp = "+(t2-t1)+" ms"). for(i=0.t2.out..i<=n. else ++a[k]. } public static void main (String[] args) { int i. } t2=System. while (k>0) if (k==n+1) {/* afis(a).out. System. METODA BACKTRACKING public static void main (String[] args) { int i. t1=System.out.i++) a[i]=gol. } } class GenCombI2b // 1250 ms 12 22 { static int n=12.

*/}// maresc.currentTimeMillis(). gol=min-1. /* afis(a). */ --k.out. min=1. while (k>0) if (k==n+1) {/* afis(a).i++) System. // maresc si pas dreapta (k>1) .i<=n.3.currentTimeMillis(). static void afis(int[] a) { for(int i=1.i<=n. PROBLEME REZOLVATE { 41 if(a[k]<max-(n-k)) // optimizat !!! if(1+a[k]>a[k-1]) if(k<n) ++a[k++].println("Timp = "+(t2-t1)+" ms").t2.. System.out. // si mai optimizat !!! long t1. static int[] a=new int[n+1].} else { if(a[k]<max-(n-k)) // optimizat !!! if(1+a[k]>a[k-1]) ++a[k++]. System.println("Timp = "+(t2-t1)+" ms").out.println(). // si mai optimizat !!! } t2=System.. for(i=0..print(a[i]). // maresc si pas dreapta (k>1) . System. afisez si raman pe pozitie else ++a[k]. else ++a[k]. } } . // maresc si raman pe pozitie else a[k--]=k-gol-1. } t2=System.currentTimeMillis(). else { ++a[k]. // initializat si a[0] care nu se foloseste!! k=1. t1=System. } } class GenCombI3a // 835 ms 12 22 { static int n=12. max=22.2.i++) a[i]=i-gol-1. k.out. // maresc si raman pe pozitie else a[k--]=gol. } public static void main (String[] args) { int i..

currentTimeMillis(). min=1.currentTimeMillis().out. m=22. METODA BACKTRACKING class GenCombI3b // 740 ms 12 22 { static int n=12. t1=System. // maresc if(a[k]>a[k-1]) if(k<n) k++.currentTimeMillis(). k. System.m.println("Timp = "+(t2-t1)+" ms"). // pas dreapta /* else afis(a).i++) a[i]=i-gol-1.42 CAPITOLUL 2. // si mai optimizat !!! k=1. gol=min-1.out.i<=n. // si mai optimizat !!! } t2=System. static int n.i<=n. t1=System. static int[] a=new int[n+1].print(a[i]). . static void afis(int[] a) { for(int i=1. */ // afisez } else a[k--]=k-gol-1. public static void main(String[] args) { long t1.println().t2.t2. while (k>0) { if(a[k]<max-(n-k)) // optimizat !!! { ++a[k].i++) System. for(i=0. n=12. } } class GenCombR1a // 2640 ms 12 22 { static int[] x. } public static void main (String[] args) { int i. long t1. System.out. max=22.

static void afis(int[] a) { for(int i=1.3. if(k<n) f(k+1). f(1). */ } } static void afisv() { for(int i=1.out.println().i<=m.out. // a[0]=0 deci merge ! a[k]=i.print(a[i]). i++) System. System. */ } } .i<=n.i++ ) { if (i<=a[k-1]) continue.i<=m. i<=n. x[k]=i.out.currentTimeMillis(). System. static int[] a=new int[n+1]. } static void f(int k) { for(int i=1. System.out.println(). /* else afis(a). t2=System.out.i++) { if(k>1) if(i<=x[k-1]) continue.m=22. if(k<n) f(k+1).i++) System. } } 43 class GenCombR1b // 2100 ms 12 22 { static int n=12.print(x[i]). PROBLEME REZOLVATE x=new int[n+1].2. } static void f(int k) { for(int i=1./* else afisv().println("Timp = "+(t2-t1)+" ms").

out. t2=System.out.print(x[i]+" "). }//main }//class class GenCombR2a // 510 ms 12 22 { static int n=12. . } static void afis(int k) // pentru urmarirea executiei ! { // ni = numar incercari !!! int i. } static void f(int k) { for(int i=k. static int[] x=new int[n+1].i++) System. if(k<n) f(k+1).currentTimeMillis().println().print(x[i]+" ").out. */ } } . nsol=0. static void afisx() { System. i<=m-n+k.println().print(++ni+" : ").print(++nsol+" ==> "). f(1).out. /* else afisx().out.i<=k. i++) // imbunatatit ! { if(k>1) { // afis(k-1).println("Timp = "+(t2-t1)+" ms").t2.i<=n.44 CAPITOLUL 2. METODA BACKTRACKING public static void main (String [] args) { long t1.currentTimeMillis(). for(i=1.out. System. for(int i=1.i++) System. m=22. ni=0. t1=System.out. } x[k]=i. System. System. System. if(i<=x[k-1]) continue.

i++) System. System.out. i++) // imbunatatit ! { if(i<=x[k-1]) continue.out.i++) System. */ } } public static void main(String[] args) { long t1. t1=System.i<=k.println().out.2.print(x[i]+" ").print(++ni+" : ").t2. i<=m-n+k.print(x[i]+" ").out. . nsol=0. /* else afisx().3.currentTimeMillis().println("Timp = "+(t2-t1)+" ms"). } static void f(int k) { for(int i=k.out. for(int i=1.i<=n. t2=System. x[k]=i. System. f(1).out. } static void afis(int k) // pentru urmarirea executiei ! { // ni = numar incercari !!! System.currentTimeMillis().print(++nsol+" ==> "). for(int i=1. } }// class class GenCombR2b // 460 ms 12 22 { static int n=12. static void afisx() { System. PROBLEME REZOLVATE 45 public static void main(String[] args) { long t1. if(k<n) f(k+1).currentTimeMillis(). m=22. System.t2.ni=0.println().out. static int[] x=new int[n+1]. t1=System.

} }// class class GenCombR3a // 165 ms 12 22 { static int n=12. System. System. static int nsol=0. METODA BACKTRACKING f(1).currentTimeMillis().i++) System. System. t2=System.out.out.out.println(). } static void afis(int k) { int i. */ } } . } static void f(int k) { int i. System.i++) System. for(i=1.i<=n. static void afisx() { int i.println("Timp = "+(t2-t1)+" ms"). i<=m-n+k.out.ni=0. i++) // si mai imbunatatit !!! { if(k>1) { // afis(k-1).println(). System. if(k<n) f(k+1). for(i=1. static int m=22.print(++nsol+" ==> "). if(i<=x[k-1]) continue.out.out. static int[] x=new int[n+1].print(x[i]+" "). /* else afisx().print(++ni+" : ").print(x[i]+" ").out.i<=k. } x[k]=i. for (i=x[k-1]+1.46 CAPITOLUL 2.

} }// class 47 . t2=System. t1=System. } }// class class GenCombR3b // 140 ms 12 22 { static int n=12.out. System.out.currentTimeMillis(). */ } } public static void main(String[] args) { long t1.i<=n.3.println(). static int nsol=0. } static void f(int k) { int i. System. t2=System. f(1).print(x[i]+" ").print(++nsol+" : "). if(k<n) f(k+1).currentTimeMillis(). static int m=22. for(i=1.t2.out. /* else afisx(). i++) { x[k]=i.out. static int[] x=new int[n+1].println("Timp = "+(t2-t1)+" ms"). for (i=x[k-1]+1. System.i++) System.currentTimeMillis().t2. f(1).2. t1=System.println("Timp = "+(t2-t1)+" ms"). PROBLEME REZOLVATE public static void main(String[] args) { long t1. i<=m-n+k.currentTimeMillis().out. System. static void afisx() { int i.

48 CAPITOLUL 2. Restrict ¸ii ¸ si condit ¸ii de continuare: xi = xj pentru oricare i = j (reginele nu se atac˘ a pe coloan˘ a) ¸ si |xi − xj | = |i − j | (reginele nu se atac˘ a pe diagonal˘ a). Reprezentarea solut ¸iei: un vector x unde xi reprezint˘ a coloana pe care se afl˘ a regina dac˘ a linia este i. while (x[k] <= n-1) // caut o valoare posibila { ++x[k]. class RegineI1 { static int[] x. k=1. else if (k == n) afis(). public static void main(String[] args) { n=5. if(ok) break. regine(n). ok=posibil(k).3 Problema reginelor pe tabla de ¸ sah O problem˘ a clasic˘ a de generare a configurat ¸iilor ce respect˘ a anumite restrict ¸ii este cea a amplas˘ arii damelor pe o tabl˘ a de ¸ sah astfel ˆ ıncˆ at s˘ a nu se atace reciproc.i++) x[i]=i. Sunt prezentate o variant˘ a iterativ˘ a¸ si una recursiv˘ a. x[k]=0. } }//regine() .nv=0. } if(!ok) k--. x=new int[n+1]. } static void regine(int n) { int k. boolean ok. else x[++k]=0. for(int i=0.i<=n. static int n.3. while (k>0) { ok=false. METODA BACKTRACKING 2.

static int n.abs(i-x[j]))) {ok=false. } static void f(int k) { boolean ok.nv=0. System.print(x[i]+" "). else afisv().abs(x[k]-x[i]))) return false.j<k.println().print(++nv+" : "). break.out. f(1).out. int i. PROBLEME REZOLVATE 49 static boolean posibil(int k) { for(int i=1. for(i=1.} if(!ok) continue. i++) System.2. return true. } } . } static void afis() { int i. if(k<n) f(k+1). public static void main(String[] args) { n=5.j.i++) if ((x[k]==x[i])||((k-i)==Math. for(i=1.3. } }// class class RegineR1 { static int[] x.i++) { ok=true. x=new int[n+1].i<=n. x[k]=i. System.i<=k-1. for(j=1. i<=n.j++) if((i==x[j])||((k-j)==Math.out.

i++) for(int j=0.println("\nNu exista solutii !!!"). .parseInt(br. } } 2.println("\r----------------------------------").io.1..2. i++) System.-1. if(incerc(2. i<=n.*.-2. System.println().SUCCES=1.2}. // np=n*n public static void main(String[] args) throws IOException { BufferedReader br=new BufferedReader( new InputStreamReader(System. } // main static void afisare() { System.ni=0.-2.j++) tabla[i][j]=LIBER.out.2. tabla[1][1]=1.1.in)).1.2.j<=n.-1}.println("\n\nNumar de incercari = "+ni).4 Turneul calului pe tabla de ¸ sah import java. System.i<=n.-2. while ((n<3)||(n>NMAX)) { System.1.out. n=Integer.out.3."+NMAX+"] : "). class Calut { static final int LIBER=0. else System.out. System. // tabla de sah static int n.50 CAPITOLUL 2.1)==SUCCES) afisare(). // miscari posibile pe Ox static final int[] b={0.NMAX=8.readLine()).np. for(int i=0.out.-2.print(++nv+" : ").out.-1.out. for(i=1.ESEC=0.print("Dimensiunea tablei de sah [3. } np=n*n.-1. // miscari posibile pe Oy static int[][] tabla=new int[NMAX+1][NMAX+1]. METODA BACKTRACKING static void afisv() { int i.1.print(x[i]+" "). static final int[] a={0.

ni++.out.i<=n.8] : 5 ---------------------------------1 14 19 8 25 6 9 2 13 18 15 20 7 24 3 10 5 22 17 12 21 16 11 4 23 Numar de incercari = 8839 .. while ((rezultatIncercare==ESEC)&&(k<=8))// miscari posibile cal { xu=x+a[k]. int y) { int xu. if (i<np) { rezultatIncercare=incerc(i+1.yu.i++) { System. int x. } k++.j++) System.3. // afisare().j<=n. } else rezultatIncercare=SUCCES. k=1.rezultatIncercare. }// incerc() }// class Pe ecran apar urm˘ atoarele rezultate: Dimensiunea tablei de sah [3. for(int j=1. if(rezultatIncercare==ESEC) tabla[xu][yu]=LIBER. PROBLEME REZOLVATE for(int i=1.yu). } }// afisare() 51 static int incerc(int i. yu=y+b[k].out.k.2. rezultatIncercare=ESEC. }// while return rezultatIncercare. if((xu>=1)&&(xu<=n)&&(yu>=1)&&(yu<=n)) if(tabla[xu][yu]==LIBER) { tabla[xu][yu]=i.println().print("\t"+tabla[i][j]).xu.

length.// Calarasi (1) {1.1.0}. .0.1}.1. if(nrVar==0) System. astfel ˆ ıncˆ at oricare dou˘ a¸ t˘ ari vecine s˘ a fie colorate diferit. // indicele pentru tara boolean okk.. m}.1. METODA BACKTRACKING 2. xn ) cu xi ∈ {1.0.2} // Tulcea (4) }. .// Braila (3) {1..1.3.j = 1 dac˘ a i este vecin˘ a cu j 0 dac˘ a i nu este vecin˘ a cu j Reprezentarea solut ¸iilor: O solut ¸ie a problemei este o modalitate de colorare a ¸ t˘ a rilor ¸ si poate fi reprezentat˘ a printru-un vector x = (x1 . Mult ¸imile de valor ale elementelor sint A1 = A2 = .2.1}... Restrict ¸ii ¸ si condit ¸ii de continuare: Restrict ¸ia ca dou˘ a¸ t˘ ari vecine s˘ a fie colorate diferit se specific˘ a prin: xi = xj pentru orice i ¸ si j avˆ and proprietatea vi. Condit ¸ia de continuare pe care trebuie s˘ a o satisfac˘ a solut ¸ia part ¸ial˘ a (x1 .println("Nu se poate colora !").. .5 Problema color˘ arii h˘ art ¸ilor Se consider˘ a o hart˘ a cu n ¸ t˘ ari care trebuie colorat˘ a folosind m < n culori. static int n=harta.out. . class ColorareHartiI1 { static int nrCulori=3..// Ialomita (2) {1..52 CAPITOLUL 2.k = 1.0.. Relat ¸ia de vecin˘ atate dintre ¸ t˘ ari este ret ¸inut˘ aˆ ıntr-o matrice n × n ale c˘ arei elemente sunt: vi. x2 .3 static int[][] harta= {// CoCaIaBrTu {2.2..1..1..1. x2 . .0}.j = 1.2. = An = {1. 2.1. static int[] culoare=new int[n]. } // main static void harta() { int k.0. public static void main(String[] args) { harta(). static int nrVar=0..2. m} reprezentˆ and culoarea asociat˘ a ¸ t˘ arii i.// Constanta (0) {1..// culorile sunt 1. 2. xk ) este: xk = xi pentru orice i < k cu proprietatea c˘ a vi..

} // posibil static void afis() { System.i<n.i<=k-1.print(++nrVar+" : ").3. else culoare[++k]=0. } } // harta static boolean posibil(int k) { for(int i=0.i++) if((culoare[k]==culoare[i])&&(harta[i][k]==1)) return false. } if (!okk) k--. System. // prima pozitie culoare[k]=0. PROBLEME REZOLVATE k=0. okk=posibil(k).out. if (okk) break. else if (k == (n-1)) afis(). return true.i++) System.print(culoare[i]+" ").2. } // scrieRez } // class Pentru 3 culori rezultatele care apar pe ecran sunt: 1 2 3 4 5 6 1 1 2 2 3 3 2 3 1 3 1 2 3 2 3 1 2 1 2 3 1 3 1 2 3 2 3 1 2 1 53 .out. while(culoare[k] < nrCulori)// selectez o culoare { ++culoare[k].println(). // tara k nu este colorata (inca) while (k>-1) // -1 = iesit in stanga !!! { okk=false. for(int i=0.out.

j<k.0. for(int i=1.1.0. } static void f(int k) { boolean ok.1. } } . m=3.// {0.1. for(i=1. static int[][] a= { {0.j.0. } } static void afisv() { System.1.1.2} // }. static int n.j++) if((i==x[j])&&(a[j][k]==1)) {ok=false.out.out. else afisv().length-1.0.1}.2. if(k<n) f(k+1). METODA BACKTRACKING class ColorareHartiR1 { static int[] x.1.nv=0.1.print(++nv+" : ").} if(!ok) continue. i++) System. i<=n.print(x[i]+" "). break.0.m.1.1.2.0.// {0.i++) { ok=true.1.1.// {0. for(j=1.out.1}.2.println(). x[k]=i. System.0.0.0}.54 CAPITOLUL 2.// {0. {0. int i. f(1).2.0}. Constanta (1) Calarasi (2) Ialomita (3) Braila (4) Tulcea (5) public static void main(String[] args) { n=a.i<=m.0}.1. // nr culori x=new int[n+1].

int i.i++) { ok=true..} if(!ok) continue.j<k.abs(i-x[k-1])!=2)&&(Math.. static int n. break. . m=7. class VeciniR1 { static int[] x.. . x=new int[n+1]. x[k]=i.j.2. Vom rezolva problema prin metada backtracking.nsol=0.. k − 1 (x trebuie s˘ a fie permutare) b) |xk − xk−1 | = 2 sau 3 (ˆ ıntre persoana k ¸ si vecinul s˘ au anterior trebuie s˘ a se afle una sau dou˘ a persoane) ˆ In locul solutiei x vom lista permutarea y = x−1 unde yi reprezint˘ a persoana care se aseaz˘ a pe locul i.m.j++) if(i==x[j]) {ok=false. condit ¸iile de continuare sunt: a) xj = xk .abs(i-x[k-1])!=3)) continue. pentru j = 1. Rearanjat ¸i persoanele pe scaune astfel ˆ ıncˆ at ˆ ıntre oricare doi vecini ”certat ¸i” s˘ a existe una sau cel mult dou˘ a persoane cu care nu au apucat s˘ a se certe! Afi¸ sat ¸i toate variantele de rea¸ sezare posibile.6 Problema vecinilor Un grup de n persoane sunt a¸ sezate pe un rˆ and de scaune.3. de la stanga la dreapta cu 1. public static void main(String[] args) { n=7. PROBLEME REZOLVATE 55 2. if((Math. Pentru k > 1 dat.i<=m. for(j=1. Presupunem c˘ a persoanele sunt numerotate la ˆ ınceput. } . f(1). Consider˘ am ca solut ¸ie un vector x cu n componente pentru care xi reprezint˘ a ”pozit ¸ia pe care se va afla persoana i dup˘ a rea¸ sezare”. for(i=1. ˆ Intre oricare doi vecini izbucnesc conflicte. else afis(). n.3. 2.. } static void f(int k) { boolean ok.. if(k<n) f(k+1).

out.io.n. System.j. if(nrVar==0) System. while ((n<3)||(n>50)) { System. System.i<n.out.. } x=new int[n].print("numar persoane [3. i<=n.out.println("Nu se poate !!!").*. static int[] x.print(x[i]+" ").i++) { ok=true. y=new int[n]. } // main static void reasez(int k) { int i.in)).y. n=Integer. while ((j<k-1) && ok) ok=(i != x[j++]). if (k>0) . j=0.print(++nsol+" : ").println(). METODA BACKTRACKING static void afis() { int i. if (k==n) scrieSolutia(). reasez(0).56 } CAPITOLUL 2.out. public static void main(String[] args) throws IOException { BufferedReader br=new BufferedReader( new InputStreamReader(System. i++) System. boolean ok.out.50] : "). } } import java. for(i=1.parseInt(br.// n=in afara vectorului !!! else for(i=0.readLine()). class Vecini { static int nrVar=0.

ˆ Intr-una din camere.i++) y[x[i]]=i. reasez(k+1).out. Fiecare camer˘ a din labirint poate fi caracterizat˘ a printr-un ¸ sir de patru cifre binare asociate celor patru direct ¸ii.1.. La g˘ asirea unei ie¸ siri din labirint. Pentru a testa u¸ sor ie¸ sirea din labirint. j ) din labirint.abs(i-x[k-1])==2)||(Math.out.7 Problema labirintului Se d˘ a un labirint sub form˘ a de matrice cu m linii ¸ si n coloane.i++) System. if (ok) { x[k]=i. Est.print(++nrVar+" : "). − d[2][k ] reprezint˘ a coloana camerei respective.n (la afisare) } // scrieRez } // class 2. ei ˆ ıi va corespunde ¸ sirul 1010 care reprezint˘ a num˘ arul 10 ˆ ın baza zece. matricea se bordeaz˘ a cu dou˘ a linii ¸ si dou˘ a coloane de valoare egal˘ a cu 16..3.out.. d.} } } // reasez static void scrieSolutia() { int i. Principiul algoritmului este urm˘ atorul: . de coordonate x0 ¸ si y0 se g˘ a se¸ ste un om. O prim˘ a problem˘ a care se pune este precizarea modului de codificare a ie¸ sirilor din fiecare camer˘ a a labirintului.. for(i=0. Ne punem problema determin˘ arii ie¸ sirilor pe care le are o camer˘ a.2.println().abs(i-x[k-1])==3))).. for(i=0. O camer˘ a are ie¸ sirea numai spre N dac˘ a¸ si numai dac˘ a a[i][j ]&&8 = 0. PROBLEME REZOLVATE 57 ok=(ok&&((Math. // ca sa nu fie 0. dar odat˘ a aleas˘ a aceasta se p˘ astreaz˘ a pˆ an˘ a la sfˆ ar¸ situl problemei).3..i<n. 4) exist˘ a ie¸ siri la N ¸ si S. Prin urmare.n-1 System. Fiecare element al matricei reprezint˘ a o camer˘ a a labirintului.// inversarea permutarii !!! System.. Dintr-o pozit ¸ie oarecare (i.. avˆ and semnificat ¸ie faptul c˘ a acea camer˘ a are sau nu ie¸ siri pe direct ¸iile considerate. dac˘ a pentru camera cu pozit ¸ia (2.i<n. Drumul parcurs la un moment dat se ret ¸ine ˆ ıntr-o matrice cu dou˘ a linii. ˆ ın care: − d[1][k ] reprezint˘ a linia camerei la care s-a ajuns la pasul k . De exemplu. codific˘ am labirintul printr-o matrice a[i][j ] cu elemente ˆ ıntre 1 ¸ sai 15. drumul este afi¸ sat. Sud ¸ si Vest considerate ˆ ın aceast˘ a ordine (putem alege oricare alt˘ a ordine a celor patru direct ¸ii. Se cere s˘ a se g˘ a seasc˘ a toate ie¸ sirile din labirint.print(++y[i]+" ").2. // ci 1. deplasarea se poate face ˆ ın patru direct ¸ii: Nord.

’. E.nval. import java.io. for(j=0. st. public static void main(String[] args) throws IOException { int i. METODA BACKTRACKING − se testeaz˘ a dac˘ a s-a ie¸ sit din labiritn (adic˘ a a[i][j ] = 16).out"))).j++) l[i][j]=st. static boolean ok. class Labirint { static final char coridor=’.i++) { if (i>0) out. st.nextToken().i++) { st. for(i=0.nextToken(). gard=’H’. start=’x’.n.in"))). st. StreamTokenizer st = new StreamTokenizer( new BufferedReader(new FileReader("labirint. •ˆ ınaintea ie¸ sirii din procedur˘ a se decrementeaz˘ a valoarea lui k . x0=(int)st. V ¸ si acolo unde este g˘ asit˘ a o astfel de ie¸ sire se reapeleaz˘ a procedura cu noile coordonate.x0. S.j.println().y0. caz ˆ ın care se iese din procedur˘ a. .i<m.sval. pas=’*’. m=(int)st.*. • se verific˘ a dac˘ a drumul arcurs a mai trecut prin aceast˘ a camer˘ a. −ˆ ın caz contrar se procedeaz˘ a astfel: • se ret ¸in ˆ ın matricea d coordonatele camerei vizitate. static char[][] l.j<n. ok=false.i<m. • se testeaz˘ a pe rˆ and ie¸ sirile spre N. for(i=0. −ˆ ın caz afirmativ se afi¸ seaz˘ a drumul g˘ asit. l=new char[m][n]. l[x0][y0]=start.nextToken(). n=(int)st. PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter("labirint.58 CAPITOLUL 2.y0).k.nval.nextToken().nval. } st. static int m.nval. gi(x0. y0=(int)st.charAt(j). iesire=’E’.nextToken().

H.j<n.H.H H.y). else { l[x][y]=pas...print(l[i][j]).H. PROBLEME REZOLVATE for(j=0.in 8 8 HHHHHHEH H.HHHH.H H******H HHHHHH*H 59 .2. out...close().println("NU exista iesire !"). if(l[x][y+1]==coridor||l[x][y+1]==iesire) gi(x.y+1)....j++) out. if(!ok&&(l[x-1][y]==coridor||l[x-1][y]==iesire)) gi(x-1. } if (ok) l[x][y]=pas.H H.y-1).out HHHHHHEH H.H H*xHHH. if(!ok&&(l[x][y-1]==coridor||l[x][y-1]==iesire)) gi(x.H HHHHHHEH 2 2 se obt ¸ine fi¸ sierul de ie¸ sire: labirint.. pentru fisierul de intrare: labirint.H H*. l[x][y]=coridor..H H*HHHH..3..H H. if(!ok&&(l[x+1][y]==coridor||l[x+1][y]==iesire)) gi(x+1.. } }// class De exemplu.. }// main() static void gi(int x..H H*HHHH..HHHH.H H.H..H H. } if (!ok) out.int y) { if ((x==0)||(x==n-1)||(y==0)||(y==m-1)) ok=true.HHHH.y).

i). } . f(n. . Init ¸ial.currentTimeMillis(). afis2(val..print(x[i]+" "). t2=System. } } static void afis1() { System. public static void main(String[] args) { long t1.out. Din valoarea care se g˘ ase¸ ste pe un nivel. procedura va apela o alta pentru afi¸ sarea vectorului (init ¸ial afi¸ seaz˘ a n).1). afis1(). static int[] x=new int[n+1]. class PartitieNrGenerare // n = suma de numere { static int dim=0. int maxp.out. valori cu care se apeleaz˘ a procedura pentru nivelul urm˘ ator.val). dim=poz-1. return. int poz) { if(maxp==1) { nsol++.i--) { x[poz]=i. Imediat ce este apelat˘ a. System. S [k ] − 1.. t1=System.print("\n"+nsol+" : ").8 Generarea partit ¸iilor unui num˘ ar natural S˘ a se afi¸ seze toate modurile de descompunere a unui num˘ ar natural n ca sum˘ a de numere naturale. S [k ].out.t2.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n"). METODA BACKTRACKING 2. return.. } int maxok=min(maxp. for(int i=1.i++) System.n.maxp).min(val-i. La revenire se reface valoarea existent˘ a. f(val-i. procedura este apelat˘ a pentru nivelul 1 ¸ si valoarea n.3.poz+1).currentTimeMillis(). for(int i=maxok. se scad pe rˆ and valorile 1.i<=dim.i>=1.60 CAPITOLUL 2. 2. n=6. } static void f(int val. } if(val==0) { nsol++. dim=poz-1. nsol=0. Vom folosi o procedur˘ a 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 pˆ an˘ a la n).

System.currentTimeMillis(). return.nsol=0. PROBLEME REZOLVATE 61 static void afis2(int val.i++) System. } if(val==0) { nsol++.out. // afis2(val.i<=val.maxi. } static void f(int val.t2. f(n. public static void main(String[] args) { long t1. return. } static int min(int a. for(i=1.out. t1=System. maxi=((n-1)/2)*2+1. } . int n=160.print("1 ").2.maxip).println("nsol = "+nsol+" timp = "+(t2-t1)+"\n").out. t2=System.3.print("\n"+nsol+" : "). // dim=poz-1.i<=dim. // dim=poz-1.int maxip) { int i.out. } } Pentru descompunerea ca sum˘ a de numere impare. static int[] x=new int[161].1). for(i=1. System.int b) { return (a<b)?a:b. int poz) { if(maxip==1) { nsol++.i++) System. int maxip.currentTimeMillis(). programul este: class PartitieNrImpare1 // n = suma de numere impare { // nsol = 38328320 1Gata!!! timp = 8482 static int dim=0. afis1().print(x[i]+" ").

int n=160. } static int min(int a.i++) System.i<=dim. t1=System.i<=dim. // nsol = 38328320 2Gata!!! static int[] x=new int[161]. } static void afis2(int val.out. f(n.print(x[i]+" "). maxi=((n-1)/2)*2+1.maxi.out.poz+1).i++) System.i). METODA BACKTRACKING int maxi=((val-1)/2)*2+1.out. System.println("nsol = "+nsol+" timp= "+(t2-t1)).nsol=0.print("\n"+nsol+" : "). for(int i=maxiok.currentTimeMillis(). for(i=1. { // optimizat timp la jumatate !!! static int dim=0. timp = 4787 public static void main(String[] args) { long t1.i++) System.int b) { return (a>b)?a:b. for(int i=1.print("1 ").i<=val.currentTimeMillis().out.print(x[i]+" "). } .i>=1. int maxiok=min(maxip. for(i=1. } static int max(int a.out. t2=System.i=i-2) { x[poz]=i.maxi).int b) { return (a<b)?a:b.min(maxiok. } }// class O versiune optimizat˘ a este: class PartitieNrImpare2 // n = suma de numere impare .int maxip) { int i.62 CAPITOLUL 2.out.t2. f(val-i. } } static void afis1() { System.1). System.print("\n"+nsol+" : ").

for(int i=1.i<=val. } static int max(int a.i++) System.2. // dim=poz-1. } if(val==0) { nsol++. int poz) { if(maxip==1) { nsol++. // dim=poz-1. } static void afis1() { System. } static void afis2(int val) { System. for(int i=maxiok. for(int i=1. // dim=poz-1.i. afis1(). } int maxi=((val-1)/2)*2+1. PROBLEME REZOLVATE static void f(int val.print("1 "). } static int min(int a. return.3.i++) System.out.out.int b) { return (a<b)?a:b. f(val-i.print(x[i]+" ").out. for(int i=1.i++) System. } }// class 63 .out.out. int maxiok=min(maxip.int b) { return (a>b)?a:b.maxi). int maxip.i<=dim. afis2(val).i>=3.print("\n"+nsol+" : "). } nsol++. return.poz+1).i=i-2) { x[poz]=i.print("\n"+nsol+" : ").i<=dim. // afis2(val).print(x[i]+" ").

print(") "). else System. f(k+1. t2=System.k++) if(x[k]==1) System.out. static int n=4.out.out. } if(npi<npd) { x[k]=2. f(k+1.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. System. int npi) { if(k>n2) afis(). System. t1=System.print(++nsol+" : ").t2.3.0.currentTimeMillis(). METODA BACKTRACKING 2.npd.currentTimeMillis(). f(1. System.npi+1). } } } static void afis() { int k. } static void f(int k.out.k<=n2.0). static int[] x=new int[n2+1].println().println("nsol = "+nsol+" timp = "+(t2-t1)+"\n").64 CAPITOLUL 2. int npd.out.npd+1. } }// class .npi). static int n2=2*n. else { if(npd<n) { x[k]=1.print("( "). for(k=1. class ParantezeGenerare // 2*n paranteze { static int nsol=0. public static void main(String[] args) { long t1.

O problem˘ a este abordabil˘ a folosind tehnica program˘ arii dinamice dac˘ a satisface principiul de optimalitate sub una din formele prezentate ˆ ın continuare. dou˘ a sau mai multe subprobleme necesit˘ a rezolvarea unei aceea¸ si subprobleme. Sunt considerate urm˘ atoarele aspecte care caracterizeaz˘ a o rezolvare prin programare dinamic˘ a: • Problema se poate descompune recursiv ˆ ın mai multe subprobleme care sunt caracterizate de optime part ¸iale. ˆ ın maniera bottom-up. • Subproblemele respective se suprapun. Sn ale sistemului.. optimele part ¸iale se vor ret ¸ine treptat. ˆ ın anumite structuri de date (tabele).1 Prezentare general˘ a Folosirea tehnicii program˘ arii dinamice solicit˘ a experient ¸˘ a. Pentru a evita risipa de timp rezultat˘ aˆ ın urma unei implement˘ ari recursive. Prima variant˘ a se bazeaz˘ a pe conceptul de subproblem˘ a. De foarte multe ori rezolv˘ arile date prin aceast˘ a tehnic˘ a au ordin de complexitate polinomial. 65 .. S1 . ˆ In literatura de specialitate exist˘ a dou˘ a variante de prezentare a acestei tehnici. . A doua variant˘ a de prezentare face apel la conceptele intuitive de sistem. Prima dintre ele este mai degrab˘ a de natur˘ a deductiv˘ a pe cˆ and a doua folose¸ ste gˆ andirea inductiv˘ a. stare ¸ si decizie. La un anumit nivel.Capitolul 3 Programare dinamic˘ a 3.. intuit ¸ie ¸ si abilit˘ a¸ ti matematice. iar optimul global se obt ¸ine prin combinarea acestor optime part ¸iale. Fie secvent ¸a de st˘ ari S0 .

. d2 . se folosesc tabele auxiliare pentru ret ¸inerea optimelor part ¸iale iar spat ¸iul de solut ¸ii se parcurge ˆ ın ordinea cresc˘ atoare a dimensiunilor subproblemelor. se rezolv˘ a fiecare ”sub-subproblem˘ a” o singur˘ a dat˘ a¸ si se folose¸ ste un tablou pentru a memora solut ¸ia ei.66 ˘ CAPITOLUL 3. reconstituirea unei solut ¸ii pe baza informat ¸iei calculate. . este necesar˘ a¸ si o justificare sau o demonstrat ¸ie a faptului c˘ a aceste recurent ¸e sunt cele corecte. • Dac˘ a d1 . caracterizarea unei solut ¸ii optime (identificarea unei modalit˘ ati optime de rezolvare... 2. ˆ ın a doua variant˘ a avem recurent ¸e ˆ ın ¸ sirul de decizii... .. Spunem c˘ a se aplic˘ a metoda mixt˘ a.. d2 . Astfel. programarea dinamic˘ a rezolv˘ a problemele combinˆ and solut ¸iile unor subprobleme. dn este un ¸ sir optim de decizii care duc la trecerea sistemului din starea S0 ˆ ın starea Sn . rezolvarea prin programare dinamic˘ a presupune g˘ asirea ¸ si rezolvarea unui sistem de recurent ¸e. d2 .. di+2 . dn este un ¸ sir optim de decizii care duc la trecerea sistemului din starea Si ˆ ın starea Sn .. Algoritmul pentru rezolvarea unei probleme folosind programarea dinamic˘ a se dezvolta ˆ ın 4 etape: 1. atunci pentru orice i (1 ≤ i ≤ n) di+1 . ele avˆ and ”sub-subprobleme” comune... Indiferent de varianta de prezentare.. ˆ In afara cazurilor ˆ ın care recurent ¸ele sunt evidente. . Asem˘ an˘ ator cu metoda ”divide et impera”.. . dn .. • Dac˘ a d1 . di+2 . Deoarece rezolvarea prin recursivitate duce de cele mai multe ori la ordin de complexitate exponent ¸ial.. decizia di+1 depinde de deciziile anterioare d1 . . care satisface una dintre formele principiului optimalit˘ a¸ tii). dn este un ¸ sir optim de decizii care duc la trecerea sistemului din starea Si ˆ ın starea Sn ¸ si d1 ... Astfel... atunci pentru orice i (1 ≤ i ≤ n) d1 . d2 .. definirea recursiv˘ a a valorii unei solut ¸ii optime. dn este un ¸ sir optim de decizii care duc la trecerea sistemului din starea S0 ˆ ın starea Sn . di este un ¸ sir optim de decizii care duc la trecerea sistemului din starea S0 ˆ ın starea Si . di este un ¸ sir optim de decizii care duc la trecerea sistemului din starea S0 ˆ ın starea Si .. Spunem c˘ a se aplic˘ a metoda ˆ ınapoi.. ˆ In prima variant˘ a avem recurent ¸e ˆ ıntre subprobleme.. Spunem c˘ a se aplic˘ a metoda ˆ ınainte.. d2 . . 4. dn este un ¸ sir optim de decizii care duc la trecerea sistemului din starea S0 ˆ ın starea Sn .. decizia di depinde de deciziile anterioare di+1 .. .. ˆ In aceast˘ a situat ¸ie. . Folosirea acestor tabele d˘ a¸ si numele tehnicii respective. . atunci pentru orice i (1 ≤ i ≤ n) di+1 . di . 3.. le rezolv˘ a (de obicei recursiv) ¸ si apoi combin˘ a solut ¸iile lor pentru a rezolva problema initial˘ a. calculul efectiv al valorii unei solut ¸ii optime. Deosebirea const˘ aˆ ın faptul c˘ a ”divide et impera” partit ¸ioneaz˘ a problema ˆ ın subprobleme independente. evitˆ andu-se recalcularea ei de cˆ ate ori subproblema reapare. ˆ ın timp ce programarea dinamic˘ a se aplic˘ a problemelor ale c˘ aror subprobleme nu sunt independente. PROGRAMARE DINAMICA • Dac˘ a d1 .

.. d1 × d2 . PROBLEME REZOLVATE 67 3.. necesitˆ and 1000000+1000000=2000000 ˆ ınmult ¸iri. An .. 1000)..2. Numim ˆ ınmult ¸ire elementar˘ aˆ ınmult ¸irea a dou˘ a elemente. calcularea produsului Ai ×Ai+1 ×. cu o matrice B ..3.... × An . × An ). deci vom paranteza produsul astfel: (A1 × A2 × . × Aj . 10) ¸ si (10. × Aj ...2. este n ∗ m ∗ p. Reamintim c˘ a num˘ arul de ˆ ınmult ¸iri elementare necesare pentru a ˆ ınmult ¸i o matrice A. Pentru a calcula A1 × A2 × . A2 .. ... × An este M [1][n]. de dimensiuni d0 × d1 . × Ak ) × (Ak+1 × . dn−1 × dn .1 Inmult ¸irea optimal˘ a a matricelor Consider˘ am n matrice A1 . Solutie 1. avˆ and n linii ¸ si m coloane.. × Aj . cu n linii ¸ si n coloane. Prin urmare. Observ˘ am c˘ a subproblemele nu sunt independente. ˆ ın final trebuie s˘ aˆ ınmult ¸im dou˘ a matrice. cu semnificatia: M [i][j ] = num˘ arul minim de ˆ ınmult ¸iri elementare necesare pentru a calcula produsul Ai × Ai+1 × .. Se cere parantezare optimal˘ a a produsului A1 × A2 × .. au ca subproblem˘ a comun˘ a calcularea produsului Ai+1 × . aplicˆ and asociativitatea operat ¸iei de ˆ ınmultire a matricelor. Evident.2 Probleme rezolvate 3. avˆ and m linii ¸ si p coloane. 2... 1 ≤ i ≤ j ≤ n. A1 × (A2 × A3 ). subproblemele problemei init ¸iale constau ˆ ın determinarea parantez˘ arii optimale a produselor de matrice de forma Ai × Ai+1 × ... ˆ In funct ¸ie de modul de parantezare difer˘ a num˘ arul de ˆ ınmult ¸iri elementare necesare pentru calculul produsului A1 × A2 × . × An se poate calcula ˆ ın diverse moduri.×Aj ¸ si calcularea produsului Ai+1 ×Ai+2 ×. × An .. Exemplu: Pentru 3 matrice de dimensiuni (10. De exemplu.. (A1 × A2 ) × A3 necesitˆ and 1000000+10000=1010000 ˆ ınmult ¸iri elementare 2... 100). Pentru a ret ¸ine solut ¸iile subproblemelor.. Produsul A1 × A2 × ... produsul A1 × A2 × A3 se poate calcula ˆ ın dou˘ a moduri: 1. . 1 ≤ i ≤ j ≤ n... × An (pentru care costul. s˘ a fie minim). num˘ arul minim de ˆ ınmult ¸iri necesare pentru a calcula A1 × A2 × .×Aj +1 . (1000.. vom utiliza o matrice M . Aceast˘ a observat ¸ie se aplic˘ a ¸ si produselor dintre paranteze. adic˘ a num˘ arul total de ˆ ınmult ¸iri elementare.

ci-l vom ret ¸ine. static int p[]=new int[100]... Prin urmare vom rezolva relat ¸ia de recurent ¸˘ aˆ ın mod bottom-up: (determin˘ am parantezarea optimal˘ a a produselor de dou˘ a matrice. . 4.. parantezarea produselor A1 × A2 × . pentru care se obtine minimul.*. fix˘ am pozit ¸ia de parantezare k ˆ ın toate modurile posibile (ˆ ıntre i ¸ si j − 1). la care se adaug˘ a num˘ arul de ˆ ınmult ¸iri elementare necesare pentru calculul produsului Ak+1 × . ¸ si alegem varianta care ne conduce la minim. import java. .min.io..out... etc).v.imin. i = 1.. 2. PROGRAMARE DINAMICA 3..j. × Aj ¸ si costul ˆ ınmult ¸irii celor dou˘ a matrice rezultate (di−1 × dk × dj ).k. paranteze(i.k). × An trebuie s˘ a fie de asemenea optimal˘ a. class InmOptimalaMatrice { static int nmax=20. Observ˘ am c˘ a numai jum˘ atatea de deasupra diagonalei principale din M este utilizat˘ a. apoi de 3 matrice.×Ak . pe pozit ¸ia simetric˘ a fat ¸˘ a de diagonala principala (M [j ][i]). Pentru a construi solut ¸ia optim˘ a este util˘ a¸ si ret ¸inerea indicelui k . datorit˘ a faptului c˘ a subproblemele se suprapun. n. Prin urmare elementele matricei M trebuie s˘ a satisfac˘ a urmatoarea relat ¸ie de recurent ¸˘ a: M [i][i] = 0.int j) { int k. M [i][j ] = min {M [i][k ] + M [k + 1][j ] + d[i − 1] × d[k ] × d[j ]} i≤k<j Cum interpret˘ am aceast˘ a relat ¸ie de recurent ¸˘ a? Pentru a determina num˘ arul minim de ˆ ınmult ¸iri elementare pentru calculul produsului Ai × Ai+1 × . × Aj .68 ˘ CAPITOLUL 3. Pentru o pozit ¸ie k fixata. static int m[][]=new int[100][100]. × Ak ¸ si Ak+1 × . 4 matrice.. Pentru ca parantezarea s˘ a fie optimal˘ a. if(i!=k) { System.. deci o abordare recursiv˘ a ar conduce la rezolvarea aceleia¸ si subprobleme de mai multe ori. static int n.. Rezolvarea recursiv˘ a a relat ¸iei de recurent ¸˘ a este ineficient˘ a. public static void paranteze(int i. if(i<j) { k=m[j][i]. costul parantez˘ arii este egal cu numarul de ˆ ınmult ¸iri elementare necesare pentru calculul produsului Ai ×Ai+1 ×. Nu vom considera un alt tablou.i...print("(").

for(k=i+1.print(")"). } } m[i][j]=min. for(i=1. imin=i. System.i>=1.k<=j-1.println("Dimensiunile matricelor:"). p[i]=Integer.out.in)).k++) { v=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1]. }//if else paranteze(k+1.print("p["+i+"]=").j).parseInt(br.readLine()).k).3.print(")").print("numarul matricelor: ").print("A"+i). System.out.out.j<=n.readLine()). System. n=Integer.print("(").out.j). PROBLEME REZOLVATE System. }//for i.i++) { System. }//paranteze 69 public static void main(String[]args) throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System.j++) { min=m[i][i]+m[i+1][j]+p[i]*p[i+1]*p[j+1].j .out.print(" * ").out.i<=n+1.out. m[j][i]=imin.out. }//if else System. if(min>v) { min=v. paranteze(k+1.i--) for(j=i+1. System. imin=k. }//if else paranteze(i. } for(i=n. if(k+1!=j) { System.2.parseInt(br.

Evident Ai2 = (ai2 ≤ ai3 ≤ . ˆ ın ordinea ˆ ın care acestea apar ˆ ın A: ai1 . 30. 80). . 3. ai1 +1 .print("Ordinea inmultirilor: "). ..2 Sub¸ sir cresc˘ ator maximal Fie un ¸ sir A = (a1 . a2 . 6. an ). < ik ≤ n.n). an ).... aik . 10..... ≤ aik ) este cel mai lung sub¸ a a problemei cresc˘ ator al lui (ai2 . ai2 +1 . 8. Numim sub¸ sir al ¸ sirului A o succesiune de elemente din A.70 ˘ CAPITOLUL 3.out.. System. Se cere determinarea unui sub¸ sir cresc˘ ator al ¸ sirului A. 6. de lungime maxim˘ a. paranteze(1... o subproblem˘ init ¸iale const˘ aˆ ın determinarea celui mai lung sub¸ sir cresc˘ ator care ˆ ıncepe cu ai .. 40. De exemplu. 50. . 60. ai2 ..println("Numarul minim de inmultiri este: "+m[1][n]). n}. etc.println().. 30.. Rezolvare sir cresc˘ ator al 1. 80) o solut ¸ie poate fi (3. unde 1 ≤ i1 < i2 < . PROGRAMARE DINAMICA System.. . pentru A = (8.. ≤ aik ) cel mai lung sub¸ ¸ sirului A.. an ). . Observ˘ am c˘ a el coincide cu cel mai lung sub¸ sir cresc˘ ator al ¸ sirului sir (ai1 .. Fie Ai1 = (ai1 ≤ ai2 ≤ .out. 60. . 100. Prin urmare.. i = {1. System.out.2. 10. }//main }//class /* numarul matricelor: 8 Dimensiunile matricelor: p[1]=2 p[2]=8 p[3]=3 p[4]=2 p[5]=7 p[6]=2 p[7]=5 p[8]=3 p[9]=7 Numarul minim de inmultiri este: 180 Ordinea inmultirilor: ((((A1 * A2) * A3) * (A4 * A5)) * (A6 * A7)) * A8 */ 3.

for (int i=2. sau −1 dac˘ a un astfel de element nu exist˘ a. dac˘ a un astfel de element exist˘ a. 3. n}. fiecare cu n componente. poz[i]=j. . i++) if (max<l[i]) { max=l[i]. poz[n]=-1. j. i--) for (l[i]=1. pozmax=i. j=i+1. apoi afi¸ sa ˘m solut ¸ia.n unde poz [i] = indicele j pentru care se obt ¸ine maximul l[i]. i=poz[i]) cout<<a[i]<<’ ’. for (i=pozmax. Pentru a ret ¸ine solut ¸iile subproblemelor vom considera doi vectori l ¸ si poz . } cout<<"Lungimea celui mai lung subsir crescator: " <<max. j++) if (a[i] <= a[j] && l[i]<1+l[j]) { l[i]=1+l[j]. i!=-1.2. cout<<"\nCel mai lung subsir:\n". l[i] = max {1 + l[j ]|a[i] ≤ a[j ]} j =i+1. for (i=n-1. PROBLEME REZOLVATE 71 Subproblemele nu sunt independente: pentru a determina cel mai lung sub¸ sir cresc˘ ator care ˆ ıncepe cu ai . Relat ¸ia de recurent ¸˘ a care caracterizeaz˘ a substructura optimal˘ a a problemei este: l[n] = 1. ˆ ıncepˆ and cu pozit ¸ia maximului ¸ si utilizˆ and informat ¸iile memorate ˆ ın vectorul poz : //determin maximul din vectorul l int max=l[1].. avˆ and semnificat ¸ia: l[i] =lungimea celui mai lung sub¸ sir cresc˘ ator care ˆ ıncepe cu a[i]. Rezolv˘ am relat ¸ia de recurent ¸˘ aˆ ın mod bottom-up: int i. poz [i] =pozit ¸ia elementului care urmeaz˘ a dup˘ a a[i] ˆ ın cel mai lung sub¸ sir cresc˘ ator care ˆ ıncepe cu a[i].. l[n]=1. } Pentru a determina solut ¸ia optim˘ a a problemei.3. poz[i]=-1. j = {i + 1. i>0. ai ≤ aj . j<=n. poz [n] = −1. i<=n. pozmax=1. este necesar s˘ a determin˘ am cele mai lungi sub¸ siruri cresc˘ atoare care ˆ ıncep cu aj . 2. Secvent ¸ele de program de mai sus sunt scrise ˆ ın c/C++. . determin˘ am valoarea maxim˘ a din vectorul l.

for(k=1. jmax=j. } int max.io.i--) { max=0.jmax.j++) if((a[i]<=a[j])&&(max<lg[j])) { max=lg[j].println(max).k<=max. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("SubsirCrescatorNumere. public static void main(String []args) throws IOException { int i.i++) { st. } else lg[i]=1. for(i=1. for(j=1. } if(max!=0) { lg[i]=1+max. for(j=i+1. jmax=j.nextToken(). } max=0.out"))). poz=new int[n+1].j<=n.n=(int)st.nval.j<=n.nextToken().*. static int[] poz. PROGRAMARE DINAMICA Programele urm˘ atoare sunt scrise ˆ ın Java: import java. PrintWriter out = new PrintWriter ( new BufferedWriter( new FileWriter("SubsirCrescatorNumere. jmax=0. poz[i]=jmax.i<=n.nval.i>=1. j=poz[j]. for(i=n-1. class SubsirCrescatorNumere { static int n.in"))). lg[n]=1. static int[] a. static int[] lg. } out. jmax=0. st. int k.k++) { out.close(). a[i]=(int)st. lg=new int[n+1]. j=jmax. }//main }//class .print(a[j]+" ").j. a=new int[n+1]. } out.72 ˘ CAPITOLUL 3.j++) if(max<lg[j]){ max=lg[j].

for(i=n-2.//l[i]=lg.i<n.print("Solutia "). a=st. } out.k.charAt(j))&&(1+l[j]>1+l[jmax])) jmax=j. if(l[i]>lmax) { lmax=l[i].jmax.print("("+l[pozmax]+") : ").3. } } out.length().sval. l=new int[n].i>=0. st.celui mai lung subsir care incepe cu a[i] poz=new int[n].poz.io. out. n=a.nextToken().j++) if((a.charAt(i)<=a.charAt(k)). int [] l.j++) { out.lmax=-1. String a. out. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("SubsirCrescatorLitere. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("SubsirCrescatorLitere. l[n-1]=1. class SubsirCrescatorLitere { 73 public static void main(String[] args) throws IOException { int n.j<=lmax.//poz[i]=pozitia elementului care urmeaza dupa a[i] for(i=0.print(a. pozmax=i. poz[n-1]=-1.println(a+" "+n). for(j=1.pozmax=-1.j. for(j=i+1.2. k=pozmax. PROBLEME REZOLVATE import java.i--) { jmax=i.out"))). l[i]=1+l[jmax].close(). poz[i]=jmax.j<n.*.i++) poz[i]=-1. k=poz[k]. } // main }// class .i.in"))).

i<=n. n} S [i][j ] = T [i][j ] + max{S [i + 1][j ].1: Triunghi de numere Problema const˘ aˆ ın scrierea unui program care s˘ a determine cea mai mare sum˘ a de numere aflate pe un drum ˆ ıntre num˘ arul de pe prima linie ¸ si un num˘ ar de pe ultima linie. 2. Evident. 2. PROGRAMARE DINAMICA 3. ca ˆ ın exemplul urm˘ ator: 7 3 8 2 4 5 7 2 1 4 6 8 0 4 5 Tabelul 3. respectˆ and condit ¸iile problemei. 3.. Vom ret ¸ine triunghiul ˆ ıntr-o matrice p˘ atratic˘ a T .74 ˘ CAPITOLUL 3. j.2. de ordin n. solut ¸ia problemei va fi S [1][1]. pˆ an˘ a la un num˘ ar de pe ultima linie. Evident. Subproblemele problemei date constau ˆ ın determinarea sumei maxime care se poate obt ¸ine din numere aflate pe un drum ˆ ıntre numarul T [i][j ]. subproblemele nu sunt independente: pentru a calcula suma maxim˘ a a numerelor de pe un drum de la T [i][j ] la ultima linie. j++) . Pentru a ret ¸ine solut ¸iile subproblemelor. j<=i.. . fiecare linie cont ¸inˆ and numere ˆ ıntregi din domeniul [1. Relat ¸ia de recurent ¸a care caracterizeaz˘ a substructura optimal˘ a a problemei este: S [n][i] = T [n][i]. cu semnificat ¸ia S [i][j ] = suma maxim˘ a ce se poate obt ¸ine pe un drum de la T [i][j ] la un element de pe ultima linie. (IOI. la stˆ anga sau la dreapta sa. 99]. i++) S[n][i]=T[n][i]. fiecare num˘ ar din acest drum fiind situat sub precedentul. la stˆ anga sau la dreapta acestuia. i>0.3 Sum˘ a maxim˘ aˆ ın triunghi de numere S˘ a consider˘ am un triunghi format din n linii (1 < n ≤ 100). p˘ atratic˘ a de ordin n. vom utiliza o matrice S .. S [i + 1][j + 1]} 4. i--) for (j=1. for (i=1. for (i=n-1. Suedia 1994) Rezolvare 1. Fiecare num˘ ar din acest drum este situat sub precedentul. Rezolv˘ am relat ¸ia de recurent ¸˘ aˆ ın mod bottom-up: int i. trebuie s˘ a calcul˘ am suma maxim˘ a a numerelor de pe un drum de la T [i + 1][j ] la ultima linie ¸ si suma maxim˘ a a numerelor de pe un drum de la T [i + 1][j + 1] la ultima linie. sub diagonala principal˘ a. i = {1.

1. Exemplu Pentru X = (2. . problema cere determinarea LCS (Xn . n}.. } 75 Exercit ¸iu: Afi¸ sat ¸i ¸ si drumul ˆ ın triunghi pentru care se obt ¸ine solut ¸ia optim˘ a. y2 . LCS (Xk . Ym ). 8).. O subproblem˘ a a problemei date const˘ a ˆ ın determinarea celui mai lung sub¸ sir comun al lui Xk . 8. respectiv m numere ˆ ıntregi.. precum ¸ si un astfel de sub¸ sir. Observat ¸ie 1. Utilizˆ and aceste notat ¸ii.. 2.2. 5. dac˘ a x[k ] = y [h] max lcs[k ][h − 1]. Dac˘ a Xk = Y h atunci LCS (Xk... PROBLEME REZOLVATE { S[i][j]=T[i][j]+S[i+1][j]. 5. 2. yh ) prefixul lui Y de lungime h. . Vom caracteriza substructura optimal˘ a a problemei prin urm˘ atoarea relat ¸ie de recurent ¸˘ a: lcs[k ][0] = lcs[0][h] = 0. xk ) (prefixul lui X de lungime k ) ¸ si cu Yh = (y1 . Din observat ¸ia precedent˘ a deducem c˘ a subproblemele problemei date nu sunt independente ¸ si c˘ a problema are substructur˘ a optimal˘ a. 3.. 5.. if (S[i+1][j]<S[i+1][j+1]) S[i][j]=T[i][j]+S[i+1][j+1]).. lcs[k − 1][h]. 4..4 Sub¸ sir comun maximal Fie X = (x1 . Yh ) lungimea celui mai lung sub¸ sir comun al lui Xk .. 5. ym ) dou˘ a¸ siruri de n. 3. xn ) ¸ si Y = (y1 . 4. Yh ).3. 5. 5. 5. Notam cu LCS (Xk . 2.. Yh .. . 0. k = {1. 2. Yh−1 ). 2. Determinat ¸i un sub¸ sir comun de lungime maxim˘ a. Linia ¸ si coloana 0 sunt utilizate pentru init ¸ializare cu 0. dac˘ a x[k ] <> y [h] Rezolv˘ am relat ¸ia de recurent ¸˘ aˆ ın mod bottom-up: . iar elementul lcs[k ][h] va fi lungimea celui mai lung sub¸ sir comun al ¸ sirurilor Xk si Yh . m} lcs[k ][h] = 1 + lcs[k − 1][h − 1]. denumit˘ a lcs.. x2 . 6. x2 . Not˘ am cu Xk = (x1 .. 5. 6. Yh−1 )). 5. Y h) = max(LCS (Xk−1 . Yh . 3.. Pentru a ret ¸ine solut ¸iile subproblemelor vom utiliza o matrice cu n + 1 linii ¸ si m + 1 coloane. . 2. 3. Yh ) = 1 + LCS (Xk−1 . 3. 4. y2 . 5. 8) ¸ si Y = (6.2. Solutie 1. . . 8) o solut ¸ie posibil˘ a este: Z = (2. Dac˘ a Xk = Yh atunci LCS (Xk . h = {1.

k>=0. h--. k--. static int [][] a. h=m. din acest motiv vom memora solutia ˆ ıntr-un vector. pe care ˆ ıl vom afi¸ sa de la sfˆ ar¸ sit c˘ atre ˆ ınceput: cout<<"Lungimea subsirului comun maximal: " <<lcs[n][m]. ) if (x[k]==y[h]) { d[i++]=x[k]. k=n. De asemenea sunt afi¸ sate matricele auxiliare de lucru pentru a se putea urm˘ arii mai u¸ sor modalitatea de determinare recursiv˘ a a tuturor solut ¸iilor. Deoarece nu am utilizat o structur˘ a de date suplimentar˘ a cu ajutorul c˘ areia s˘ a memor˘ am solut ¸ia optim˘ a. h++) if (x[k]==y[h]) lcs[k][h]=1+lcs[k-1][h-1]. } else if (lcs[k][h]==lcs[k-1][h]) k--. cout<<"\nCel mai lung subsir comun este: \n". else lcs[k][h]=lcs[k][h-1]. Programul urm˘ ator este scris ˆ ın Java ¸ si determin˘ a toate solut ¸iile. PROGRAMARE DINAMICA for (int k=1. lcs[k][h]. vom reconstitui solut ¸ia optim˘ a pe baza rezultatelor memorate ˆ ın matricea lcs. ˆ ın ordine lexicografic˘ a.76 ˘ CAPITOLUL 3. k++) for (int h=1. h<=m. . // SubsirComunMaximal class scm { static PrintWriter out. int d[100]. else if (lcs[k-1][h]>lcs[k][h-1]) lcs[k][h]=lcs[k-1][h].io. else h--. for (k=i-1. Prin reconstituire vom obt ¸ine solut ¸ia ˆ ın ordine invers˘ a. k--) cout<< d[k] <<’ ’. import java. k<=n. Sunt determinate cea mai mic˘ a¸ si cea mai mare solut ¸ie. for (int i=0.*. Secvent ¸ele de cod de mai sus sunt scrise ˆ ın C/C++.

77 public static void main(String[] args) throws IOException { citire(). y=br.out"))). zmin=new char[z.out. System. out. afism(d). afisv(zmax). out. afism(a).m.m.3. z=new char[a[n][m]+1].readLine().zmin.close(). int i. out=new PrintWriter(new BufferedWriter( new FileWriter("scm. System.println("\nToate solutiile").length]. osol(n.n.out.a[n][m]). m=y.j. PROBLEME REZOLVATE static char [][] d. static static static static String x.println(nsol). System.print("SOLmin = ").2.// toate solutiile out. final char sus=’|’.out.y.length].println("O solutie oarecare").length(). toatesol(n. char[] z. int nsol=0.// o solutie System.length().m).out. diag=’*’. zmax=new char[z.print("SOLmax = ").in")). afisv(zmin).println(a[n][m]). } static void citire() throws IOException { BufferedReader br=new BufferedReader(new FileReader("scm.zmax. stanga=’-’. } .readLine(). n=x. matrad(). x=br.

if(a[i-1][j]>a[i][j-1]) d[i][j]=sus.col-1).78 ˘ CAPITOLUL 3.charAt(lin-1)). zminmax(). while(a[i-1][col]==k)//merg in sus . } static void toatesol(int lin.col-1).j<=m.print(x. for(i=1. } } static void osol(int lin.i++) for(j=1.charAt(j-1)) // x.a[i][j-1]). else if(d[lin][col]==sus) osol(lin-1.j. } else { a[i][j]=max(a[i-1][j]. afisv(z). PROGRAMARE DINAMICA static void matrad() { int i. if(d[lin][col]==diag) osol(lin-1.out. if(d[lin][col]==diag) System.charAt(i)==y.print(++nsol+" "). d=new char[n+1][m+1].i<=n. } i=lin+1. else d[i][j]=stanga.out.charAt(j) ! { a[i][j]=1+a[i-1][j-1]. return. if(k==0) { System. else osol(lin.col).int k) { int i. int col) { if((lin==0)||(col==0)) return.j++) if(x. int col. d[i][j]=diag.j. a=new int[n+1][m+1].charAt(i-1)==y.

} static int compar(char[] z1. } }//while } static void zminmax() { if(nsol==1) { copiez(z. 0=identice. char[] z2)//-1=<. } 79 .// z1 si z2 au n componente 1. j=col+1. toatesol(i-1. while(z1[i]==z2[i]) i++.n if(i>n) return 0.length.zmax).j-1.i++) z2[i]=z1[i]. else return 1. 1=> { int i=1. else if(compar(z. char[] z2) { int i.zmin).zmax). } static void copiez(char[] z1.charAt(i-1). } else if(compar(z. PROBLEME REZOLVATE { i--.2. copiez(z. while(a[i][j-1]==k) j--.3.k-1). if( (a[i][j-1]==k-1)&&(a[i-1][j-1]==k-1)&&(a[i-1][j]==k-1)) { z[k]=x.zmax)>0) copiez(z..zmin).zmin)<0) copiez(z. for(i=1.i<z1. else if(z1[i]<z2[i]) return -1.

out. for(j=0. for(j=0.j++) System.print(a[i][j]+" ").length.out.j++) System. System. } static void afism(int[][]a)// difera tipul parametrului !!! { int n=a. System. m=a[i].j.j<=m.print(x.out. } static void afism(char[][]d)// difera tipul parametrului !!! { int n=d.out.print(y.print(" ").out.i<n.out.length(). System. System.length.out.charAt(j)+" ").m. System.println().println(). for(i=1.charAt(j)+" "). System.j++) System.println("\n").println().out.j++) System. PROGRAMARE DINAMICA static int max(int a. System. m=y.j++) System.length.print(d[0][j]+" "). int i.print(y.length().j. int b) { if(a>b) return a.charAt(i-1)+" ").println().80 ˘ CAPITOLUL 3.out. int i.j<m.j<m. } System.out. for(j=0.j<m. for(j=0.print(a[0][j]+" ").print(" ").m.out.out.out. for(j=0. .i++) { System.j<=m.out. else return b. System. m=y.print(" ").out.out. System.println().print(" ").

2. PROBLEME REZOLVATE for(i=1.out.i++) System.println("\n").println().out.println(). System.length-1.j++) System. out.i<n.i++) out.out.i++) { System. for(j=0. for(i=1.print(d[i][j]+" "). } System.out.length-1. } static void afisv(char[]v) { int i.print(x.charAt(i-1)+" ").j<m. m=d[i].println().out.print(v[i]). for(i=1.3. } } Pe ecran apar urm˘ atoarele rezultate: 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 3 0 1 1 2 2 2 2 2 2 2 2 2 2 2 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 6 0 1 2 2 3 3 4 4 4 4 4 4 4 5 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 c 0 1 2 2 3 4 4 5 5 6 6 6 6 b 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 f 0 1 2 2 3 4 4 5 6 6 7 7 8 e 0 1 2 2 3 4 4 5 6 6 7 8 8 81 1 2 3 4 5 6 a b c d e f 1 3 2 4 6 5 a c b d f e 1 2 3 4 * | | | * | * * - .i<=v.length.i<=v.print(v[i]). System.out.

inserare ¸ si modificare) dintre ¸ sirurile de caractere s1 ¸ si s2.5 Distant ¸a minim˘ a de editare Fie d(s1. ””) = 0 d(s.82 5 6 a b c d e f | | | | | | | | | | | | | | | | | | | | | | | | * | | | | | | * * | | | | | * | | | * * | | * * - ˘ CAPITOLUL 3.1) (3. PROGRAMARE DINAMICA 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 3. la sfˆ ar¸ situl acestuia (dar dup˘ a modific˘ arile efectuate deja). ””) = d(””.2.2.2. s2) distant ¸a de editare (definit˘ a ca fiind num˘ arul minim de operat ¸ii de ¸ stergere.2) Avˆ and ˆ ın vedere ultima operat ¸ie efectuat˘ a asupra primului ¸ sir de caractere. (3. obt ¸inem: . s) = |s|. Atunci: d(””.

j.j ]).j<=length(s2).. s2 + ch2 ) = min (3. stanga=’-’. // dir = directii pentru minim !!! // a--> b static String a. class DistEdit { static final char inserez=’i’.bj char [][] op..i].b.*. m[i][j]=min( m[i-1][j-1]+val. Algoritmul ˆ ın pseudocod este: def m[0][0]=0. i<=length(s1). j++) { val=(s1[i]==s2[j]) ? 0 : 1.2.io. static static static static int na.nb.|s2|] unde c[i][j ] = d(s1 [1. for(i=1.. i++) for(j=1. // op = operatia efectuata char [][] dir. i<=length(s1). // c[i][j] = cost a1.. . int [][] c. modific=’m’. diags=’x’. ˆ ınlocuire Folosim o matrice c[0. // inserez inaintea pozitiei sterg=’s’. } Programul surs˘ a: import java. ¸ stergere ch1 1 2 2 d(s1 + ch1 . public static void main(String[] args) throws IOException { int i. s + ch ) + 1. for(j=1. nimic=’ ’.2.3)  0 dac˘ a ch1 = ch2 .ai --> b1. m[i-1][j]+1. j++) m[0][j]=j.|s1|][0. s ) +  1 2 1 dac˘ a ch1 = ch2 . m[i][j-1]+1 ). s2 [1.3. i++) m[i][0]=i.cmin=0.. inserare ch2    d(s . nimic!   d ( s . j<=length(s2). s2 ) + 1. for(i=1. // caracter !!! static final char sus=’|’. PROBLEME REZOLVATE 83  d(s1 + ch1 ..

na=a. a=br.readLine().j<=nb. b=vid !!! op[i][0]=sterg.i<=na. op[i][j]=nimic.out. for(i=1. //t_j dir[0][j]=stanga. c=new int[na+1][nb+1]. } op[0][0]=nimic.i<=na.length().i++) { for(j=1.charAt(i-1)==b.out. } a=vid !!! .i++) { c[i][0]=i.j++) { if(a. } for(j=1.j<=nb. b=br. op[0][j]=inserez. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("distedit. // s_i dir[i][0]=sus. PROGRAMARE DINAMICA BufferedReader br=new BufferedReader( new FileReader("distedit.length(). op=new char[na+1][nb+1]. System. for(i=1. // inserari in a.j++) { c[0][j]=j. dir[i][j]=diags.out"))). dir[0][0]=nimic. nb=b.println(b+" --> "+nb).charAt(j-1)) { c[i][j]=c[i-1][j-1].readLine().in")). // stergeri din a.println(a+" --> "+na). dir=new char[na+1][nb+1]. System.84 ˘ CAPITOLUL 3.

85 .charAt(j-1)).j++) out.out. afissol(na. System.out). dir[i][j]=sus. } }// else }// for j }// for i afismi(c. } else if(cmin==c[i-1][j]) // sterg s_i { op[i][j]=sterg.out).c[i-1][j]. if(i>0) out.c[i][j-1]).print(" "). for(j=1. for(i=0.3. afismc(op.i<=na.j.2. }// main static void afismc(char[][] x. dir[i][j]=diags. if(cmin==c[i][j-1]) // inserez t_j { op[i][j]=inserez.println("COST transformare = "+c[na][nb]).charAt(i-1)).print(b. afismc(dir. out. dir[i][j]=stanga.out).println("\nCOST transformare = "+c[na][nb]).out). out. c[i][j]=1+cmin.nb.println(). PROBLEME REZOLVATE else { cmin=min(c[i-1][j-1].print(a.close(). else out. out. PrintWriter out) { int i.print(" "). } else if(cmin==c[i-1][j-1]) //s_i-->t_j { op[i][j]=modific.i++) { out.j<=nb.

charAt(j-1)). } else if(i==0) out. if(i>0) out. else if(j==0) out.int j. PrintWriter out) { if(i==0&&j==0) return.j++) out.print(b.charAt(j-1)). } out.i++) { out.charAt(j-1)). if(dir[i][j]==diags) afissol(i-1.println("\n").print(x[i][j]).j-1.j.) static void afissol(int i.println(i+" "+a.) static void afismi(int[][] x.charAt(i-1)+" "+op[i][j]+" "+j+" "+b.out).. PROGRAMARE DINAMICA for(j=0.print(" ").j.j<=nb.i<=na. out. PrintWriter out) { int i. else out.charAt(j)).out).j-1.86 ˘ CAPITOLUL 3. if((i>0)&&(j>0)) { if(op[i][j]==sterg) out. .print(a..j++) out. else if(dir[i][j]==stanga) afissol(i.println(). else if(op[i][j]==inserez) out.charAt(i-1)+" "+op[i][j]+" "+j+" "+b.j<=nb.println("\n").charAt(i-1)+" "+op[i][j]).println(i+" "+a. for(j=1. } out. for(i=0.println(i+" "+a.print(" ").charAt(i-1)). }// afismi(.print(x[i][j]). }// afismc(.charAt(i)+" "+op[i][j]+" "+j+" "+b. else if(dir[i][j]==sus) afissol(i-1.println(" "+op[i][j]+" "+j+" "+b...println(i+" "+a.j<=nb.out).charAt(j-1)).j++) out. else out. for(j=0.

int c) { return min(a. int b.2. PROBLEME REZOLVATE }//afissol(.) static int min(int a..3. } }// class Rezultate afi¸ sate ˆ ın fi¸ sierul de ie¸ sire: altruisti 0123456789 a1012345678 l2101234567 g3211234567 o4322234567 r5433234567 i6544333456 t7654444445 m8765555555 i9876665665 altruisti --------a|x-------l||x------g|||x-----o||||x----r||||x----i|||||xx--x t|||x|||xxm|||||||||x i||||||x-|x altruisti iiiiiiiii as iiiiiiii lss iiiiiii gsssmiiiiii ossssmiiiii rssss iiiii isssssm ii tsss sssm i msssssssssm issssss is 87 .min(b.. } static int min(int a.c)). int b) { return a<b ? a:b.

class Rucsac01 { public static void main(String[] args) throws IOException { int n. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("rucsac. g[i]=(int)st.nval. int[] g. v=new int[n+1].out"))). } for(i=1. n=(int)st.v. m=(int)st. c=new int[n+1][m+1]. i++) c[i][0]=0. int[][] c.j.in"))). st. v[i]=(int)st. int i.2. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("rucsac. st.nval.nextToken().nval.nval.nextToken().i++) { st. g=new int[n+1].nextToken().i<=n.m.nextToken().88 1 2 3 4 5 6 7 8 9 a 1 l 2 g m 3 o s r 4 i 5 i 6 i 7 t 8 m s i 9 a l t r u i s t i ˘ CAPITOLUL 3. for(i=1. .i++) { st.i<=n. i<=n.*. } for(i=1. PROGRAMARE DINAMICA COST transformare = 5 3.6 Problema rucsacului (0 − 1) import java.io.

nextToken(). v=(int)st.i++) { st.2.) static int max(int a. i<=n.out"))).in"))). else return b. }// main(.nextToken().nextToken().println(c[n][m]). PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("schimb. int b) { if(a>b) return a.n.*. i++) for(j=1. n=(int)st. j++) if(g[i]>j) c[i][j]=c[i-1][j].close(). else c[i][j]=max( c[i-1][j]. out. ns=new int[v+1].. b[i]=(int)st. class Schimb { public static void main(String[] args) throws IOException { int v. }// max(.i<=n. b=new int[n+1].. j<=m.3.j. out. j<=m. } .io. c[i-1][j-g[i]] + v[i] ). j++) c[0][j]=0.2.7 Problema schimbului monetar import java. for(i=1. for(i=1. ns.) } 89 3.nval.. st. st.nval.nval. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("schimb. int[] b. PROBLEME REZOLVATE for(j=0.. int i.

j.nval. class Traversare { public static void main(String[] args) throws IOException { int m.8 Problema travers˘ arii matricei Traversarea unei matrice de la Vest la Est. . for(i=0. int[] d.minc. m=(int)st. i++) for(j=b[i]. int[m][n].t.in"))). import java. st.j<n.2. int[][] a.nextToken().n. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("traversare. j<=v.println(ns[v]). out. a=new c=new t=new d=new int[m][n]. j++) ns[j]+=ns[j-b[i]].i<m.io. sunt permise deplas˘ ari spre vecinii unei pozit ¸ii (chiar ¸ si deplas˘ ari prin ”exteriorul” matricei). int cmin. out. int i.i++) for(j=0.nextToken(). }// main }// class 3.imin. int[m][n].nval. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("traversare.j++) { st. int[n]. st. n=(int)st.out"))).close().nextToken().c. for(i=1.*. i<=n.90 ˘ CAPITOLUL 3. PROGRAMARE DINAMICA ns[0]=1.

PROBLEME REZOLVATE a[i][j]=(int)st. } out.close(). j=n-1. else return b.i<m. cmin=c[0][n-1]... while(j>0) { i=t[i][j]/n. } imin=0. int b. out. j--. j++) for(i=0. i=imin. i++) { minc=min(c[(i+m-1)%m][j-1]. d[n-1]=imin*n+(n-1).2. else t[i][j]=((i+1)%m)*n+(j-1). c[i][j-1].) static int min(int a. d[j]=i*n+j.i++) if(c[i][n-1]<cmin) { cmin=c[i][n-1].3. }// main(. } for(i=0. for(j=1. i<m. if(minc==c[(i+m-1)%m][j-1]) t[i][j]=((i+m-1)%m)*n+(j-1). j<n. c[i][j]=a[i][j]+minc.i++) c[i][0]=a[i][0]. int b) { if(a<b) return a.nval. } for(j=0.j++) out. int c) 91 . c[(i+1)%m][j-1]).println(cmin).j<n. for(i=1. else if(minc==c[i][j-1]) t[i][j]=i*n+(j-1). imin=i.println((1+d[j]/n)+" "+(1+d[j]%n)). } static int min(int a.i<m.

Evident c[i][i + 1] = 0 pentru c˘ a nu este necesar˘ a nici o t˘ aietur˘ a. class Vergea { static int n.c)... Costul pentru t˘ aierea vergelei [xi .xj ] este format din costul transportului acesteia la ma¸ sina de t˘ aiat (xj − xi ) + costul t˘ aierii vergelei [xi . ˆ In viat ¸a real˘ a.xj ]. static int t[][]. . < xn < xn+1 ˆ ın care xn+1 reprezint˘ a lungimea vergelei iar x1 .xj ] (adic˘ a c[k ][j ])..9 Problema segment˘ arii vergelei Scopul algoritmului este de a realiza n t˘ aieturi. Pentru j > i s˘ a presupunem c˘ a realiz˘ am prima t˘ aietur˘ aˆ ın xk (i < k < j ). Obt ¸inem: c[i][j ] = xj − xi + min {c[i][k ] + c[k ][j ]} i<k<j import java.....out 3 4 6 2 1 3 2 2 1 1 3 5 4 1 2 3 4 2 7 3 3 1 4 */ 3. static int x[]..xj ] obt ¸inem dou˘ a bucat ¸i mai mici: [xi . PROGRAMARE DINAMICA return min(min(a.in traversare.nt=0.. .xk ] ¸ si [xk . x2 . cu efort minim (sau cost). static int c[][].xk ] (adic˘ a c[i][k ]) ¸ si + costul t˘ aierii vergelei [xk . Costul fiec˘ arei operat ¸ii de t˘ aiere este proport ¸ional cu lungimea vergelei care trebuie t˘ aiat˘ a. xn reprezint˘ a abscisele punctelor ˆ ın care se vor realiza t˘ aieturile (distant ¸ele fat ¸˘ a de cap˘ atul ”din stˆ anga” al vergelei). ce valoare are k ? Evident..*..xj ]. Din vergeaua [xi . Dar. dea lungul unei vergele ˆ ın locuri pre-specificate.2.....io. ne putem imagina costul ca fiind efortul depus pentru a plasa vergeaua (sau un bu¸ stean!) ˆ ın ma¸ sina de t˘ aiat. Consider˘ am ¸ sirul de numere naturale 0 < x1 < x2 < .. Not˘ am prin c[i][j ] (i < j ) costul minim necesar realiz˘ arii tuturor t˘ aieturilor segmentului de vergea [xi .b). k trebuie s˘ a aib˘ a acea valoare care s˘ a minimizeze expresia c[i][k ] + c[k ][j ]...92 { ˘ CAPITOLUL 3. } }// class /* traversare.

k<=j-1.i++) System.nval.MAX_VALUE. n=(int)st.print("x: ").out.in)). t=new int[n+2][n+2]. } c[i][j]+=min.i<=n+1. }//for i . StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("vergea.h.out"))). // pentru "stop" (pentru depanare!) public static void main(String[]args) throws IOException { int i.i<=n+1. } System.j. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("vergea.i<=n+1-h. for(i=1. x[i]=(int)st. for(k=i+1.h<=n+1.min.k. for(h=2.2.nextToken().println().println("n="+n). j=-1. for(i=1.i++) c[i][i+1]=0. System. for(i=0.print(x[i]+" "). st.3.nextToken(). // sfarsitul vargelei c[i][j]=x[j]-x[i].i++) // inceputul vargelei { j=i+h. System.out.i<=n.i++) { st. c=new int[n+2][n+2]. x=new int[n+2].out.nval.k++) if(c[i][k]+c[k][j]<min) { min=c[i][k]+c[k][j]. min=Integer. PROBLEME REZOLVATE 93 static BufferedReader br. kmin=-1.out.h++) // lungimea vargelei { for(i=0. t[i][j]=kmin.in"))). br=new BufferedReader(new InputStreamReader(System.kmin. kmin=k.

j. System.println().94 }// for h out.. ˘ CAPITOLUL 3.int j) throws IOException { if(i>=j-1) return.out. out.i++) { for(j=0. }//main public static void taieturi(int i. int k.out..out.out. PROGRAMARE DINAMICA afism(c). afism(t). }// afism(.readLine().println((++nt)+" : "+i+"..close(). System.print(x[i][j]+" ").println(c[0][n+1]). k=t[i][j].j).println("Ordinea taieturilor: \n").n+1). System.println().) }//class /* n=5 x: 2 4 5 8 12 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 8 16 27 38 3 9 19 29 0 4 12 22 0 0 7 17 0 0 0 7 0 0 0 0 0 0 0 0 . } System. System. //br. } }//taieturi static void afism(int[][] x) // pentru depanare ! { int i.out.i<=n+1.println(). taieturi(0."+j+" --> "+k).out. for(i=0. // "stop" pentru depanare ! if((i<k)&&(k<j)) { taieturi(i. taieturi(k.j<=n+1.k).j++) System.

k.6 0. 6.2. Diagonalele triangulat ¸iei optime vor genera o triangulat ¸ie optim˘ a a poligoanelor convexe [1... 7. n (ˆ ın figur˘ a n = 9) ¸ si dorim s˘ a obt ¸inem o triangularizare ˆ ın care suma lungimilor diagonalelor trasate s˘ a fie minim˘ a (ca ¸ si cum am dori s˘ a consum˘ am cˆ at mai put ¸in tu¸ s pentru trasarea acestora!).2. dac˘ a j =i+1 .4 0. 9] face parte din triunghiul [1. 5] ¸ si [5. i + 1. Atunci: c[i][j ] = 0. j ] (i < k < j ) i¸ si prin c[i][j ] costul minim al triagulat ¸iei poligonului convex [i. j ] (unde i < j ). 3.. PROBLEME REZOLVATE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 2 0 0 0 0 0 2 3 3 0 0 0 0 3 4 4 4 0 0 0 4 4 4 4 5 0 0 95 Ordinea taieturilor: 1 : 2 : 3 : 4 : 5 : */ 0. S˘ a presupunem c˘ aˆ ıntr-o anumit˘ a triangulat ¸ie optim˘ a latura [1.2 2.. 9].6 --> --> --> --> --> 4 2 1 3 5 3. S˘ a not˘ am prin p(i. 8.3.. 4.10 Triangularizarea poligoanelor convexe Consider˘ am un poligon convex cu n vˆ arfuri numerotate cu 1. 2. k..4 4. Consider˘ am la ˆ ınceput latura [1. 5.. orice latur˘ a a poligonului face parte dintr-un triunghi al triangulat ¸iei. 2. Au ap˘ arut astfel dou˘ a subprobleme ale problemei init ¸iale. 1 2 9 8 7 3 4 6 5 3 4 5 5 5 6 4 555 2 1 9 9 8 7 3 6 6 2 11 9 9 1 8 8 8 7 7 Evident. 9]. . ... j ) perimetrul triunghiului [i. 9]...

k. j ) + c[i][k ] + c[k ][j ]}.96 ¸ si ˘ CAPITOLUL 3. PROGRAMARE DINAMICA c[i][j ] = min {p(i. dac˘ ai<j≤n i≤k<j .

. . Consider˘ am m≤n¸ si dorim s˘ a determin˘ am dac˘ a textul t cont ¸ine ¸ sablonul p.. t2 .. public static void main(String[] args) throws IOException { int i.*..j. BufferedReader br=new BufferedReader( new FileReader("potriviresir. numit pattern ˆ ın englez˘ a) p = (p1 .1 Un algoritm ineficient Pentru fiecare pozit ¸ie i cuprins˘ aˆ ıntre 1 ¸ si n − m +1 vom verifica dac˘ a sub¸ sirul (xi .m. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("potriviresir. class PotrivireSir { static char[] t. xi+m−1 ) coincide cu y . xi+1 . . tn ) ¸ si un ¸ sablon (tot un ¸ sir de caractere.. import java.io. String s..out"))). dac˘ a exist˘ a 0 ≤ d ≤ n − m astfel ˆ ıncˆ at td+i = pi pentru orice 1 ≤ i ≤ m. 4. adic˘ a. Problema potrivirii ¸ sirurilor const˘ aˆ ın determinarea tuturor valorilor d (considerate deplasamente) cu proprietatea ment ¸ionat˘ a..in")). p2 ..p. pm ).Capitolul 4 Potrivirea ¸ sirurilor Consider˘ am un text (un ¸ sir de caractere) t = (t1 . .. static int n. 97 .

j--.out.i<n.print(x[i]). }// afisv(.i++) { for(j=1.out.length().readLine().out. int i1. int i2) { int i. afisv(p.print(" ").m).98 CAPITOLUL 4.i<m.n). for(i=1. System. for(i=0.1.1.out. t=new char[n+1].i++) System. m=s..out.close().j<=m.length(). }//main() static void afisv(char[] x.) }//class /* x : abababaababaababa y : abaabab abababaababaababa abaabab abababaababaababa abaabab */ .out. POTRIVIREA S ¸ IRURILOR s=br..i.i<=i2.println().i++) p[i+1]=s.i+j-1). for(i=0. System. System.i<=n-m+1.charAt(i).i++) System.i<i1.println(). afisv(p. n=s. System.n). // ultima pozi\c tie potrivita if(j==m) { afisv(t.1.charAt(i). for(i=1.println(). p=new char[m+1].out.print("t : "). System. } } out.i++) t[i+1]=s. for(i=i1.print("p : "). afisv(t.readLine(). s=br.j++) if(p[j]!=t[i+j-1]) break.

f˘ ar˘ a s˘ a risc˘ am s˘ a pierdem vreo solut ¸ie! . UN ALGORITM EFICIENT .2.KMP 99 4. Dorim s˘ a avans˘ am cu mai mult de un pas la o nou˘ aˆ ıncercare.4. Pentru t: 1 a ¸ si 1 a 2 b 3 a 1 a 3 a 4 b 2 b 4 b 5 a 3 a 5 a a 1 5 a 6 b 4 a 6 b b 2 6 b 7 a 5 b 7 a a 3 7 a 8 a 6 a 8 a a 4 8 a 9 b 7 b 9 b b 5 9 b 10 a 11 b 12 a 13 a 14 b 15 a 16 b 17 a p: 2 b exist˘ a dou˘ a potriviri: 10 a a 6 10 a a 1 11 b b 7 11 b b 2 12 a 13 a 14 b 15 a 16 b 17 a t: p: t: p: 1 a 2 b 3 a 4 b 12 a a 3 13 a a 4 14 b b 5 15 a a 6 16 b b 7 17 a S ¸ irul ineficient de ˆ ıncerc˘ ari este: 1 a a 2 b b a 3 a a b a 4 b a a b a 5 a b a a b a 6 b a b a a b a 7 a b a b a a b a 8 a b a b a a b a 9 b 10 a 11 b 12 a 13 a 14 b 15 a 16 b 17 a t: p: p: p: p: p: p: p: p: p: p: p: b a b a a b a b a b a a b a b a b a a b a 1 * b a b a a b 2 b a b a a 3 b a b a 4 b a b 5 b a 6 * b 7 Prima nepotrivire din fiecare ˆ ıncercare este evident ¸iat˘ a prin caracter boldat iar solut ¸iile sunt marcate cu *.KMP Algoritmul KMP (Knuth-Morris-Pratt) determin˘ a potrivirea ¸ sirurilor folosind informat ¸ii referitoare la potrivirea sub¸ sirului cu diferite deplasamente ale sale.2 Un algoritm eficient .

j ] ¸ si t[i] = p[j + 1]. m .. ... j j+1 ... .... Cum determin˘ am practic valorile funct ¸iei next? . Dat fiind ¸ sirul de caractere p[1. deci t[i − j......j ] secvent ¸a de elemente consecutive (ti . . POTRIVIREA S ¸ IRURILOR 8 a a a 9 b b b 10 a 11 b 12 a 13 a 14 b 15 a 16 b 17 a t: p: p: p: p: p: a a b b * a a b a a b b * a Not˘ am prin t[i.k ] este sufix pentru p[1..... . S˘ a presupunem c˘ a suntem la un pas al verific˘ arii potrivirii cu un deplasament d¸ si prima nepotrivire a ap˘ arut pe pozit ¸ia i din text ¸ si pozit ¸ia j + 1 din ¸ sablon.. tn ).... m i-j .j ]}. dorim s˘ a determin˘ am cel mai lung sufix al secvent ¸ei p[1. i-1 k i k+1 .1: Deplasare optim˘ a Folosind Figura 4..... 1.. 2.100 1 a a 2 b b 3 a a a 4 b a b 5 a b a a 6 b a a b 7 a b b a CAPITOLUL 4. dorim s˘ a determin˘ am cel mai mare indice k < j astfel ˆ ıncˆ at p[1.. .j ] iar noul deplasament d′ trebuie ales astfel ˆ ıncˆ at s˘ a realizeze acest lucru. m − 1} este definit˘ a astfel: next(j ) = max{k/k < j ¸ si p[1... R˘ amˆ ane s˘ a verific˘ am apoi dac˘ a t[i] = p[k + 1]. t: d' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx 1 . .k ] = p[j + 1 − k. Observ˘ am c˘ a noul deplasament depinde numai de ¸ sablonul p ¸ si nu are nici o leg˘ atur˘ a cu textul t..j ].... Algoritmul KMP utilizeaz˘ a pentru determinarea celor mai lungi sufixe o funct ¸ie numit˘ a next....k ]... j+1-k i-k 1 . tj ) (cuprins˘ aˆ ıntre pozit ¸iile i ¸ si j ) din ¸ sirul t = (t1 ...2. Care este cel mai bun deplasament d′ pe care trebuie s˘ a-l ˆ ıncercam? d p: 1 .. Cu alte cuvinte. t[i − k.. Este astfel realizat˘ a ¸ si potrivirea textului t cu ¸ sablonul p. funct ¸ia next : {1.. m} → {0..i − 1] = p[1.m]. . t2 .. . n p: ..i − 1] = p[1. Figura 4..

.. Dar acest sufix mai mic este cel mai lung sufix pentru p[1.k ′ ].. .. .. . Ce se ˆ ıntˆ ampl˘ a dac˘ a p[j + 1] = p[k + 1]? C˘ aut˘ am un sufix mai mic pentru p[1.m]=pattern static int[] next.. Dac˘ a ajungem la 0. .. Ordinul de complexitate al algoritmului KMP este O(n + m)... import java. acest algoritm se termin˘ aˆ ıntr-un num˘ ar finit de pa¸ si pentru c˘ a j > k > k′ > .KMP Initializ˘ am next[1] = 0.... cu alte cuvinte k ′ = next(k ) = next(next(j )).. k+1 j+1 k'+1 .. UN ALGORITM EFICIENT .io.. .... . Dac˘ a p[j +1] = p[k +1] (folosind notat ¸iile din figur˘ a) atunci next[j +1] = k +1. ≥ 0.2..2: Funct ¸ia next Ne ajut˘ am de Figura 4. ..... dac˘ a p[j + 1] = p[k ′ + 1] atunci next(j + 1) = k ′ + 1. atunci vom avea next(j + 1) = 0. .n]=text. j+1-k ...*.. p[1.p. // t[1.. class KMP { static int na=0. Astfel. . // nr aparitii static char[] t.. Dac˘ a nici acum nu avem egalitate de caractere. . ...j ]! Fie acesta p[1. Evident. next[2].2 pentru a urm˘ ari mai u¸ sor rat ¸ionamentul! ˆ In aceast˘ a ′ figur˘ a next[j ] = k ¸ si next[k ] = k ..... vom continua acela¸ si rat ¸ionament pˆ an˘ a cand g˘ asim o egalitate de caractere sau lungimea prefixului c˘ autat este 0. .k ]. Cum determin˘ am next[j + 1]? xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 101 1 k+1-k' k p: p: 1 . p: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx j+1-k' .. j 1 k' Figura 4... . Obt ¸inem: dac˘ a p[j + 1] = p[next(j ) + 1] atunci next[j + 1] = next(j ) + 1..4. .. Presupunem c˘ a au fost determinate valorile next[1]. . Obt ¸inem: dac˘ a p[j + 1] = p[next(next(j )) + 1] atunci next[j + 1] = next(next(j )) + 1... next[j ].

. s=br.in")). // nr caractere potrivite deja int i=1.i++) p[i]=sc[i-1]. }//readData() static int[] calcNext(char[] p) { int m=p. } return next.i<=m.. // nr caractere potrivite int j=2. if(p[k+1]==p[j]) k++. sc=s. p=new char[m+1]. t=new char[n+1].length-1. int[] next=new int[m+1]. // ultima pozitie a sufixului while(i<=n) // t[1. n=sc. char[] sc.n.m] pentru p[1.toCharArray(). next[j]=k.. j++.toCharArray().. sc=s. BufferedReader br=new BufferedReader(new FileReader("kmp.i++) t[i]=sc[i-1]. int i. m=sc.length-1.m] next[1]=0. POTRIVIREA S ¸ IRURILOR static void readData() throws IOException { String s. while(j<=m) { while(k>0&&p[k+1]!=p[j]) k=next[k]. for(i=1. p[1.n] { .i<=n.readLine(). for(i=1.readLine().n]. next=calcNext(p).. }// calcNext() static void kmp(char[] t.length.102 CAPITOLUL 4. // next[1.length.m] { int n=t. s=br. int j=0..length-1. // initializare int k=0. char[] p) // t[1. m=p.m.

.println().print(" "). for(i=1. for(i=1.out. afissol(t. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("kmp.print(p[i]). UN ALGORITM EFICIENT .i++) System.) public static void main(String[] args) throws IOException { readData().i++) System.close(). } i++.p). System.i-m).4.KMP 103 while(j>0&&p[j+1]!=t[i]) j=next[j].length-1. for(i=1.p. out. }// afissol(.out.out. out. if(p[j+1]==t[i]) j++..2.out. char[] p. System.i<=d.out.println(). j=next[j]. System. m=p. if(j==m) { na++. n=t.println(na). }//main() }//class /* pattern apare cu deplasarea 5 : 12312123412123412 1234 pattern apare cu deplasarea 11 : 12312123412123412 1234 */ .println("pattern cu deplasarea "+(i-m)+" : "). int d) { int i.i<=m.i++) System.i<=n.out"))).out. }// while }// kmp static void afissol(char[] t.print(t[i]).length-1. kmp(t.

io. doar ¸ tinem cont c˘ a indicii care se refer˘ a la ¸ sirul ”mai lung” trebuie luat ¸i modulo n.. . .out se va scrie cel mai mic num˘ ar natural p pentru care ¸ sirul de pe linia a treia este o permutare circular˘ a cu p pozit ¸ii a ¸ sirului de pe linia a doua.Balcescu” . POTRIVIREA S ¸ IRURILOR 4. yn este o permutare circular˘ a cu p pozit ¸ii a ¸ sirului x1 . dar nu se ˆ ıncadreaz˘ aˆ ın timp pentru n mare..1 secunde Rezolvare (indicat ¸ia autorului): O variant˘ a cu dou˘ a ”for”-uri e foarte u¸ sor de scris. Pe liniile urm˘ atoare sunt dou˘ a¸ siruri de caractere de lungime n.1 Probleme rezolvate Circular .. x2 .. class Circular { static int n.3 4. unde indicii mai mari ca n se consider˘ a modulo n. // pozitia de potrivire . Folosim algoritmului KMP de c˘ autare a unui sub¸ sir..104 CAPITOLUL 4. formate numai din litere mari ale alfabetului latin. ˆ In realitate nu e nevoie de concatenarea efectiv˘ aa ¸ sirului. import java...d=-1.*. y2 .Braila Se spune c˘ a¸ sirul y1 .Campion 2003-2004 Runda 6 Autor: prof.in este scris numarul natural n. adic˘ a indicele k . yn = xp + n..in 10 ABCBAABBAB BABABCBAAB circular. sau num˘ arul −1 dac˘ a nu avem o permutare circular˘ a. Cerint ¸˘ a Pentru dou˘ a¸ siruri date determinat ¸i dac˘ a al doilea este o permutare circular˘ a a primului ¸ sir.3. Date de intrare Pe prima linie a fi¸ sierului de intrare circular. Restrict ¸ii ¸ si preciz˘ ari • 1 ≤ n ≤ 20000 Exemple circular. . xn dac˘ a y1 = xp + 1.. cu k > n se refer˘ a la elementul de indice k − n.out 7 Timp maxim de execut ¸ie/test: 0. Concaten˘ am primul ¸ sir cu el ˆ ınsu¸ si ¸ si c˘ aut˘ am prima aparit ¸ie a celui de-al doilea ¸ sir ˆ ın ¸ sirul nou format. y2 = xp + 2. Colegiul National ”N. Mot Nistor. Date de ie¸ sire Pe prima linie a fi¸ sierului circular.

PROBLEME REZOLVATE static char[] x. // System. j++.n]=text.3.m] pentru p[1. }//readData() static int[] calcNext(char[] p) { int m=n.println("x="+s).m] next[1]=0.out...y. y=new char[n+1].readLine(). while(j<=m) { while(k>0&&p[k+1]!=p[j]) k=next[k].parseInt(br. char[] p) // t[1. int j=0.println("n="+n). // System. int i.out.i++) x[i]=sc[i-1]. s=br..4..i<=n.i++) y[i]=sc[i-1]. // nr caractere potrivite deja int i=1. x=new char[n+1]. next=calcNext(p). BufferedReader br=new BufferedReader(new FileReader("circular. s=br. if(p[k+1]==p[j]) k++..m]=pattern static int[] next. } return next. n=Integer. next[j]=k. // x[1. // initializare int k=0..m] { int m=p. for(i=1. // next[1. 105 static void readData() throws IOException { String s. sc=s. // System. sc=s.toCharArray(). }// calcNext() static void kmp(char[] t. p[1.in")). char[] sc. int[] next=new int[m+1]. // ultima pozitie a sufixului .println("y="+s). for(i=1. y[1.readLine()..i<=n. // nr caractere potrivite int j=2.toCharArray().n].length-1.readLine()).out.

}// while }// kmp public static void main(String[] args) throws IOException { readData(). } i++. out. p(2)=1. if(j==m) { d=i-n.in circular. codificarea mesajului se realizeaz? astfel: se nlocuie?te fiecare liter? ci cu p(ci).2 Cifru . Alfabetul solarian con?ine m litere foarte complicate. Pentru codificare ei folosesc un cifru bazat pe o permutare p a literelor alfabetului solarian ?i un num?r natural d. out. Date de intrare Fi?ierul de intrare cifru.out ---------------------20 5 12312123412123412341 12341212341234112312 */ 4. determina?i cifrul folosit (permutarea p ?i num?rul d). permutarea p=(3 1 2)(adic? p(1)=3. c1 c2 cn. De exemplu. pentru mesajul 2 1 3 3 2 1.println(d). Dat fiind un mesaj n limbaj solarian. POTRIVIREA S ¸ IRURILOR while((i<=2*n)&&(d==-1)) // t[1. kmp(x.106 CAPITOLUL 4. Cerin?? Date fiind un mesaj necodificat ?i codificarea sa.in con?ine pe prima linie numele .close(). apoi ?irul ob?inut p(c1) p(c2) p(cn) se rote?te spre dreapta. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("circular. apoi rotind spre dreapta ?irul cu dou? pozi?ii ob?inem codificarea 1 3 1 3 2 2. if(p[j+1]==t[(i>n)?(i-n):i]) j++.out"))).. }//main() }//class /* circular. reprezentat de noi ca o succesiune de n numere cuprinse ntre 1 ?i m. a?a c? noi le vom reprezenta prin numere de la 1 la m. p(3)=2) ?i d=2. f?cnd o permutare circular? cu d pozi?ii rezultnd ?irul p(cn-d+1) p(cn-1) p(cn) p(c1) p(c2) p(cn-d).n] { while(j>0&&p[j+1]!=t[(i>n)?(i-n):i]) j=next[j]. break.y). Aplicnd permutarea p vom ob?ine ?irul 1 3 2 2 1 3.3.ONI2006 baraj Copiii solarieni se joac? adesea trimi?ndu-?i mesaje codificate.

Pentru teste cu m ? 5 se acord? 40 de puncte din care 20 pentru teste ?i cu n ? 2000. Dac? pentru d exist? mai multe posibilit??i se va alege valoarea minim?. m] cel pu?in o dat?. separate prin spa?iu. p(2).out va con?ine pe prima linie num?rul natural d. Pe urm?toarea linie este descris? permutarea p. p(m) separate prin cte un spa?iu. . PROBLEME REZOLVATE 107 naturale n ?i m. Pe cea de a doua linie este scris mesajul necodificat ca o succesiune de n numere cuprinse ntre 1 ?i m separate prin cte un spa?iu.3.in cifru. Pe cea de a treia linie este scris mesajul codificat ca o succesiune de n numere cuprinse ntre 1 ?i m separate prin cte un spa?iu.out 6 3 2 1 3 3 2 1 1 3 1 3 2 2 2 3 1 2 Timp maxim de execu?ie/test: 0. reprezentnd num?rul de pozi?ii cu care s-a realizat permutarea circular? spre dreapta. reprezentnd lungimea mesajului ?i respectiv num?rul de litere din alfabetul solarian. Exemplu cifru. Mai exact se vor scrie valorile p(1). Date de ie?ire Fi?ierul de ie?ire cifru. Restric?ii n ? 100 000 m ? 9999 Mesajul con?ine fiecare num?r natural din intervalul [1.2 secunde .4.

POTRIVIREA S ¸ IRURILOR .108 CAPITOLUL 4.

y1 ). P3 (x3 . y3 )) = (y2 − y1 ) · (x3 − x2 ) − (y3 − y2 ) · (x2 − x1 ) 109 . cazul b) ˆ ın figur˘ a Orientarea depinde de valoarea expresiei o(P1 (x1 . P2 (x2 . cazul a) ˆ ın figur˘ a •ˆ ın sensul acelor de ceas (spre dreapta): m12 > m23 . P2 (x2 . y1 ). Panta segmentului P1 P2 : m12 = (y2 − y1 )/(x2 − x1 ) Panta segmentului P2 P3 : m23 = (y3 − y2 )/(x3 − x2 ) P3 P3 P2 P2 P1 a) P1 b) P1 c) P2 P3 Orientarea parcurgerii laturilor P1 P2 ¸ si P2 P3 (ˆ ın aceast˘ a ordine): •ˆ ın sens trigonometric (spre stˆ anga): m12 < m23 . y2 ) ¸ si P3 (x3 . y3 ). cazul c) ˆ ın figur˘ a • vˆ arfuri coliniare: m12 = m23 .1 Determinarea orient˘ arii Consider˘ am trei puncte ˆ ın plan P1 (x1 . y2 ).Capitolul 5 Geometrie computat ¸ional˘ a 5.

110 astfel

˘ CAPITOLUL 5. GEOMETRIE COMPUTAT ¸ IONALA

5.2

  < 0 ⇒ sens trigonometric o(P1 (x1 , y1 ), P2 (x2 , y2 ), P3 (x3 , y3 )) = 0 ⇒ coliniare   > 0 ⇒ sensul acelor de ceas

Testarea convexit˘ a¸ tii poligoanelor

Consider˘ am un poligon cu n vˆ arfuri P1 (x1 , y1 )P2 (x2 , y2 )...Pn (xn yn ), n ≥ 3. Poligonul este convex dac˘ a¸ si numai dac˘ a perechile de segmente (P1 P2 , P2 P3 ), (P2 P3 , P3 P4 ), ..., (Pn−2 Pn−1 , Pn−1 Pn ) ¸ si (Pn−1 Pn , Pn P1 ) au aceea¸ si orientare sau sunt colineare.
P7 P6 P5 P1 P2 P3 P4 P1 P7 P6 P5 P2 P3 P4

a)

b)

5.3

Aria poligoanelor convexe

Aria poligonului convex cu n vˆ arfuri P1 (x1 , y1 )P2 (x2 , y2 )...Pn (xn yn ), n ≥ 3 se poate determina cu ajutorul urm˘ atoarei formule: 1 |x1 y2 + x2 y3 + ... + xn−1 yn + xn y1 − y1 x2 + y2 x3 + ... + yn−1 xn + yn x1 | 2 Expresia de sub modul este pozitiv˘ a dac˘ a orientarea P1 → P2 → ...Pn → P1 este ˆ ın sens trigonometric, este negativ˘ a dac˘ a orientarea P1 → P2 → ...Pn → P1 este ˆ ın sensul acelor de ceasornic ¸ si este nul˘ a dac˘ a punctele P1 (x1 , y1 ), P2 (x2 , y2 ), ..., Pn (xn yn ) sunt colineare. Reciproca acestei afirmat ¸ii este deasemenea adev˘ arat˘ a ˆ ın cazul poligoanelor convexe.

5.4

Pozit ¸ia unui punct fat ¸˘ a de un poligon convex

˘ DE UN POLIGON CONCAV 5.5. POZIT ¸ IA UNUI PUNCT FAT ¸A

111

Consider˘ am un poligon convex cu n vˆ arfuri P1 (x1 , y1 )P2 (x2 , y2 )...Pn (xn yn ), n≥3¸ si un punct P0 (x0 , y0 ). Dorim s˘ a determin˘ am dac˘ a punctul P0 (x0 , y0 ) este ˆ ın interiorul poligonului. Pentru comoditatea prezent˘ arii consider˘ am ¸ si punctul Pn+1 (xn+1 , yn+1 ) unde x1 = xn+1 ¸ si y1 = yn+1 , adic˘ a punctul Pn+1 este de fapt tot punctul P1 . Consider˘ am o latur˘ a oarecare [Pi Pi+1 ] (1 ≤ i ≤ n) a poligonului. Ecuat ¸ia dreptei (Pi Pi+1 ) este (Pi Pi+1 ) : y − yi = yi+1 − yi (x − xi ) xi+1 − xi

Aducem la acela¸ si numitor ¸ si consider˘ am funct ¸ia fi (x, y ) = (y − yi ) · (xi+1 − xi ) − (x − xi ) · (yi+1 − yi ) Dreapta (Pi Pi+1 ) ˆ ımparte planul ˆ ın dou˘ a semiplane. Funct ¸ia fi (x, y ) are valori de acela¸ si semn pentru toate punctele din acela¸ si semiplan, valori cu semn contrar pentru toate punctele din cel˘ alalt semiplan ¸ si valoarea 0 pentru doate punctele situate pe dreapt˘ a. Pentru a fi siguri c˘ a punctul P0 (x0 , y0 ) se afl˘ aˆ ın interiorul poligonului (acesta fiind convex) trebuie s˘ a verific˘ am dac˘ a toate vˆ arfurile poligonului ˆ ımpreun˘ a cu punctul P0 (x0 , y0 ) sunt de aceea¸ si parte a dreptei (Pi Pi+1 ), adic˘ a toate valorile fi (xj , yj ) (1 ≤ j ≤ n, j = i ¸ si j = i + 1) au acela¸ si semn cu fi (x0 , y0 ) (sau sunt nule dac˘ a accept˘ am prezent ¸a punctului P0 (x0 , y0 ) pe frontiera poligonului). Aceasta este o condit ¸ie necesar˘ a dar nu ¸ si suficient˘ a. Vom verifica dac˘ a pentru orice latur˘ a [Pi Pi+1 ] (1 ≤ i ≤ n) a poligonului toate celelalte vˆ arfuri sunt ˆ ın acela¸ si semiplan cu P0 (x0 , y0 ) (din cele dou˘ a determinate de dreapta suport a laturii respective) iar dac˘ a se ˆ ıntˆ ampl˘ a acest lucru atunci putem trage concluzia c˘ a punctul P0 (x0 , y0 ) se afl˘ aˆ ın interiorul poligonului convex. O alt˘ a modalitate de verificare dac˘ a punctul P0 (x0 , y0 ) este ˆ ın interiorul sau pe frontiera poligonului convex P1 (x1 , y1 )P2 (x2 , y2 )...Pn (xn yn ) este verificarea urm˘ atoarei relat ¸ii:
n

ariepoligon (P1 P2 ...Pn ) =
k=1

arietriunghi (P0 Pk Pk+1 )

unde punctul P (xn+1 , yn+1 ) este de fapt tot punctul P1 (x1 , y1 ).

5.5

Pozit ¸ia unui punct fat ¸˘ a de un poligon concav

Consider˘ am un poligon concav cu n vˆ arfuri P1 (x1 , y1 )P2 (x2 , y2 )...Pn (xn yn ), n≥3¸ si un punct P0 (x0 , y0 ). Dorim s˘ a determin˘ am dac˘ a punctul P0 (x0 , y0 ) este ˆ ın interiorul poligonului.

112

˘ CAPITOLUL 5. GEOMETRIE COMPUTAT ¸ IONALA

Poligonul concav se descompune ˆ ın poligoane convexe cu ajutorul diagonalelor interne ¸ si se folose¸ ste un algoritm pentru poligoane convexe pentru fiecare poligon convex astfel obt ¸inut. Dac˘ a punctul este ˆ ın interiorul unui poligon convex obt ¸inut prin partit ¸ionarea poligonului concav atunci el se afl˘ aˆ ın interiorul acestuia. Dac˘ a nu se afl˘ aˆ ın nici un poligon convex obt ¸inut prin partit ¸ionarea poligonului concav atunci el nu se afl˘ aˆ ın interiorul acestuia.
P7 P6 P5 P1 P2 P3 P4 P1 P2 P3 P4 P7 P6 P5

a)

b)

5.6

ˆ Inf˘ a¸ sur˘ atoarea convex˘ a

5.6.1

ˆ Impachetarea Jarvis

5 4 3 2 1 1 2 3 4 5 6 7

5 4 3 2 1 1 2 3 4 5 6 7

a)

b)

Toate punctele de pe ˆ ınf˘ a¸ sur˘ atoarea convex˘ a (cazul a) ˆ ın figur˘ a): import java.io.*; // infasuratoare convexa class Jarvis1 // pe frontiera coliniare ==> le iau pe toate ... !!! {

˘ ¸ URATOAREA ˘ ˘ 5.6. ˆ INFAS CONVEXA static static static static static int int int int int n,npic=0; [] x; [] y; [] p; [] u;

113

// npic=nr puncte pe infasuratoarea convexa

// precedent // urmator

static void afisv(int[] a, int k1, int k2) { int k; for(k=k1;k<=k2;k++) System.out.print(a[k]+" "); System.out.println(); } static int orient(int i1, int i2, int i3) { long s=(y[i1]-y[i2])*(x[i3]-x[i2])-(y[i3]-y[i2])*(x[i1]-x[i2]); if(s<0) return -1; else if(s>0) return 1; else return 0; } static void infasurareJarvis() throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); int i0,i,i1,i2; i0=1; for(i=2;i<=n;i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i; System.out.println("Stanga_Jos ==> P"+i0+" : "+x[i0]+" "+y[i0]); i1=i0; npic++; System.out.println(npic+" --> "+i1); //br.readLine(); do { i2=i1+1; if(i2>n) i2-=n; for(i=1;i<=n;i++) { //System.out.println("orient("+i1+","+i+","+i2+")="+orient(i1,i,i2)); //br.readLine(); if(orient(i1,i,i2)>0) i2=i; else if(orient(i1,i,i2)==0) // coliniare if( // i intre i1 i2 ==> cel mai apropiat ((x[i]-x[i1])*(x[i]-x[i2])<0)|| ((y[i]-y[i1])*(y[i]-y[i2])<0)

npic++. // npic=nr puncte pe infasuratoarea convexa static int [] x.. }// infasurareJarvis() public static void main(String[] args) throws IOException { int k. // precedent static int [] u. i1=i2.114 ) i2=i. y[k]=(int)st. x[k]=(int)st. ˘ CAPITOLUL 5.1. for(k=1.nval.1.out.in"))). // infasuratoare convexa class Jarvis2 // pe frontiera coliniare ==> iau numai capetele . u=new int[n+1]. p=new int[n+1]. y=new int[n+1]. System. } while(i2!=i0). st. !!! { static int n. StreamTokenizer st= new StreamTokenizer( new BufferedReader(new FileReader("jarvis. //br. n=(int)st.print("p : ").k++) { st.nval.out.k<=n. }//main }//class F˘ ar˘ a punctele coliniare de pe ˆ ınf˘ a¸ sur˘ atoarea convex˘ a (cazul b) ˆ ın figur˘ a): import java. afisv(p.*.nextToken().npic=0.print("u : ").n).out. x=new int[n+1]. st. System.n). afisv(u.. GEOMETRIE COMPUTAT ¸ IONALA } u[i1]=i2. // urmator .nval.io.nextToken().println(npic+" --> "+i1). static int [] p. } infasurareJarvis(). p[i2]=i1.nextToken(). // apare de doua ori primul punct ! System. static int [] y.readLine(). npic--.

if(i2>n) i2-=n. } static void infasurareJarvis() throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System.i."+i+". i1=i2.println("Stanga_Jos ==> P"+i0+" : "+x[i0]+" "+y[i0]). System. //br. if(s<0) return -1. if(orient(i1. System. i0=1.6.˘ ¸ URATOAREA ˘ ˘ 5. for(i=2.k++) System. ˆ INFAS CONVEXA static void afisv(int[] a. . for(i=1.println(npic+" --> "+i1). int k1. int i2.i++) { //System.readLine().i2.i<=n. p[i2]=i1.in)). int i3) { long s=(y[i1]-y[i2])*(x[i3]-x[i2])-(y[i3]-y[i2])*(x[i1]-x[i2]).println("orient("+i1+". int i0.out. for(k=k1.println().i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i.out. do { i2=i1+1. System.print(a[k]+" ").i.i2)).i1. else if(orient(i1."+i2+")="+orient(i1.i. } 115 static int orient(int i1.readLine().i<=n. //br.i2)>0) i2=i. int k2) { int k.out. else return 0.out.k<=k2. i1=i0. npic++.i. } u[i1]=i2. else if(s>0) return 1.out.i2)==0) // coliniare if( // i2 intre i1 i ==> cel mai departat ((x[i2]-x[i1])*(x[i2]-x[i])<0)|| ((y[i2]-y[i1])*(y[i2]-y[i])<0) ) i2=i.

afisv(p.n). npic--. static int[] y... x[k]=(int)st. afisv(u.nextToken()..n).. // npic=nr puncte pe infasuratoarea convexa static int[] x. static int[] o.nextToken(). System.2 Scanarea Craham Versiune cu mesaje pentru sortarea punctelor: import java. // numai pentru sortare si mesaje .nval. }//main }//class 5.6.. n=(int)st. // of[k] = pozitia lui k dupa sortare // pentru depanare . y=new int[n+1]. System. p=new int[n+1]. // apare de doua ori primul punct ! System. u=new int[n+1]. static BufferedReader br=new BufferedReader(new InputStreamReader(System.1.print("u : "). StreamTokenizer st= new StreamTokenizer( new BufferedReader(new FileReader("jarvis. x=new int[n+1]. GEOMETRIE COMPUTAT ¸ IONALA npic++.116 ˘ CAPITOLUL 5.. y[k]=(int)st.*.out.println(npic+" --> "+i1). . stop ! .out.nval.nextToken(). //br.readLine().io.nval. st.in"))). class Graham0 { static int n. } infasurareJarvis().1.print("p : ").in)). // o[k] = pozitia lui k inainte de sortare static int[] of.npic=0. } while(i2!=i0). }// infasurareJarvis() public static void main(String[] args) throws IOException { int k.k++) { st.k<=n.out. for(k=1. st.

} . else if(s>0) return 1.k++) System.k<=k2.out.6. else return 0. if(s<0) return -1.int i1. int i2) { long s=(y[i1]-y[i0])*(x[i2]-x[i0])-(y[i2]-y[i0])*(x[i1]-x[i0]). for(k=k1. ˆ INFAS CONVEXA 117 5 4 3 2 1 1 2 3 4 5 6 7 5 4 3 2 1 1 2 3 4 5 6 7 a) b) 5 4 3 2 1 1 2 3 4 5 6 7 5 4 3 2 1 1 2 3 4 5 6 7 a) b) static void afisv(int[] a. } static int orient(int i0.out. int k2) { int k. int k1.print(a[k]+" "). System.˘ ¸ URATOAREA ˘ ˘ 5.print(" ").

} }// while System.. o[i]=o[j]. y[j]=aux. System.out. System.j=u.println("Final while .out.u).k)==0)&& (((x[i]-x[1])*(x[i]-x[k])<0)||((y[i]-y[1])*(y[i]-y[k])<0))) ) ) { i++. x[i]=x[j]. GEOMETRIE COMPUTAT ¸ IONALA static void qsort(int p.u).j..out. int i.j.118 ˘ CAPITOLUL 5. i="+i+" // i=j si P[i] este pe locul lui !!! j="+j).i. afisv(x.out. afisv(y. aux=y[i].k)==0)&& (((x[j]-x[1])*(x[j]-x[k])>0)||((y[j]-y[1])*(y[j]-y[k])>0))) ) ) { j--.k)>0)|| ((orient(1.out. System.print("y : "). while(i<j) { while( (i<j)&& ( (orient(1. } while( (i<j)&& ( (orient(1. y[i]=y[j].p. System. . } if(i<j) { if(k==i) k=j. System.println("qsort: p="+p+" u="+u+" k="+k+" xk="+x[k]+" yk="+y[k]).println().i. x[j]=aux. aux=o[i].k)<0)|| ((orient(1.j.print("x : "). int u) throws IOException { // aleg un punct fix k int k=(p+u)/2. o[j]=aux.p.// k=fix dar se poate schimba pozitia aux=x[i].out.aux.println(). i=p. else if(k==j) k=i.

afisv(o.println().n). afisv(y. afisv(x.i+1.i.) static void scanareGraham() throws IOException { int i0. System. System. y[i0]=aux. afisv(x. System.println().i2.i+1. afisv(o.i-1).out.1.out.out.i-1). afisv(x. System.println().1.out.out. System. i0=1.print("o : "). System.n). System.p. afisv(y.print("y : "). if(p<i-1) qsort(p.println().˘ ¸ URATOAREA ˘ ˘ 5. afisv(x. o[i0]=aux. System.n). System. System. System.out.print("y : ").println().out.print("x : "). System.n). br. System. .i<=n. qsort(2.i).n). }// scanareGraham() public static void main(String[] args) throws IOException { int k. System.out.out. x[1]=x[i0].print("x : ").out. System.i-1).println().n). System..print("y : "). afisv(x.i. System.n). }// qSort(.p.aux.println("Stanga_Jos ==> P"+i0+" : "+x[i0]+" "+y[i0]+"\n").out. if(j+1<u) qsort(j+1. aux=y[1].6.1.n).1..out. System. y[1]=y[i0]. ˆ INFAS CONVEXA 119 System.1. System.print("y : ").out.i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i.out.println().out.println().u).readLine(). System. for(i=2.out.out. System.1. System.out.println(). aux=o[1].print("x : ").out. afisv(y.out.i1.println(). o[1]=o[i0].out.u).out.1.out.i. System.i).println().print("o : ").println().n).u). aux=x[1].out.out.print("x : "). afisv(y. afisv(y.println(). x[i0]=aux. System. afisv(y. afisv(x.println().1.

out.out. } scanareGraham(). GEOMETRIE COMPUTAT ¸ IONALA StreamTokenizer st= new StreamTokenizer( new BufferedReader(new FileReader("graham.*.. atunci . static int np.k<=n.println().120 ˘ CAPITOLUL 5. System. y=new int[n+1]. o[k]=k.nval. int i2) { long s=(y[i1]-y[i0])*(x[i2]-x[i0])-(y[i2]-y[i0])*(x[i1]-x[i0]). // poligonul infasuratoare convexa static int orient(int i0. st.io. // pozitia inainte de sortare static int[] p.1. { // NU prinde punctele coliniarele pe prima latura. y[k]=(int)st. if(s<0) return -1.k<=n. afisv(of. System. // np=nr puncte pe infasuratoarea convexa static int[] x.. } static void qsort(int p.int i1. of=new int[n+1].out. System. // NU prinde punctele coliniarele pe ultima latura ! class Graham1 // daca schimb ordinea pe "razele" din sortare. o=new int[n+1]. for(k=1.k++) { st.nextToken(). static int n. asa ca . x=new int[n+1]. else return 0.out. static int[] o. st. else if(s>0) return 1. System.in"))). // ordinea finala (dupa sortare) a punctelor for(k=1.nval.println().println(). }//main }//class Versiune cu toate punctele de pe ˆ ınf˘ a¸ sur˘ atoare: import java.nextToken().nextToken().. n=(int)st.k++) of[o[k]]=k. static int[] y.print("of : ").. x[k]=(int)st. int u) .n).nval.

i<=n. j=u. aux=y[i].j. } } if(p<i-1) qsort(p.u). aux=o[1]. ˆ INFAS CONVEXA { 121 int i. x[1]=x[i0].k)==0)&& (((x[j]-x[1])*(x[j]-x[k])>0)||((y[j]-y[1])*(y[j]-y[k])>0))) ) ) j--.k)>0)|| ((orient(1. if(i<j) { if(k==i) k=j. aux=o[i].j.k.. o[i]=o[j]. i=p.i3. else if(k==j) k=i.) static void scanareGraham() throws IOException { int i0. . while(i<j) { while( (i<j)&& ( (orient(1.i-1). o[j]=aux.i2. x[i0]=aux.i.j. aux=x[1].k)==0)&& (((x[i]-x[1])*(x[i]-x[k])<0)||((y[i]-y[1])*(y[i]-y[k])<0))) ) ) i++. o[1]=o[i0]. // aleg un punct fix k k=(p+u)/2. y[j]=aux.aux. x[j]=aux. i0=1. y[i]=y[j]. if(j+1<u) qsort(j+1. o[i0]=aux.i1. }// qSort(.˘ ¸ URATOAREA ˘ ˘ 5. for(i=2.// k=fix dar se poate schimba pozitia aux=x[i]..i. x[i]=x[j]. while( (i<j)&& ( (orient(1. y[i0]=aux.aux.k)<0)|| ((orient(1.i. aux=y[1].n).i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i.6. y[1]=y[i0]. qsort(2.

i3)>0) { i2=p[np-1]. p[1]=i1. for(i=1.print(y[p[i]]+" ").print("infasuratoare x: ").println().print(o[p[i]]+" "). i1=p[np-1]. .i)==0) p[++np]=i--.nextToken(). }// scanareGraham() public static void main(String[] args) throws IOException { int k.122 ˘ CAPITOLUL 5.print(x[p[i]]+" "). for(i=1. System. System. i2=2. i2=p[np].nval. GEOMETRIE COMPUTAT ¸ IONALA i1=1. i1=p[np-2]. } np++. System.print("infasuratoare y: ").println(). np--.i<=np.out. System.i++) System. StreamTokenizer st= new StreamTokenizer( new BufferedReader(new FileReader("graham1. while(i3<=n) { while(orient(i1.out. for(i=1. n=(int)st. System. while(orient(1. p[2]=i2.p[np].i++) System. }// while // plasez si punctele coliniare de pe ultima latura a infasuratorii i=n-1.i2. st.out. // afisez rezultatele System. i3++.out.out.i<=np.in"))).out.print("punctele initiale: ").out.println().out. i3=3.i++) System.i<=np.out. np=2. p[np]=i3.

infasuratoarea nu contine puncte coliniare . // poligonul infasuratoare convexa static int orient(int i0.... }//main }//class Versiune f˘ ar˘ a puncte coliniare pe ˆ ınf˘ a¸ sur˘ atoare: import java. int[n+1]. static int[] o. i=p.˘ ¸ URATOAREA ˘ ˘ 5. while(i<j) { . 123 for(k=1.int i1. // aici .. int u)// elimin si punctele coliniare (din interior) { int i. // aleg un punct fix k k=(p+u)/2.j. j=u. o[k]=k.k++) { st.k<=n. y[k]=(int)st. // pozitia inainte de sortare static int[] p. class Graham2 // este o eliminare din rezultatul final dar . // np=nr puncte pe infasuratoarea convexa static int[] x.nextToken(). int i2) { long s=(y[i1]-y[i0])*(x[i2]-x[i0])-(y[i2]-y[i0])*(x[i1]-x[i0]). static int np.nextToken(). st. else return 0. static int[] y.nval.6.k. else if(s>0) return 1. int[n+1].aux. x[k]=(int)st.io. } static void qsort(int p. } scanareGraham()...nval. int[n+1]. ˆ INFAS CONVEXA x=new y=new o=new p=new int[n+1]. if(s<0) return -1. { // se pot elimina puncte la sortare si/sau scanare . static int n..*..

n). if(j+1<u) qsort(j+1. y[i]=y[j]. if(i<j) { if(k==i) k=j.j. np=2. x[i0]=aux. aux=o[i]. y[i0]=aux.. o[i0]=aux. x[1]=x[i0]. } } if(p<i-1) qsort(p.k)>0)|| ((orient(1.i. x[i]=x[j].k)<0)|| ((orient(1. p[2]=i2. o[1]=o[i0]. i2=2. else if(k==j) k=i.j.i3. p[1]=i1.124 while( ˘ CAPITOLUL 5.k)==0)&& (((x[j]-x[1])*(x[j]-x[k])>0)||((y[j]-y[1])*(y[j]-y[k])>0))) ) ) j--. aux=o[1]. o[i]=o[j]. i1=1. o[j]=aux.// k=fix dar se poate schimba pozitia aux=x[i]. while(i3<=n) { .. aux=y[1].i<=n. while( (i<j)&& ( (orient(1.k)==0)&& (((x[i]-x[1])*(x[i]-x[k])<0)||((y[i]-y[1])*(y[i]-y[k])<0))) ) ) i++.i2. i3=3.i.u).i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i.i. aux=y[i].i1. aux=x[1]. GEOMETRIE COMPUTAT ¸ IONALA (i<j)&& ( (orient(1.aux. }// qSort(.i-1). qsort(2. y[1]=y[i0]. for(i=2.) static void scanareGraham() throws IOException { int i0. i0=1. y[j]=aux. x[j]=aux.

System.i2. i1=p[np-1]. System.out. ˆ INFAS CONVEXA while(orient(i1. for(i=1. StreamTokenizer st= new StreamTokenizer( new BufferedReader(new FileReader("graham2. System.in"))). n=(int)st.print(y[p[i]]+" ").i<=np.out.˘ ¸ URATOAREA ˘ ˘ 5. st.i++) if(o[p[i]]!=0) System.out.out.print(o[p[i]]+" "). p=new int[n+2]. System.out.i<=np-1. } np++.i<=np.print(x[p[i]]+" ").out.p[i+2])==0) o[p[i+1]]=0.println(). for(i=1.out. y=new int[n+2]. for(i=1.i++) if(orient(p[i].i<=np.i++) if(o[p[i]]!=0) System.i++) if(o[p[i]]!=0) System. x=new int[n+2]. np--.k<=n. for(k=1.out. for(i=1.print("infasuratoare x: ").k++) { .6. i1=p[np-2].p[i+1].print("infasuratoare y: ").println().println().i3)>0) // elimin i2 { i2=p[np-1]. System. }// while // eliminarea punctelor coliniare de pe infasuratoare p[np+1]=p[1]. i2=p[np].out. }// scanareGraham() public static void main(String[] args) throws IOException { int k. p[np]=i3. 125 // afisez rezultatele System.print("punctele initiale: "). i3++. o=new int[n+2].nval.nextToken().

1: Dreptunghi minim de acoperire Putem s˘ a presupunem c˘ a punctele formeaz˘ a un poligon convex. }//main }//class 5. x[k]=(int)st.126 ˘ CAPITOLUL 5. Dintre aceste dreptunghiuri se alege cel cu aria minim˘ a. o[k]=k. Pentru fiecare latur˘ a a poligonului convex se determin˘ a dreptunghiul minim de acoperire care cont ¸ine acea latur˘ a.nextToken().7 Dreptunghi minim de acoperire a punctelor Se poate determina dreptunghiul minim de acoperire pentru ˆ ınf˘ a¸ sur˘ atoarea convex˘ a (figura 5. .nval. M P7 A P1 P2 N xmin D P6 P12 P10 P3 P11 P4 P5 C Q xmax ymin R ymax P8 P9 B Figura 5. st. GEOMETRIE COMPUTAT ¸ IONALA st. y[k]=(int)st. } scanareGraham().1) pentru a prelucra mai put ¸ine puncte dar nu este obligatorie aceast˘ a strategie.nextToken(). Determinarea dreptunghiului de arie minim˘ a care cont ¸ine ˆ ın interiorul s˘ au (inclusiv frontiera) toate punctele date se poate face observˆ and c˘ a o latur˘ a a sa cont ¸ine o latur˘ a a poligonului convex.nval.

y1 ). y2 )... Ovidiu Dom¸ sa . pas cu pas.. Pn (xn . CERC MINIM DE ACOPERIRE A PUNCTELOR 127 5.ONI2004 clasa a IX-a lect. . . Ci ¸ si trebuie s˘ a determin˘ am cercul Ci+1 . Ck = C k-1 P k x P k+1 Ck Ck C k+1 a) b) Ordon˘ am punctele astfel ˆ ıncˆ at pe primele pozit ¸ii s˘ a fie plasate punctele de extrem (cel mai din stˆ anga. bi . P3 (x3 . Dac˘ a punctul Pi+1 nu se afl˘ aˆ ın interiorul cercului Ci atunci cercul Ci+1 se determin˘ a reluˆ ınd algoritmul pentru ¸ sirul de puncte P1 . urmat de cel mai din dreapta. r2 ) unde a2 = (x1 + x2 )/2. P2 . Not˘ am cu Ci (ai . bi ) ¸ si raz˘ a minim˘ a ri care acoper˘ a punctele P1 . C3 .8 Cerc minim de acoperire a punctelor Se poate determina cercul minim de acoperire pentru ˆ ınf˘ a¸ sur˘ atoarea convex˘ a pentru a prelucra mai put ¸ine puncte dar nu este obligatorie aceast˘ a strategie.. dup˘ a ordonare. r2 = 1 2 1 2 1 1 2 2 S˘ a presupunem c˘ a am determinat. Presupunem c˘ a punctele. Pn . urmat de cel mai de sus. P2 (x2 .9 5. Consider˘ am cercul C2 (a2 . sunt: P1 (x1 .5.. P2 .. Putem plasa acest punct pe prima pozit ¸ie ˆ ın ¸ sirul punctelor ¸ si astfel vom impune la fiecare pas ca punctul P1 s˘ a fie pe cercul care trebuie determinat! 5. Dac˘ a punctul Pi+1 se afl˘ aˆ ın interiorul cercului Ci atunci cercul Ci+1 este identic cu Ci . Pi+1 dar impunˆ and condit ¸ia ca acest cerc s˘ a treac˘ aˆ ın mod obligatoriu prin punctul Pi+1 (xi+1 . yi+1 ).. .8. cercurile C2 . b2 .Pi .. y3 ).. . b2 = (y1 + y2 )/2 ¸ si 2 + (y − y )2 ..1 Probleme rezolvate Seceta .9. ri ) cercul de centru (ai . adic˘ ( x − x ) a cercul de diametru [ P P ]. yn ).. urmat de cel mai de jos.. dup˘ a acestea urmeaz˘ a celelalte puncte ˆ ıntr-o ordine oarecare).

128

˘ CAPITOLUL 5. GEOMETRIE COMPUTAT ¸ IONALA

Gr˘ adinile roditoare ale B˘ ar˘ aganului sufer˘ a anual pierderi imense din cauza secetei. C˘ aut˘ atorii de ap˘ a au g˘ asit n fˆ antˆ ani din care doresc s˘ a alimenteze n gr˘ adini. Fie Gi , Fi , i = 1, ..., n puncte ˆ ın plan reprezentˆ and puncte de alimentare ale gr˘ adinilor ¸ si respectiv punctele ˆ ın care se afl˘ a fˆ antˆ anile. Pentru fiecare punct se dau coordonatele ˆ ıntregi (x, y ) ˆ ın plan. Pentru a economisi materiale, leg˘ atura dintre o gr˘ adin˘ a¸ si o fˆ antˆ an˘ a se realizeaz˘ a printr-o conduct˘ aˆ ın linie dreapt˘ a. Fiecare fˆ antˆ an˘ a alimenteaz˘ a o singur˘ a gr˘ adin˘ a. Consiliul Judet ¸ean Galat ¸i pl˘ ate¸ ste investit ¸ia cu condit ¸ia ca lungimea total˘ a a conductelor s˘ a fie minim˘ a. Fiecare unitate de conduct˘ a cost˘ a 100 lei noi (RON). Cerint ¸˘ a S˘ a se determine m, costul minim total al conductelor ce leag˘ a fiecare gr˘ adin˘ a cu exact o fˆ antˆ an˘ a. Date de intrare Fi¸ sierul de intrare seceta.in va cont ¸ine: • Pe prima linie se afl˘ a num˘ arul natural n, reprezentˆ and num˘ arul gr˘ adinilor ¸ si al fˆ antˆ anilor. • Pe urm˘ atoarele n linii se afl˘ a perechi de numere ˆ ıntregi Gx Gy , separate printr-un spat ¸iu, reprezentˆ and coordonatele punctelor de alimentare ale gr˘ adinilor. • Pe urm˘ atoarele n linii se afl˘ a perechi de numere ˆ ıntregi Fx Fy , separate printr-un spat ¸iu, reprezentˆ and coordonatele punctelor fˆ antˆ anilor. Date de ie¸ sire Fi¸ sierul de ie¸ sire seceta.out va cont ¸ine: m − un num˘ ar natural reprezentˆ and partea ˆ ıntreag˘ a a costului minim total al conductelor. Restrict ¸ii ¸ si preciz˘ ari • 1 < n < 13 • 0 ≤ Gx, Gy, F x, F y ≤ 200 • Nu exist˘ a trei puncte coliniare, indiferent dac˘ a sunt gr˘ adini sau fˆ antˆ ani • Orice linie din fi¸ sierele de intrare ¸ si ie¸ sire se termin˘ a prin marcajul de sfˆ ar¸ sit de linie. Exemplu seceta.in 3 14 33 47 23 25 31 seceta.out 624 Explicat ¸ie Costul minim este [6.24264 * 100]=624 prin legarea perechilor: Gradini Fantani 14 23 33 31 47 25

Timp maxim de execut ¸ie/test: 1 sec sub Windows ¸ si 0.5 sec sub Linux.

5.9. PROBLEME REZOLVATE Indicat ¸ii de rezolvare *

129

Solut ¸ia oficial˘ a, lect. Ovidiu Dom¸ sa Num˘ arul mic al punctelor permite generarea tuturor posibilit˘ a¸ tilor de a conecta o gr˘ adin˘ a cu o fˆ antˆ an˘ a neconectat˘ a la un moment dat. Pentru fiecare astfel de combinat ¸ie g˘ asit˘ a se calculeaz˘ a suma distant ¸elor (Gi, F j ), ˆ ın linie dreapta, folosind formula distant ¸ei dintre dou˘ a puncte ˆ ın plan, studiat˘ a la geometrie. (d(A(x, y ), B (z, t) = (x − z )2 + (y − t)2 ). Acest˘ a solut ¸ie implementat˘ a corect asigur˘ a 60 − 70 de puncte. Pentru a obt ¸ine punctajul maxim se tine cont de urm˘ atoarele aspecte: 1. Se construie¸ ste ˆ ın prealabil matricea distant ¸elor d(i, j ) cu semnificat ¸ia distant ¸ei dintre gr˘ adina i ¸ si fˆ antˆ ana j . Aceasta va reduce timpul de calcul la variantele cu peste 9 perechi. 2. Pentru a elimina cazuri care nu pot constitui solut ¸ii optime se folose¸ ste proprietatea patrulaterului c˘ a suma a doua laturi opuse (condit ¸ie care asigur˘ a unicitatea conect˘ arii unei singure fˆ antˆ ani la o singur˘ a gr˘ adin˘ a) este mai mic˘ a decˆ at suma diagonalelor. De aceea nu se vor lua ˆ ın considerare acele segmente care se intersecteaz˘ a. Condit ¸ia de intersect ¸ie a dou˘ a segmente care au capetele ˆ ın punctele de coordonate A(a1, a2), B (b1, b2), C (c1, c2), D(d1, d2) este ca luˆ and segmentul AB , punctele C ¸ si D s˘ a se afle de aceea¸ si parte a segmentului AB ¸ si respectiv pentru segmentul CD, punctele A ¸ si B s˘ a se afle de aceea¸ si parte (se ˆ ınlocuie¸ ste ˆ ın ecuat ¸ia dreptei ce trece prin dou˘ a puncte, studiat˘ aˆ ın clasa a 9-a). Observat ¸ie: Pentru cei interesat ¸i, problema are solut ¸ie ¸ si la un nivel superior, folosind algoritmul de determinare a unui flux maxim de cost minim. Variant˘ a cu determinarea intesect ¸iei segmentelor. import java.io.*; // cu determinarea intesectiei segmentelor class Seceta1 // Java este "mai incet" decat Pascal si C/C++ { // test 9 ==> 2.23 sec static int nv=0; static int n; static int[] xg, yg, xf, yf, t, c; static int[] a; // permutare: a[i]=fantana asociata gradinii i static double costMin=200*1.42*12*100; static double[][] d; static PrintWriter out; static StreamTokenizer st; public static void main(String[] args) throws IOException { long t1,t2; t1=System.currentTimeMillis(); citire();

130

˘ CAPITOLUL 5. GEOMETRIE COMPUTAT ¸ IONALA rezolvare(); afisare(); t2=System.currentTimeMillis(); System.out.println("Timp = "+(t2-t1)+" ms");

} static void citire() throws IOException { int k; st=new StreamTokenizer(new BufferedReader(new FileReader("seceta.in"))); st.nextToken(); n=(int)st.nval; xg=new int[n+1]; yg=new int[n+1]; xf=new int[n+1]; yf=new int[n+1]; a=new int[n+1]; d=new double[n+1][n+1]; for(k=1;k<=n;k++) { st.nextToken(); st.nextToken(); } for(k=1;k<=n;k++) { st.nextToken(); st.nextToken(); } } static void rezolvare() throws IOException { int i,j; int s; for(i=1;i<=n;i++) // gradina i for(j=1;j<=n;j++) // fantana j { s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]); d[i][j]=Math.sqrt(s); } f(1); // generez permutari }

xg[k]=(int)st.nval; yg[k]=(int)st.nval;

xf[k]=(int)st.nval; yf[k]=(int)st.nval;

5.9. PROBLEME REZOLVATE

131

static void f(int k) { boolean ok; int i,j; for(i=1;i<=n;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; for(j=1;j<k;j++) if(seIntersecteaza(xg[k],yg[k],xf[i], yf[i], xg[j],yg[j],xf[a[j]],yf[a[j]])) { ok=false; break; } if(!ok) continue; a[k]=i; if(k<n) f(k+1); else verificCostul(); } } static void verificCostul() { int i; double s=0; for(i=1;i<=n;i++) s=s+d[i][a[i]]; if(s<costMin) costMin=s; } // de ce parte a dreptei [(xa,ya);(xb,yb)] se afla (xp,yp) static int s(int xp,int yp,int xa,int ya,int xb,int yb) { double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya; if(s<-0.001) return -1; // in zona "negativa" else if(s>0.001) return 1; // in zona "pozitiva" else return 0; // pe dreapta suport } // testeaza daca segmentul[P1,P1] se intersecteaza cu [P3,P4] static boolean seIntersecteaza(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { double x,y;

x4)) return true. if(x2!=x1) y=y1+(y2-y1)*(x-x1)/(x2-x1). if(intre(x.// nu au aceeasi dreapta suport else // nu au aceeasi panta (macar unul este oblic) { x=(double)((x4-x3)*(x2-x1)*(y3-y1)x3*(y4-y3)*(x2-x1)+ x1*(y2-y1)*(x4-x3))/ ((y2-y1)*(x4-x3)-(y4-y3)*(x2-x1)).} if((a<=c)&&(c<=b)) return true. if(a>b) {aux=a.x4)&&intre(y.x3.132 ˘ CAPITOLUL 5. a=b. if(a>b) {aux=a.y4)||intre(y2.x3. else return false. else return false.y2)&&intre(x. else if(intre(y1. if((y2-y1)*(x4-x3)==(y4-y3)*(x2-x1)) // au aceeasi panta (oblica) if((x2-x1)*(y3-y1)==(y2-y1)*(x3-x1)) // au aceeasi dreapta suport if(intre(x1. GEOMETRIE COMPUTAT ¸ IONALA if((x1==x2)&&(x3==x4)) // ambele segmente verticale if(x1!=x3) return false. b=aux.y1.b] ? { int aux. } } static boolean intre(int c. else return false.x3.x4)||intre(x2. else if(intre(x1.x3.y4)) return true. if((y1==y2)&&(y3==y4)) // ambele segmente orizontale if(y1!=y3) return false. else return false. } static boolean intre(double c. } static void afisare() throws IOException { int k.b] ? { int aux.x4)||intre(x2. . else return false. else return false. else return false.} if((a<=c)&&(c<=b)) return true.x4)) return true.y3.x1. int b) // c este in [a.y3.y4)) return true.x2)&&intre(y. int a. int b) // c este in [a.x3.y3. else y=y3+(y4-y3)*(x-x3)/(x4-x3). b=aux. int a. a=b.

currentTimeMillis(). xf. } } Variant˘ a cu cu determinarea pozitiei punctelor in semiplane ¸ si mesaje pentru depanare.currentTimeMillis(). out.nval. PROBLEME REZOLVATE 133 out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.close().42*12*100. static int n.in"))). out. static int[] a. static PrintWriter out. st. citire(). d=new double[n+1][n+1].*.k++) . a=new int[n+1].println((int)(costMin*100)). // permutare: a[i]=fantana asociata gradinii i static double costMin=200*1. yf. static StreamTokenizer st. rezolvare(). static double[][] d.5. yg. t1=System. n=(int)st. afisare().9. static int[] xg. st=new StreamTokenizer(new BufferedReader(new FileReader("seceta.out.io. c.k<=n. public static void main(String[] args) throws IOException { long t1. } static void citire() throws IOException { int k. System. for(k=1. xf=new int[n+1]. t2=System.t2.nextToken(). xg=new int[n+1]. yg=new int[n+1]. yf=new int[n+1]. t.out"))). // cu determinarea pozitiei punctelor in semiplane class Seceta2 // cu mesaje pentru depanare ! { static int nv=0.println("Timp = "+(t2-t1)+" ms"). import java.

k<=n.yf[i])<0)) { afisv(k-1).xf[i]. // generez permutari } static void f(int k) { boolean ok. st.yg[k]. xf[k]=(int)st. xg[k]=(int)st.j. st. GEOMETRIE COMPUTAT ¸ IONALA st.j<k.i++) { ok=true.yg[j].yg[k]. break.yf[a[j]].out.// pe pozitia k(gradina) vreau sa pun i(fantana) System.nval.nval.j++) // fantana j { s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]). for(i=1.out.out.. yg[k]=(int)st. for nu se executa ! for(j=1.print(k+""+i+" "+j+""+a[j]).nextToken().xg[k].i<=n. } for(k=1. .nextToken().print(i+" ").xg[j]. } } static void rezolvare() throws IOException { int i.// pe pozitia j(gradina) e pus a[j](fantana) System.134 { ˘ CAPITOLUL 5.i++) // gradina i for(j=1.j. // k=1 ==> nu am in stanga .xg[j].sqrt(s).print(" ("+xg[k]+".j<=n.xf[i]. for(i=1.j<k.nval.k++) { st.xf[a[j]].yf[a[j]])* s(xf[i].j++) if((s(xg[k].yg[k].yg[j]. xg[k]."+yf[i]+") ").nval. d[i][j]=Math."+yg[k]+") "+" ("+xf[i]+".nextToken().yf[i].nextToken(). yg[j].j++) if(i==a[j]) {ok=false. int s.yf[i])* s(xf[a[j]]. System.yf[a[j]])<0)&& (s(xg[j]. yf[k]=(int)st.i<=n.. for(j=1.} if(!ok) continue.xf[a[j]]. int i. } f(1).

// in zona "negativa" else if(s>0.out.ya). static void afisare() throws IOException { int k. for(i=1. } // de ce parte a dreptei [(xa.out. afisv(n). // pe dreapta suport } ("+xg[j]+".yb)] se afla (xp."+yf[a[j]]+") ").i++) System.int xa.print(a[i]). a[k]=i.println(" "+s+" "+costMin+" "+(++nv)). } } static void verificCostul() { int i.int xb.println(" ok=false.out. } static void afisv(int nn) { int i. out. System. out. if(s<costMin) costMin=s. // in zona "pozitiva" else return 0. if(k<n) f(k+1).int yp.5.close().9.yp) static int s(int xp.(xb.out"))). PROBLEME REZOLVATE System. double s=0.001) return -1.i<=nn. } } .i++) s=s+d[i][a[i]].i<=n. break. else verificCostul().println((int)(costMin*100)). if(s<-0."+yg[j]+") "+" 135 ("+xf[a[j]]+".int yb) { double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya. for(i=1.001) return 1. } if(!ok) continue. out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.int ya.

out.nextToken(). c.currentTimeMillis(). yg=new int[n+1].k<=n. yg. } for(k=1.currentTimeMillis(). citire(). static int[] xg.136 ˘ CAPITOLUL 5. System. xg=new int[n+1]. yf. public static void main(String[] args) throws IOException { long t1.nval. st=new StreamTokenizer(new BufferedReader( new FileReader("seceta. // permutare: a[i]=fantana asociata gradinii i static double costMin=200*1.k++) { st.nextToken(). static double[][] d. xf=new int[n+1].k++) . t. d=new double[n+1][n+1].nextToken().io. st.nval. xf. st.nval. yg[k]=(int)st. GEOMETRIE COMPUTAT ¸ IONALA Variant˘ a cu cu determinarea pozitiei punctelor in semiplane.t2.42*12*100. n=(int)st. import java.k<=n. static StreamTokenizer st.println("Timp = "+(t2-t1)+" ms"). afisare().18 sec static int n. static int[] a. // cu determinarea pozitiei punctelor in semiplane class Seceta3 // Java este "mai incet" decat Pascal si C/C++ { // test 9 ==> 2. a=new int[n+1]. rezolvare(). } static void citire() throws IOException { int k. t2=System.*.in"))). for(k=1. yf=new int[n+1]. static PrintWriter out. f˘ ar˘ a mesaje pentru depanare. t1=System. xg[k]=(int)st.

nextToken().i<=n.j++) if(i==a[j]) {ok=false.. } } static void rezolvare() throws IOException { int i. xf[k]=(int)st.i<=n.j<k. for(i=1.j<=n.} if(!ok) continue. xg[j].9. yf[i])* s(xf[a[j]].xf[a[j]]. for(j=1. yg[j].yg[j]. } } static void verificCostul() .xg[k]. yf[i]. int i. xg[k]. } if(!ok) continue.xf[i].5.i++) // gradina i for(j=1. break. for(i=1.yf[a[j]]. // k=1 ==> nu am in stanga .nval. d[i][j]=Math.j. break.yg[k]. xg[j]. a[k]=i. else verificCostul().yf[a[j]])* s(xf[i].j++) if((s(xg[k].sqrt(s). st.yg[j]. yg[k].nextToken(). } f(1).j.nval. if(k<n) f(k+1). PROBLEME REZOLVATE { st..xf[i].yf[a[j]])<0)&& (s(xg[j].j++) // fantana j { s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]). yf[i])<0)) { ok=false.xf[a[j]]. yf[k]=(int)st. int s. for nu se executa ! for(j=1.i++) { ok=true.yg[k]. // generez permutari } 137 static void f(int k) { boolean ok.j<k.

i<=n. static StreamTokenizer st. static double[][] d. } //de ce parte a dreptei [(xa.i++) s=s+d[i][a[i]].currentTimeMillis().. static int[] a.18 sec --> 0. c.int ya.yb)] se afla (xp. yg. if(s<costMin) costMin=s. static int[] xg.int xa. for(i=1.*.int yb) { double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya.001) return -1. public static void main(String[] args) throws IOException { long t1. GEOMETRIE COMPUTAT ¸ IONALA int i. . // in zona "negativa" else if(s>0. double s=0.001) return 1. } } Varianta 4: import java. // gresit (!) dar .. !!! class Seceta4 // test 9 : 2.(xb.04 sec { static int n. out=new PrintWriter(new BufferedWriter(new FileWriter("seceta. yf. xf. static boolean[] epus=new boolean[13].close().. t.println((int)(costMin*100)).138 { ˘ CAPITOLUL 5.42*12*100. if(s<-0. out.int yp.t2.. // permutare: a[i]=fantana asociata gradinii i static double costMin=200*1.yp) static int s(int xp. t1=System.ya).int xb. obtine 100p . static PrintWriter out. out.io.out"))). // in zona "pozitiva" else return 0. // pe dreapta suport } static void afisare() throws IOException { int k.

yg=new int[n+1]. xf[k]=(int)st.j<=n. } }// citire(. st=new StreamTokenizer(new BufferedReader(new FileReader("seceta.. xf=new int[n+1]. } f(1).j++) // fantana j { s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]). a=new int[n+1]. PROBLEME REZOLVATE citire().nextToken().5. yg[k]=(int)st. st. afisare(). rezolvare().i++) // gradina i for(j=1.j. st. }// main(.9.k++) { st. for(i=1. xg=new int[n+1]. } for(k=1.nval. yf[k]=(int)st.in"))).. n=(int)st.println("Timp = "+(t2-t1)+" ms").i<=n..nval. int s.k<=n. System.sqrt(s).k<=n. // generez permutari . yf=new int[n+1].nval.out.) 139 static void citire() throws IOException { int k.nextToken(). d=new double[n+1][n+1].k++) { st.nextToken()..nextToken(). static void rezolvare() throws IOException { int i. t2=System. st.nval.) xg[k]=(int)st.currentTimeMillis(). for(k=1. d[i][j]=Math.nextToken().nval.

i++) { if(epus[i]) continue.) }// class . if(k<n) f(k+1).) ˘ CAPITOLUL 5. GEOMETRIE COMPUTAT ¸ IONALA static void f(int k) { int i. } if(seIntersecteaza) continue. else verificCostul()...j. seIntersecteaza=false. }// afisare(. out. for(i=1. double s=0.out"))).i<=n.i<=n. out=new PrintWriter(new BufferedWriter(new FileWriter("seceta. out.j<=k-1.. break. boolean seIntersecteaza. for(i=1.println((int)(costMin*100))...i++) s=s+d[i][a[i]].. a[k]=i. if(s<costMin) costMin=s. }// for i }// f(. for(j=1. }// verificCostul(.) static void verificCostul() { int i.140 }// rezolvare(. epus[i]=false..) static void afisare() throws IOException { int k..close().j++) if(d[k][i]+d[j][a[j]]>d[j][i]+d[k][a[j]]) { seIntersecteaza=true. epus[i]=true.

va trebui s˘ a instaleze o anten˘ a de emisie special˘ a pentru aceasta.01. precum ¸ si puterea minim˘ a a acesteia. Mai exact. se verific˘ a dac˘ a diferent ¸a dintre solut ¸ia afi¸ sat˘ a¸ si cea corect˘ a (ˆ ın valoare absolut˘ a) este < 0. pe linia i + 1 se afl˘ a dou˘ a numere ˆ ıntregi separate printr-un spat ¸iu x y .5. Cerint ¸˘ a Scriet ¸i un program care s˘ a determine o pozit ¸ie optim˘ a de amplasare a antenei. Raza zonei este denumit˘ a puterea antenei. Pe cea de a doua linie se va scrie un num˘ ar real reprezentˆ and puterea antenei. y < 15001 • Numerele reale din fi¸ sierul de ie¸ sire trebuie scrise cu trei zecimale cu rotunjire. iar puterea antenei s˘ a fie minim˘ a. Datele de ie¸ sire Fi¸ sierul de ie¸ sire antena.9. prin urmare. reprezentˆ and num˘ arul de case din zon˘ a. ce reprezint˘ a abscisa ¸ si respectiv ordonata casei i. astfel ˆ ıncˆ at fiecare cas˘ a s˘ a se afle ˆ ın interiorul sau pe frontiera zonei circulare ˆ ın care emite antena. Osman Ay.9. Postul de radio al ONI 2005 dore¸ ste s˘ a emit˘ a pentru tot ¸i locuitorii din zon˘ a ¸ si.out cont ¸ine pe prima linie dou˘ a numere reale separate printr-un spat ¸iu x y reprezentnd abscisa ¸ si ordonata pozit ¸iei optime de amplasare a antenei. Cu cˆ at puterea antenei este mai mare. ˆ In aceast˘ a zon˘ a exist˘ a doar n case. Prin urmare trebuie selectat˘ a o pozit ¸ie optim˘ a de amplasare a antenei. cu atˆ at antena este mai scump˘ a. Restrict ¸ii ¸ si preciz˘ ari • 2 < N < 15001 • −15000 < x.2 Antena . Nu exist˘ a dou˘ a case ˆ ın aceea¸ si locat ¸ie.in cont ¸ine pe prima linie un num˘ ar natural n.ONI2005 clasa a X-a prof. Exemplu . rupt˘ a de bucuriile ¸ si necazurile civilizat ¸iei moderne. Datele de intrare Fi¸ sierul de intrare antena. Liceul International de Informatic˘ a Bucure¸ sti ˆ In Delta Dun˘ arii exist˘ a o zon˘ a s˘ albatic˘ a. • La evaluare. Pe urm˘ atoarele n linii se afl˘ a pozit ¸iile caselor. O anten˘ a emite unde radio ˆ ıntr-o zon˘ a circular˘ a. PROBLEME REZOLVATE 141 5. pozit ¸iile acestora fiind specificate prin coordonatele carteziene de pe hart˘ a. Centrul zonei coincide cu punctul ˆ ın care este pozit ¸ionat˘ a antena.

250 2. t1=System.in"))). r0.250.1 secunde pentru Linux. // practic.out"))).142 antena. GEOMETRIE COMPUTAT ¸ IONALA antena.825) iar puterea antenei este 3.875 3. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("antena. y0. static int[] x. trebuie sa determinam cele trei puncte class Antena // prin care trece cercul care le acopera pe toate!!! { static int n. long t1.366 Timp maxim de execut ¸ie/test: 0. 2. import java. static double x0.in 7 50 26 45 22 02 36 52 ˘ CAPITOLUL 5.366 Explicat ¸ie Antena va fi plasat˘ aˆ ın punctul de coordonate (3.y.nanoTime().3 secunde pentru Windows ¸ si 0.io.t2.*.out 3. . public static void main(String[] args) throws IOException { int k. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("antena.

j-1). for(k=1.nextToken().print( (double)((int)((x0+0.2.2..yy) for(j=2.j-1 }// cercPrin(. out.5.yy) si acopera punctele 1.y[2]).9.. // trece prin P1 si (xx.j<=k.) // trece prin (xx. t2=System. // scriere cu 3 zecimale rotunjite out.k++) if(!esteInCerc(k)) cercPrin(x[k]. for(k=3.j static void cercPrin(int xx..0005)*1000))/1000+" ").x[2].nextToken()...int j) { int i.close(). System. out. // .println("Timp = "+((double)(t2-t1))/1000000000).2. x=new int[n+1].yyy) si acopera 1.nval.k-1 } else cercCircumscris(x[1].0005)*1000))/1000).. y[k]=(int)st.x[3]. PROBLEME REZOLVATE st. int k) { int j..yy)...println((double)((int)((y0+0.. n=(int)st. int yy.y[2]. x[k]=(int)st.y[1].println((double)((int)((r0+0.int yy..nval. out.. }// main(..j++) if(!esteInCerc(j)) cercPrin(xx.y[k]..nextToken().nanoTime()..y[j]. cercDeDiametru(x[1]..2.y[1]..x[2]. . y=new int[n+1].int xxx.int yyy.yy.) // trece prin (xx.out. // trece prin Pk si acopera 1. st. } 143 if(n>3) { puncteExtreme(). cercDeDiametru(x[1]..xx.k<=n.nval.y[1].x[j].y[3]). acopera 1.k-1).k static void cercPrin(int xx.k<=n.k++) { st.3..yy) si (xxx...0005)*1000))/1000)..

.y[2])<d(x[3].0001) return true. // caut cel mai de sus (si mai la stanga) punct si-l pun pe pozitia 4 // (caut incepand cu pozitia 4) kmax=4.min.aux.k++) if((y[k]>max)||(y[k]==max)&&(x[k]<x[kmax])) {max=y[k]. if(d(x[1]...kmax). min=y[3].y[k].kmin.y[1].yyy).y[i]). kmin=k.y0)<r0+0. // caut cel mai din dreapta (si mai sus) punct si-l pun pe pozitia 2 // (caut incepand cu pozitia 2) kmax=2.} if(kmax!=4) swap(4.k<=n.144 ˘ CAPITOLUL 5.yy.. for(k=2. max=x[2].xxx.yyy.kmax).x0. kmin=k.k<=n.kmin). for(k=4.x[i]. // caut cel mai din stanga punct (si mai jos) si-l pun pe pozitia 1 // (caut incepand cu pozitia 1) kmin=1.k++) if((x[k]<min)||(x[k]==min)&&(y[k]<y[kmin])) {min=x[k].k++) if((x[k]>max)||(x[k]==max)&&(y[k]>y[kmax])) {max=x[k]. kmax=k. else return false.i++) // acopera 1.} if(kmin!=1) swap(1.k<=n. for(k=5. } static void puncteExtreme() { int k..} if(kmin!=3) swap(3. for(i=1.} if(kmax!=2) swap(2. kmax=k.2. }// cercPrin(.kmin). GEOMETRIE COMPUTAT ¸ IONALA cercDeDiametru(xx.) static boolean esteInCerc(int k) { if(d(x[k].k++) if((y[k]<min)||(y[k]==min)&&(x[k]>x[kmin])) {min=y[k]..x[4]. max=y[4].j if(!esteInCerc(i)) cercCircumscris(xx.y[4])) // puncte mai departate .i<=j. // caut cel mai de jos (si mai la dreapta) punct si-l pun pe pozitia 3 // (caut incepand cu pozitia 3) kmin=3.kmax.x[2].xxx.y[3]. min=x[1].max. for(k=3.k<=n.yy.

int x2. b13=2*(y1-y3).x2.int x2. } }// puncteExtreme() 145 static void cercCircumscris(int x1. y0=((double)y1+y2)/2. y0.x2.int x3.y2)/2.x2. else return b.b). r0=Math.c)).int y2.(miny. PROBLEME REZOLVATE { swap(1.maxx).int y2) { x0=((double)x1+x2)/2.x3))/2.int y1.P2.int b. a13=2*(x1-x3).y3)+min(y1.int b. } static int max(int a.min(a.c)). y0=(a12*c13-a13*c12)/(a12*b13-a13*b12). a13. y0=(max(y1. r0=d(x0.y3))/2.y2. swap(2. b13. r double a12.int y1. b12=2*(y1-y2).. // sistemul devine: a12*x0+b12*y0=c12. } static int min(int a.int b) { if(a>b) return a.y0. c12=x1*x1+y1*y1-x2*x2-y2*y2.P3 // 3 ecuatii si 3 necunoscute: x0..max(a. c12.x3)+min(x1.5.y2. } else // consider cercul de diametru [(minx. } .. } }// cercCircumscris(.b).4).int b) { if(a<b) return a.maxy)] { // punctele sunt coliniare ! x0=(max(x1.int c) { return max(min(a.y1)/2. else return b.x1.y1. b12.9. if(a12*b13-a13*b12!=0) { x0=(c12*b13-c13*b12)/(a12*b13-a13*b12).. } static int max(int a. c13=x1*x1+y1*y1-x3*x3-y3*y3.3).) static void cercDeDiametru(int x1. c13.sqrt((x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)).) static int min(int a. // int ==> eroare !!! a12=2*(x1-x2).int c) { return min(min(a. // a13*x0+b13*y0=c13. }// cercDeDiametru(.int y3) { // consider ca punctele nu sunt coliniare ! // (x-x0)^2+(y-y0)^2=r^2 ecuatia cercului verificata de punctele P1. r0=d(x1.

y[j]=aux.dy. Cunoscnd pozit ¸iile init ¸iale ale parilor ¸ si valoarea ˆ ınscris˘ a pe fiecare par. double y1. double x2. Date de intrare .sqrt(dx*dx+dy*dy). se cere suprafat ¸a maxim˘ a cu care poate s˘ a-¸ si extind˘ a P˘ acal˘ a proprietatea. int x2... fiind cam ¸ suubred. dx=x2-x1. unul cˆ ate unul. y[i]=y[j]. }// swap(.3 Mo¸ sia lui P˘ acal˘ a . un petec de teren de pe mo¸ sia boierului. dy=y2-y1. int y1. dar nu pe o distant ¸˘ a mai mare decˆ at o valoare dat˘ a (scris˘ a pe fiecare par) ¸ si fiecare segment de gard.9. } static double d(double x1. cel˘ alalt r˘ amˆ anˆ and nemi¸ scat.dy. Se ¸ stie c˘ a parii sunt dat ¸i ˆ ıntr-o ordine oarecare. a¸ sa cum era ˆ ınvoiala. cum i-o fi voia. GEOMETRIE COMPUTAT ¸ IONALA static double d(int x1. distant ¸ele pe care fiecare par poate fi deplasat sunt numere naturale strict pozitive ¸ si figura format˘ a de terenul init ¸ial este un poligon neconcav. dx=x2-x1. } //interschimb punctele i si j static void swap(int i. pozit ¸iile lor init ¸iale sunt date prin numere ˆ ıntregi de cel mult 3 cifre. Terenul este ˆ ımprejmuit complet cu segmente drepte de gard ce se sprijin˘ a la ambele capete de cˆ ate un par zdrav˘ an. poate fi rotit ¸ si prelungit de la un singur cap˘ at. P˘ acal˘ a iese iar ˆ ın cˆ a¸ stig ¸ si prime¸ ste dreptul s˘ a str˘ amute ni¸ ste pari. Dar ˆ ınvoiala prevede c˘ a fiecare par poate fi mutat ˆ ın orice direct ¸ie. return Math. aux=x[i]. int y2) { double dx. astfel ˆ ıncˆ at s˘ a-¸ si extind˘ a suprafat ¸a de teren. x[j]=aux. double y2) { double dx. dy=y2-y1. aux=y[i]. x[i]=x[j]. La o nou˘ a prinsoare.) }// class 5. return Math.sqrt(dx*dx+dy*dy). int j) { int aux.146 ˘ CAPITOLUL 5.OJI2004 clasa a XI-a P˘ acal˘ a a primit.

coordonatele init ¸iale ¸ si distant ¸a pe care poate fi mutat parul 1 x2 y2 d2 .0000 Explicat ¸ie: prin mutarea parilor 1 ¸ si 2 cu cˆ ate 2 ¸ si respectiv 3 unit˘ a¸ ti.5.coordonatele init ¸iale ¸ si distant ¸a pe care poate fi mutat parul n Date de ie¸ sire ˆ In fi¸ sierul MOSIA. pentru care se cunosc coordonatele vˆ arfurilor lor.OUT se scrie un num˘ ar real cu 4 zecimale ce reprezint˘ a suprafat ¸a maxim˘ a cu care se poate m˘ ari mo¸ sia.ONI2006 baraj Ionic˘ a a primit de ziua lui de la tat˘ al s˘ au un joc format din piese de form˘ a triunghiular˘ a de dimensiuni diferite ¸ si o suprafat ¸u a magnetic˘ a pe care acestea pot fi a¸ sezate. PROBLEME REZOLVATE 147 Fi¸ sierul MOSIA.IN cont ¸ine n + 1 linii cu urm˘ atoarele valori: n . Timp limit˘ a de executare: 1 sec.9. respectiv semiaxa [Oy pe cateta de lungime b. Tat˘ al lui Ionic˘ a vrea s˘ a verifice dac˘ a pe tabl˘ a piesele realizeaz˘ a o partit ¸ie a triunghiului dreptunghic desenat. La un moment dat Ionic˘ a a¸ seaz˘ a pe tabla magnetic˘ a n piese.. . respectiv b ¸ si un sistem de coordonate xOy cu originea ˆ ın unghiul drept al triunghiului. Restrict ¸ii ¸ si observat ¸ii: 3 < N ≤ 200 num˘ ar natural −1000 < xi . xn yn dn . Pe suprafat ¸a magnetic˘ a este desenat un triunghi dreptunghic cu lungimile catetelor a. semiaxa [Ox pe cateta de lungime a./test 5.9.coordonatele init ¸iale ¸ si distant ¸a pe care poate fi mutat parul 2 . adic˘ a dac˘ a sunt ˆ ındeplinite condit ¸iile: • nu exist˘ a piese suprapuse..4 Partit ¸ie .num˘ arul de pari x1 y1 d1 . se obt ¸ine un teren avˆ and suprafat ¸a cu 30 de unit˘ a¸ ti mai mare decˆ at terenul init ¸ial. yi < 1000 numere ˆ ıntregi 0 < di ≤ 20 numere ˆ ıntregi poligonul neconcav se define¸ ste ca un poligon convex cu unele vˆ arfuri coliniare pozit ¸iile parilor sunt date ˆ ıntr-o ordine oarecare poligonul obt ¸inut dup˘ a mutarea parilor poate fi concav pozit ¸iile finale ale parilor nu sunt ˆ ın mod obligatoriu numere naturale Exemplu Pentru fi¸ sierul de intrare 4 -3 0 2 3 0 3 0 6 2 0 -6 6 se va scrie ˆ ın fi¸ sierul de ie¸ sire valoarea 30.

. 31000]. Urmeaz˘ a k grupe de linii. reprezentˆ and num˘ arul de seturi de date din fi¸ sier.out se vor scrie k linii. Cerint ¸˘ a Se cere s˘ a se verifice dac˘ a piesele plasate pe tabla magnetic˘ a formeaz˘ a o partit ¸ie a triunghiului desenat pe tabla magnetic˘ a. static final int INSIDE=1.3 secunde/test Prelucrare ˆ ın Java dup˘ a rezolvarea ˆ ın C a autorului problemei import java. 2. Date de ie¸ sire ˆ In fi¸ sierul part. Restrict ¸ii ¸ si preciz˘ ari • 1 ≤ n ≤ 150 • 1 ≤ k ≤ 10 • a. cˆ ate o grup˘ a pentru fiecare set de date. b sunt numere ˆ ıntregi din intervalul [0. b. class part { static final int ON_EDGE=0.. Date de intrare Fi¸ sierul de intrare part. Exemplu part. Grupa de linii corespunz˘ atoare unui set este format˘ a dintr-o linie cu numerele a. n separate ˆ ıntre ele prin cˆ ate un spat ¸iu ¸ si n linii cu cˆ ate ¸ sase numere ˆ ıntregi separate prin spat ¸ii reprezentˆ and coordonatele vˆ arfurilor (abscis˘ a ordonat˘ a) celor n piese.io. GEOMETRIE COMPUTAT ¸ IONALA • piesele acoper˘ a toat˘ a port ¸iunea desenat˘ a (ˆ ın form˘ a de triunghi dreptunghic).. Pe linia i (i = 1. cˆ ate o pies˘ a pe o linie. cˆ ate o linie pentru fiecare set de date. 31000] • Coordonatele vrfurilor pieselor sunt numere ntregi din intervalul [0. .*.in 2 20 10 4 0 5 0 10 10 5 0 0 10 5 0 5 0 0 10 0 10 5 10 0 20 0 10 5 20 10 2 0 0 0 10 10 5 0 0 20 0 20 10 part. k) se va scrie 1 dac˘ a triunghiurile din setul de date i formeaz˘ a o partit ¸ie a triunghiului desenat pe tabla magnetic˘ a sau 0 ˆ ın caz contrar.148 ˘ CAPITOLUL 5.out 1 0 Timp maxim de execut ¸ie: 0.. • nu exist˘ a port ¸iuni din piese ˆ ın afara triunghiului desenat.in cont ¸ine pe prima linie un num˘ ar natural k .

B.9. static final int N_MAX=512. int _x.y). int y2.Y[n][(i+1)%3].Y[n][i].i<3. } static int point_sign (int x1. static int[][] X=new int[N_MAX][3]. return INSIDE. int y1. A. int x. PROBLEME REZOLVATE y 10 T1 5 T2 T3 a) 10 T4 20 x b) 10 5 T1 T2 20 x 10 y 149 Figura 5. int[] sgn=new int[3]. a=y2-y1. static int sgn(int x) { return x>0 ? 1 : (x<0 ? -1 : 0). for(i=0.x. return sgn(a*_x+b*_y+c).5. static int[][] Y=new int[N_MAX][3].2: a) pentru setul 1 de date ¸ si b) pentru setul 2 de date static final int OUTSIDE=2. static int N. c=y1*x2-x1*y2. c. b=x1-x2. int y) { int i. } static int point_inside (int n.X[n][(i+1)%3]. if(sgn[0]==0 || sgn[1]==0 || sgn[2]==0) return ON_EDGE. int _y) { int a. if(sgn[0]*sgn[1]<0 || sgn[0]*sgn[2]<0 || sgn[1]*sgn[2]<0) return OUTSIDE.i++) sgn[i]=point_sign(X[n][i]. } . b. int x2.

area=0. return sgn(a1*x3+b1*y3+c1)*sgn(a1*x4+b1*y4+c1)<0 && sgn(a2*x1+b2*y1+c2)*sgn(a2*x2+b2*y2+c2)<0.X[n1][(i+1)%3]. } if(area!=A*B) return 0.int y2. X[n2][j].Y[n1][i]. for(i=0.Y[n2][i]))==ON_EDGE) t2++.int y1.t2=0.X[i][j]. for(i=0.abs((X[i][1]*Y[i][2]-X[i][2]*Y[i][1])(X[i][0]*Y[i][2]-X[i][2]*Y[i][0])+ (X[i][0]*Y[i][1]-X[i][1]*Y[i][0])).j++) if(point_inside(N. a2=y4-y3. c1=y1*x2-x1*y2.b2.j.150 ˘ CAPITOLUL 5.c2. for(i=0.int x2. c2=y3*x4-x3*y4.j. b1=x1-x2. } static int solve() { int i. int n2) { int i. } return false. .i++) for(j=0. } if(t1==3 || t2==3) return true. if(x==INSIDE) return true.i++) { for(j=0.int y4) { int a1. area+=Math.X[n2][i].j<3. GEOMETRIE COMPUTAT ¸ IONALA static boolean segment_intersect(int x1.Y[n1][i]))==ON_EDGE) t1++.Y[n1][(i+1)%3]. a1=y2-y1.Y[i][j])==OUTSIDE) return 0. int x3.Y[n2][j].X[n1][i].int x4.t1=0. if(x==INSIDE) return true. if((x=point_inside(n1.x.b1.i<3.a2. b2=x3-x4.X[n2][(j+1)%3].int y3.c1.i<N.i++) { if((x=point_inside(n2.j++) if(segment_intersect( X[n1][i]. } static boolean triangle_intersect (int n1.j<3.Y[n2][(j+1)%3] )) { return true.i<3.

out.j++) if(triangle_intersect(i..nextToken().nval.j++) { st. X[N][2]=0. Y[N][1]=0. i. } out.. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("part. st. PROBLEME REZOLVATE 151 for(i=0.j<N. for(i=0.println(solve()). A=(int)st.j<3. Y[N][0]=0.nval.nextToken(). B=(int)st.out"))).nextToken(). st. for(. } public static void main(String[] args) throws IOException { int tests.nval.nextToken(). tests-->0. tests=(int)st.j)) return 0.in"))). Y[i][j]=(int)st.nval.i<N.nval. } X[N][0]=0.5.close(). X[N][1]=A.nextToken().) { st.) }// class .nextToken(). N=(int)st.i++) for(j=i+1. j. return 1.i++) for(j=0. }// main(.9. X[i][j]=(int)st.i<N. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("part. st. Y[N][2]=B.nval. st.

GEOMETRIE COMPUTAT ¸ IONALA .152 ˘ CAPITOLUL 5.

.Capitolul 6 Grafuri 6.. Un ciclu este un drum care este simplu. dac˘ aˆ ıntre oricare dou˘ a vˆ arfuri exist˘ a un drum. an ) sau de forma {a1 . Pentru grafuri orientate... an } dup˘ a cum graful este orientat sau neorientat. dac˘ a graful este neorientat.. M ′′ >. Un graf part ¸ial este un graf < V. a3 }. Un drum simplu este un drum ˆ ın care nici un vˆ arf nu se repet˘ a. Num˘ arul vˆ arfurilor adiacente vˆ arfului i se nume¸ ste gradul nodului i ¸ si se noteaz˘ a d[i]. b). dac˘ a graful este orientat. iar M ⊆ V × V este o mult ¸ime de muchii. a3 ). iar M ′ este format˘ a din muchiile din M care unesc vˆ arfuri din V ′ . M ′ >. Un vˆ arf care este extremitatea unei singure muchii se nume¸ ste vˆ arf terminal. {a2 . ¸ si cu multimea {a. a2 }. Dou˘ a vˆ arfuri unite printr-o muchie se numesc adiacente. unde M ′′ ⊂ M . M >. Un graf aciclic este un graf f˘ ar˘ a cicluri. Un subgraf al lui G este un graf < V ′ . Un drum este o succesiune de muchii de forma (a1 . Lungimea drumului este egal˘ a cu num˘ arul muchiilor care ˆ ıl constituie. Un graf neorientat este conex. 153 . unde V ′ ⊂ V .1 Reprezentarea grafurilor Un graf este o pereche G =< V. O muchie de la vˆ arful a la vˆ arful b este notat˘ a cu perechea ordonat˘ a (a. unde V este o mult ¸ime de vˆ arfuri. {an−1 . b}. a2 ). cu except ¸ia primului ¸ si ultimului vˆ arf. dac˘ aˆ ıntre oricare dou˘ a vˆ arfuri i ¸ si j exist˘ a un drum de la i la j ¸ si un drum de la j la i.. care coincid. (a2 . . aceast˘ a not ¸iune este ˆ ınt˘ arit˘ a: un graf orientat este tare conex. (an−1 ..

Pe de alt˘ a parte. trebuie s˘ a analiz˘ am o ˆ ıntreag˘ a linie din matrice. ˆ Intrun graf cu m muchii. M > ˆ ın componentele sale conexe determin˘ a o partit ¸ie a lui V ¸ si una a lui M . Memoria necesar˘ a este ˆ ın ordinul lui n2 . O variant˘ a alternativ˘ a este s˘ a-i d˘ am lui A[i. Aceast˘ a reprezentare este eficient˘ a atunci cˆ and avem de examinat toate muchiile grafului. Exist˘ a doi algoritmi foarte cunoscuti de determinare a unui arbore minim de acoperire al unui graf: algoritmul lui Kruskal. j ] = true dac˘ a vˆ arfurile i ¸ si j sunt adiacente. de complexitate O(m · log2 n) ¸ si algoritmul lui Prim. j ] = f alse ˆ ın caz contrar. dac˘ a graful este orientat. putem verifica u¸ sor dac˘ a dou˘ a vˆ arfuri sunt adiacente. respectiv m. M > un graf neorientat conex. ˆ ın mai putin de n operat ¸ii. GRAFURI ˆ In cazul unui graf neconex. este necesar ca muchia s˘ a plece din i). trebuie s˘ a analiz˘ am lista de adiacent˘ a a lui i (¸ si. j ] = +∞ atunci cˆ and cele dou˘ a vˆ arfuri nu sunt adiacente. suma lungimilor listelor de adiacent˘ a este 2m. Aceast˘ a problem˘ a se mai nume¸ ste ¸ si problema conect˘ arii ora¸ selor cu cost minim. iar suma lungimilor muchiilor din A s˘ a fie cˆ at mai mic˘ a. lista de adiacent˘ a a lui j ). adic˘ a un subgraf conex ˆ ın care nici un vˆ arf din subgraf nu este unit cu unul din afar˘ a printr-o muchie a grafului initial. considerand A[i. A > este un arbore ¸ si este numit arborele part ¸ial de cost minim al grafului G. • Prin liste de adiacent ¸a ˘. Un graf poate avea mai mult ¸i arbori part ¸iali de cost minim. de complexitate O(n2 ) (pentru acest algoritm se pot obtine . pentru a determina dac˘ a dou˘ a vˆ arfuri i ¸ si j sunt adiacente. iar muchiilor li se pot ata¸ sa informat ¸ii numite uneori lungimi sau costuri. • Printr-o list˘ a de muchii. 6. avˆ and numeroase aplicat ¸ii. astfel ˆ ıncˆ at toate vˆ arfurile din V s˘ a r˘ amˆ an˘ a conectate atunci cˆ and sunt folosite doar muchii din A. ceea ce este mai put ¸in eficient decˆ at consultarea unei valori logice ˆ ın matricea de adiacent ¸˘ a.2 Arbore minim de acoperire Fie G =< V. posibil. aceast˘ a reprezentare este preferabil˘ a din punct de vedere al memoriei necesare. Problema este s˘ a g˘ asim o submultime A ⊆ M . Aceasta necesit˘ a n operat ¸ii (unde n este num˘ arul de vˆ arfuri ˆ ın graf). independent de num˘ arul de muchii care conecteaz˘ a vˆ arful respectiv. Pe de alt˘ a parte. ˆ ın care A[i. Graful part ¸ial < V. Fiecare muchie are un cost nenegativ (sau o lungime nenegativ˘ a). Exist˘ a cel put ¸in trei moduri evidente de reprezentare ale unui graf: • Printr-o matrice de adiacent ¸a ˘ A.154 CAPITOLUL 6. O component˘ a conex˘ a este un subgraf conex maximal. dac˘ a dorim s˘ a afl˘ am toate vˆ arfurile adiacente unui vˆ arf dat. Cu aceast˘ a reprezentare. iar A[i. se pune problema determinarii componentelor sale conexe. Vˆ arfurilor unui graf li se pot ata¸ sa informat ¸ii numite uneori it valori. dac˘ a graful este neorientat. Este posibil s˘ a examin˘ am tot ¸i vecinii unui vˆ arf dat. adic˘ a prin ata¸ sarea la fiecare vˆ arf i a listei de vˆ arfuri adiacente lui (pentru grafuri orientate. ˆ Imp˘ art ¸irea unui graf G =< V. Dac˘ a num˘ arul muchiilor ˆ ın graf este mic. ˆ ın medie. j ] valoarea lungimii muchiei dintre vˆ arfurile i ¸ si j . unde V este mult ¸imea vˆ arfurilor ¸ si M este mult ¸imea muchiilor.

imin=0. n=(int)st. static int[][] cost. Ambii algoritmi folosesc tehnica greedy.io. La fiecare pas al algoritmului. Initial. adic˘ a pˆ an˘ a cˆ and U = V .*. costArbore=0. // predecesor in arbore public static void main(String[]args) throws IOException { int nods=3. care se adaug˘ a la arborele precedent.6. se alege o muchie de cost minim.1 Algoritmul lui Prim ˆ In acest algoritm. static int[] p. // nod start int i. k. cu cˆ ate o ramur˘ a. modul ˆ ın care se realizeaz˘ a alegerea muchiilor difer˘ a. dˆ and na¸ stere unui nou arbore part ¸ial de cost minim (deci. se adaug˘ a o muchie ˆ ın A de cost minim ˆ ın A.nval. Mai precis se porne¸ ste cu o mult ¸ime de arbori A (init ¸ial A cont ¸ine doar arbori cu un singur nod).m. Astfel. La fiecare pas. mult ¸imea U a vˆ arfurilor acestui arbore cont ¸ine un singur vˆ arf oarecare din V .out"))). care reprezint˘ a subarbori ai unui arbore minim de acoperire. exact una dintre extremit˘ a¸ tile acestei muchii este un vˆ arf ˆ ın arborele precedent). static boolean[] esteInArbore. la fiecare pas. mult ¸imea A de muchii alese ˆ ımpreun˘ a cu multimea U a vˆ arfurilor pe care le conecteaz˘ a formeaz˘ a un arbore part ¸ial de cost minim pentru subgraful < U. static int n.min. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("prim.2. care va fi r˘ ad˘ acina. A > al lui G. iar mult ¸imea A a muchiilor este vid˘ a. 6.2. // arbore minim de acoperire: algoritmul lui Prim class Prim // O(n^3) { static final int oo=0x7fffffff. st.jmin=0. Arborele partial de cost minim creste natural. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("prim. mult ¸imea A va ret ¸ine chiar un arbore minim de acoperire al grafului dat. dup˘ a introducerea a n − 1 muchii. De¸ si tehnica de abordare este aceea¸ si pentru ambii algoritmi. pˆ an˘ a cˆ and va atinge toate vˆ arfurile din V . j. . Algoritmul lui Prim de ordinul O(n3 ) import java. ARBORE MINIM DE ACOPERIRE 155 si complexit˘ a¸ ti mai bune).in"))).nextToken().

} CAPITOLUL 6. i=(int)st. st. for(i=1. if(min>cost[i][j]) { min=cost[i][j].j<=n. for(i=1. st.k++) if(p[k]!=0) out.k<=n.nval. esteInArbore[nods]=true. j=(int)st.nextToken().close().i<=n. for(k=1.i++) // O(n) { if(!esteInArbore[i]) continue. p[jmin]=imin. for(j=1. } }//for j }//for i esteInArbore[jmin]=true. p=new int[n+1]. m=(int)st.i++) for(k=1.println("cost="+costArbore).nextToken().156 st.j<=n. out.println(k+" "+p[k]).j++) // O(n) { if(esteInArbore[j]) continue.j++) cost[i][j]=oo.nextToken(). }//main }//class /* 6 7 1 2 3 1 3 1 2 3 2 1 2 4 5 3 3 3 4 . }//for k for(k=1. cost=new int[n+1][n+1]. esteInArbore=new boolean [n+1].nextToken().k<=m.k++) { st. GRAFURI for(j=1. jmin=j.k<=n-1. cost[i][j]=cost[j][i]=(int)st.nval. costArbore+=min.nval. out. imin=i.i<=n.nval.k++) // sunt exact n-1 muchii in arbore !!! O(n) { min=oo.

k. static int[][] cost.i++) d[i]=oo. for(i=1.jmin=0. for(i=1.nextToken().min.nextToken(). d=new int[n+1].nextToken().j++) cost[i][j]=oo. // nod start int i. costArbore=0. m=(int)st. static boolean[] esteInArbore. st. for(k=1. // distante de la nod catre arbore public static void main(String[]args) throws IOException { int nodStart=3. ARBORE MINIM DE ACOPERIRE 3 4 4 5 5 6 4 6 */ 1 1 3 2 6 4 cost=7 157 Algoritmul lui Prim cu distant ¸e import java.io.j<=n. n=(int)st. j. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("prim.2. // predecesor in arbore static int[] d.k<=m. st.i<=n.i<=n.out"))).nval.6.k++) { st.in"))).*.nval. p=new int[n+1].m. cost=new int[n+1][n+1]. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("prim. i=(int)st.i++) for(j=1. . esteInArbore=new boolean [n+1]. static int[] p.nval. static int n. // arbore minim de acoperire: algoritmul lui Prim class PrimDist // O(n^2) { static final int oo=0x7fffffff.

j++) // actualizez distantele nodurilor O(n) { if(esteInArbore[j]) continue. jmin=j. j=(int)st. st. // j este deja in arbore if(cost[jmin][j]<oo) // am muchia (jmin.println(k+" "+p[k]).j++) // O(n) { if(esteInArbore[j]) continue. }//main }//class /* 6 7 1 2 1 3 2 3 3 4 4 5 3 1 2 1 1 1 3 2 3 4 3 5 4 6 4 cost=7 . } }//for j esteInArbore[jmin]=true. for(j=1.k<=n.j<=n.j) if(d[jmin]+cost[jmin][j]<d[j]) { d[j]=d[jmin]+cost[jmin][j].j<=n. GRAFURI st. costArbore+=min.nextToken(). cost[i][j]=cost[j][i]=(int)st. } d[nodStart]=0. p[j]=jmin. } } }//for k for(k=1.close(). if(min>d[j]) { min=d[j].nextToken(). for(k=1.nval. out.k++) if(p[k]!=0) out.k<=n. d[jmin]=0.println("cost="+costArbore).158 CAPITOLUL 6. for(j=1. out.k++) // O(n) { min=oo.nval.

w[j][i]=w[i][j]=cost.i++) // afisez muchiile din arbore . } prim(nods).2. // hd[i]=distanta din pozitia i din heap static int[] hnod.nextToken().out"))). st. st. st.k. // hnod[i]= nodul din pozitia i din heap static int[] pozh. for(k=1. cost=(int)st.nextToken().nval. ARBORE MINIM DE ACOPERIRE 5 6 3 4 6 2 */ Algoritmul lui Prim cu heap 159 import java. n=(int)st.j<=n. // arbore minim de acoperire: algoritmul lui Prim class PrimHeap // folosesc distantele catre arbore { // pastrate in MinHeap ==> O(n log n) ==> OK !!! static final int oo=0x7fffffff.nval.j++) w[i][j]=oo. i=(int)st.nval.6.nval.i<=n.m.cost.i++) for(j=1. st.in"))).costa=0.k<=m. // predecesorul nodului in arbore static int[] hd. w=new int[n+1][n+1]. st. nods=(int)st.nextToken().i<=n. j=(int)st.nextToken(). // pozh[i]=pozitia din heap a nodului i public static void main(String[]args) throws IOException { StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("prim.nods. for(i=1.k++) { st.*. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("prim. // matricea costurilor static int[] d.nextToken(). for(i=1. static int[][] w. m=(int)st.nval. int i.nextToken(). static int n.io.nval. // distante de la nod catre arbore static int[] p.j.

urcInHeap(pozh[nods]). . p[u]=0.160 CAPITOLUL 6.v<=q.q..close(). if(u==-1) { System.) static void relax(int u..u++) { hnod[u]=pozh[u]=u. p=new int [n+1].v. hd[u]=d[u]=oo. while(q>0) // la fiecare pas adaug un varf in arbore { u=extragMin(q). hnod=new int[n+1]. d=new int [n+1].} out. out. for(v=1. hd=new int[n+1].hnod[v]). hd[pozh[nods]]=0. GRAFURI if(i!=nods) {out.println("graf neconex !!!"). } q=n.println(p[i]+" "+i).out.println("costa="+costa).v) relax(u. break. pozh=new int[n+1]. }//main static void prim(int nods) { int u.q if(w[u][hnod[v]]<oo) // cost finit ==> exista arc (u.aux. for(u=1..int v) { if(w[u][v]<d[v]) { d[v]=w[u][v]. p[v]=u.v++) // noduri nefinalizate = in heap 1.u<=n. // q = noduri nefinalizate = dimensiune heap d[nods]=0. } q--.costa+=w[p[i]][i]. // relax si refac heap } }//prim(.

hd[1]=hd[q]. tata=fiu. hnod[fiu]=hnod[tata].fiu1. fiu1=2*tata. hd[fiu]=hd[tata]. urcInHeap(pozh[v]).tata.. fiu2=2*tata+1. aux=hnod[fiu]. hd[q]=aux.fiu2. pozh[hnod[fiu]]=tata. ARBORE MINIM DE ACOPERIRE hd[pozh[v]]=d[v]. if(hd[tata]<=hd[fiu]) break. } }//relax(. } .q { // returnez valoarea minima (din varf!) si refac heap in jos // aducand ultimul in varf si coborand ! int aux.2. // 1 <---> q 161 while(fiu1<=q-1) //refac heap de sus in jos pana la q-1 { fiu=fiu1. aux=hd[1]. tata=1.fiu. fiu1=2*tata. hd[tata]=aux.. pozh[hnod[tata]]=fiu.) static int extragMin(int q) // in heap 1. hnod[q]=aux.6. hnod[1]=hnod[q]. pozh[hnod[1]]=1. aux=hd[fiu]. fiu2=2*tata+1. hnod[tata]=aux. pozh[hnod[q]]=q. if(fiu2<=q-1) if(hd[fiu2]<hd[fiu1]) fiu=fiu2. aux=hnod[1].nod..

hd[tata]=aux. GRAFURI return hnod[q]. }// extragMin(. } pozh[nod]=fiu. hd[fiu]=hd[tata]. while((tata>0)&&(hd[fiu]<hd[tata])) { pozh[hnod[tata]]=fiu.2.nod.. tata=fiu/2.) // hnod[1] a ajuns deja (!) in hnod[q] static void urcInHeap(int nodh) { int aux. fiu=tata. hnod[fiu]=hnod[tata]. dup˘ a urm˘ atoarea metod˘ a a lui Kruskal: se alege ˆ ıntˆ ai muchia de cost minim.fiu.tata. } }//class /* 6 7 1 2 1 3 2 3 3 4 4 5 5 6 4 6 */ 3 3 1 2 1 1 3 2 3 1 3 2 3 4 4 5 4 6 costa=7 6.2 Algoritmul lui Kruskal Arborele part ¸ial de cost minim poate fi construit muchie cu muchie. hnod[fiu]=nod.162 CAPITOLUL 6. fiu=nodh. nod=hnod[nodh]. aux=hd[fiu]. iar apoi se adaug˘ a repetat muchia de cost minim nealeas˘ a anterior ¸ si care nu formeaz˘ a cu .. tata=fiu/2.

z. ˆ In final se obt ¸ine arborele part ¸ial de cost minim al grafului G. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("kruskal.nval. public static void main(String[] args) throws IOException { int k.io. import java.nval.in"))). Alegem astfel |V |1 muchii.out"))).*. System.println(cost). n=(int)st. out.nextToken(). } kruskal(). st.k++) { st.nextToken(). y[k]=(int)st. }//main static void kruskal() { . et=new int[n+1].nval.nextToken(). z=new int[m+1].m. ˆ In algoritmul lui Kruskal la fiecare pas graful part ¸ial < V. x[k]=(int)st.et.nval. st. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("kruskal. out. A > formeaz˘ a o p˘ adure de componente conexe ˆ ın care fiecare component˘ a conex˘ a este la rˆ andul ei un arbore partial de cost minim pentru vˆ arfurile pe care le conecteaz˘ a.k<=m.cost=0. static int[] x. ARBORE MINIM DE ACOPERIRE 163 precedentele un ciclu.out. st. x=new int[m+1]. st.nextToken().close(). // Arbore minim de acoperire : Kruskal class Kruskal { static int n.println("cost="+cost). m=(int)st.6. y=new int[m+1]. z[k]=(int)st.2. for(k=1.y.nextToken().nval.

if(p<k-1) qsort(p. if(i<j) { invers(i.println(x[k]+" "+y[k]). etg2=et[y[k]]. invers(i.k. x[i]=x[j].u. while((z[i]<=z[j])&&(i<j)) j--. while(i<j) { while((z[i]<=z[j])&&(i<j)) i++.m.i<=n. int u.u. i=p.164 int nm=0. } . cost+=z[k]. for(k=1.k++) et[k]=k.j.x). aux=x[i]. j=u.out. int []x) { int k=poz(p.etg1. } static void invers(int i. } }//kruskal static void qsort(int p. } CAPITOLUL 6.x).y). x[j]=aux. } if(nm==n-1)break.etg2. for(int i=1. int j.j.j. for(k=1.x).k<=m. int x[]) { int aux. etg1=et[x[k]]. if(k+1<u) qsort(k+1.k-1.k<=n.k++) { if(et[x[k]]!=et[y[k]]) { nm++. int z[]) { int k.i.j. System. qsort(1.z). int u. GRAFURI static int poz(int p.x).z). invers(i.i++) if(et[i]==etg2) et[i]=etg1.

Dac˘ a exist˘ a un vˆ arf w adiacent lui v (adic˘ a. //i==j }//poz }//class 165 6. etc. sau muchia v.finalizat. apel˘ am din nou procedura. nod_destinatie (drum!) .6.culoare static int[][] a.f. Cˆ and toate vˆ arfurile adiacente lui v au fost marcate.in"))). tare conexe) ale unui graf. La intoarcerea din apelul recursiv. se ˆ ıncheie consultarea ˆ ınceput˘ aˆ ın v . dac˘ a exist˘ a arcul (v. pˆ an˘ a cˆ and toate vˆ arfurile din V au fost marcate. BLACK=2. dac˘ a exist˘ a un alt vˆ arf adiacent lui v care nu a fost vizitat.predecesor.3 Parcurgeri ˆ ın grafuri Fie G =< V. w) ˆ ın graful orientat G.j. Init ¸ial. static int[] d.3. PARCURGERI ˆ IN GRAFURI } return i. // arborele DFS (parcurgere in adancime) class DFS // momentele de descoperire si finalizare a nodurilor { // drum intre doua varfuri static final int WHITE=0. sau verificarea faptului c˘ a un graf este aciclic.1 Parcurgerea ˆ ın adˆ ancime . static int n.p.t. GRAY=1. public static void main(String[] args) throws IOException { StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dfs. // descoperit.m.*. w ˆ ın graful neorientat G) care nu a fost vizitat. cum ar fi: detectarea componentelor conexe (respectiv. M > un graf orientat sau neorientat.io.k. import java. ale c˘ arui vˆ arfuri dorim s˘ a le consult˘ am. Presupunem c˘ a avem posibilitatea s˘ a marc˘ am varfurile deja vizitate. int i. alegem un vˆ arf oarecare v ∈ V ca punct de plecare ¸ si ˆ ıl marc˘ am. Dac˘ a au r˘ amas vˆ arfuri ˆ ın V care nu au fost vizitate.3. 6. Continu˘ am astfel. nici un vˆ arf nu este marcat. Parcurgerea ˆ ın adˆ ancime se dovede¸ ste util˘ aˆ ın numeroase probleme din teoria grafurilor.DFS Pentru a efectua o parcurgere ˆ ın adˆ ancime. alegem unul din aceste vˆ arfuri ¸ si apel˘ am procedura de parurgere. alegem vˆ arful w ca noul punct de plecare ¸ si apel˘ am recursiv procedura de parcurgere ˆ ın adˆ ancime.color.nods.nodd. // nods=nod_start_DFS.

}//main static void dfs(int u) { int v.nval. for(v=1. a[j][i]=1. d[u]=++t. i=(int)st.nextToken(). !!! { color[i]=WHITE.print("Finalizat :\t"). afisv(d). st.nextToken().println(). nodd=(int)st.i++) // oricum erau initializati implicit. afisv(f). color=new int[n+1].nextToken().nval.out.nextToken().print("drum : "). } t=0. st. n=(int)st. p=new int[n+1]. dfs(nods). System. f=new int[n+1].out. p[i]=-1.v++) if(a[u][v]==1) if(color[v]==WHITE) { p[v]=u.nval. for(k=1.nextToken(). // listele de adiacenta .out. System.166 CAPITOLUL 6. j=(int)st. a[i][j]=1. st. a=new int[n+1][n+1].. nods=(int)st.nval.nextToken(). drum(nodd).nval. } for(i=1. color[u]=GRAY. d=new int[n+1].. GRAFURI st..nval.. st.out. dar .i<=n.print("Descoperit :\t").k++) { st. !!! // v in Ad(u) !!! . System. System.k<=m.v<=n. m=(int)st.

out. exploreaz˘ a prima dat˘ a un vˆ arf w adiacent lui v . Spre deosebire de parcurgerea ˆ ın adancime. System.i++) System.2 Parcurgerea ˆ ın l˘ a¸ time . } }//class /* 6 7 3 4 1 4 4 6 6 1 5 3 2 5 1 3 4 5 */ 167 drum : 3 1 4 Descoperit : Finalizat : 2 11 5 6 1 12 3 10 4 7 8 9 6.) static void afisv(int[] x) { int i. atunci cˆ and se ajunge la un varf v oarecare.out. Pentru a efectua o parcurgere ˆ ın l˘ a¸ time a unui graf (orientat sau neorientat) proced˘ am astfel: atunci cˆ and ajungem ˆ ıntr-un vˆ arf oarecare v nevizitat. }//dfs static void drum(int u) // nod_sursa ---> nod_destinatie { if(p[u]!=-1) drum(p[u]). . f[u]=++t. etc.i<=n.. Se folose¸ ste o coad˘ a pentru plasarea vˆ arfurilor nevizitate adiacente vˆ arfului v . for(i=1. PARCURGERI ˆ IN GRAFURI dfs(v). apoi un vˆ arf adiacent lui w. } color[u]=BLACK.3.3.. }// drum(. System.print(x[i]+"\t").6.out. apoi toate vˆ arfurile nevizitate adiacente vˆ arfurilor adiacente lui v .print(u+" "). parcurgerea ˆ ın lat ¸ime nu este ˆ ın mod natural recursiv˘ a. etc. ˆ ıl marc˘ am ¸ si apoi vizit˘ am toate vˆ arfurile nevizitate care sunt adiacente lui v .println().BFS Procedura de parcurgere ˆ ın adˆ ancime.

nodd. // pentru bfs static int[] p.nextToken().nval. p=new int[n+1]. // infinit static final int WHITE=0. cˆ ate unul pentru fiecare component˘ a conex˘ a.out"))). st. class BFS // nodurile sunt de la 1 la n { // distanta minima dintre nod "sursa" si nod "dest" static final int oo=0x7fffffff.nextToken(). // sfarsit coada = prima pozitie libera pe care voi pune ! static int n. atunci obt ¸inem o p˘ adure de arbori.nval.nextToken(). a[i][j]=1. BLACK=2. // matricea de adiacenta static int[] color. // predecesor static int[] d. d=new int[n+1].nval.k++) { st. nod_destinatie StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("bfs.*. parcurgerea ˆ ın l˘ atime a unui graf G conex asociaz˘ a lui G un arbore part ¸ial.168 CAPITOLUL 6. nodd=(int)st. Dac˘ a G nu este conex. nods=(int)st. q=new int[m+1]. // varfuri.k<=m.nval.nextToken(). st. for(k=1. color=new int[n+1]. GRAY=1.k. a[j][i]=1. GRAFURI Ca ¸ si ˆ ın cazul parcurgerii ˆ ın adˆ ancime. a=new int[n+1][n+1]. // inceput coada = prima pozitie ocupata din care scot ! static int sc.nval.nextToken(). static int[][] a. n=(int)st.j. muchii public static void main(String[] args) throws IOException { int i.nval. import java. j=(int)st.nextToken().in"))).m. m=(int)st. i=(int)st.nods.io. st. // nod_sursa. st. // coada static int ic. // distante catre sursa static int[] q. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("bfs. . st.

out. } static void incoada(int v) { q[sc++]=v. }//main static void videzcoada() { ic=0. color[v]=GRAY. d[u]=oo."+nodd+") = "+d[nodd]). u++) { color[u]=WHITE.println(). System.nodd). PARCURGERI ˆ IN GRAFURI } bfs(nods. out.println("Distanta("+nods+".. . drum(nodd). return v.out. for(u=1. } static void bfs(int start.introduc } static boolean coadaEsteVida() { return (sc==ic). } static int dincoada() { int v=q[ic++]. color[v]=BLACK. 169 System. sc=0. int fin) { int u.icoada. d[start]=0. } color[start]=GRAY..out.print("drum : "). System. u<=n. // coada : scot <-.6.close(). v.3.scoada <-.

v++) { if(a[u][v]==1) // v in Ad[u] { if(color[v]==WHITE) // neparcurs deja { color[v]=GRAY.170 videzcoada(). CAPITOLUL 6. while(!coadaEsteVida()) { u=dincoada(). GRAFURI // Cauta nodurile albe v adiacente cu nodul u si pun v in coada for(v=1. // optimizare. d[v]=d[u]+1. v<=n.out. } }//class /* 9 12 2 8 2 5 1 2 2 3 3 1 3 4 4 5 5 6 6 4 Distanta(2. if(color[fin]!=WHITE) break. }//while }//bfs // am ajuns la nod_destinatie static void drum(int u) // nod_sursa ---> nod_destinatie { if(p[u]!=0) drum(p[u]). p[v]=u. if(color[fin]!=WHITE) break. } } }//for color[u]=BLACK.8) = 4 drum : 2 3 4 7 8 . incoada(start).print(u+" "). System. ies din for incoada(v).

time.. u . // culoare static int[] lista. n=(int)st. 6. static int n.pozl.k. st.*.v)=arc ==> "." in lista ("u se termina inaintea lui v") Algoritm: 1.nval. Dac˘ a respectivul graf este ciclic.. // varfuri.4 Sortare topologic˘ a Sortarea topologic˘ a reprezint˘ a o sortare a nodurilor unui graf orientat cu proprietatea c˘ a un nod i se afl˘ aˆ ınaintea unui nod j dac˘ a¸ si numai dac˘ a nu exist˘ a un drum de la nodul j la nodul i.t.nextToken(). BLACK=2.m... v . muchii. DFS pentru calcul f[u].io. cand u=terminat ==> plasaz in lista pe prima pozitie libera de la sfarsit catre inceput Solutia nu este unica (cea mai mica lexicografic = ???) O(n*n)= cu matrice de adiacenta O(n+m)= cu liste de adiacenta import java.nods.4. // lista static int[][] a.in"))). acesta nu poate fi sortat topologic.4. // descoperit static int[] f. // matricea de adiacenta public static void main(String[] args) throws IOException { int i.1 // // // // // // // // Folosind parcurgerea ˆ ın adˆ ancime Sortare Topologica = ordonare lineara a varfurilor (in digraf) (u. u=nod 2.˘ 6. GRAY=1.j.. SORTARE TOPOLOGICA 7 8 8 9 9 7 7 4 */ 171 6. . class SortTopoDFS { static final int WHITE=0. // finalizat static int[] color. // nods=nod_start_DFS StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("sortTopo. pozitie in lista static int[] d..

f[u]=++t.println(). f=new int[n+1].nval. lista[pozl]=u. color=new int[n+1]. t=0. --pozl.nval. i=(int)st.nextToken(). color[u]=GRAY.i++) System.v<=n.out. }//dfs }//class /* 6 4 6 3 1 2 3 4 5 6 */ 5 6 3 4 1 2 . CAPITOLUL 6. System. pozl=n.nods++) if(color[nods]==WHITE) dfs(nods).k<=m.nval. for(k=1.. for(nods=1. GRAFURI a=new int[n+1][n+1]. for(v=1.print(lista[i]+" ").i<=n. }//main static void dfs(int u) { int v. color[u]=BLACK.k++) { st.nextToken(). st. d=new int[n+1]. m=(int)st. for(i=1. a[i][j]=1.v++) // mai bine cu liste de adiacenta ..nextToken().i++) color[i]=WHITE. lista=new int[n+1].i<=n.out. } for(i=1. d[u]=++t.nods<=n.172 st. j=(int)st. !!! if(a[u][v]==1) // v in Ad(u) !!! if(color[v]==WHITE) dfs(v).

st..4.i. lista=new int[n+1].nextToken()..nval.nval.nextToken(). j=(int)st.pozl. // matricea de adiacenta public static void main(String[] args) throws IOException { int u. a[i][j]=1.4. st. u --> lista (pe cea mai mica pozitie neocupata) 3. . i=(int)st. // varfuri. n=(int)st. v . SORTARE TOPOLOGICA 173 6. class SortTopoGRAD { static final int WHITE=0. for(k=1. // lista static int[] gi. muchii..m.nval.i<=n.v)=arc OBS: pentru prima solutie lexicografic: aleg u="cel mai mic" (heap!) OBS: Algoritm="stergerea repetata a nodurilor de grad zero" import java. pozitie in lista static int[] color.v)=arc ==> ".nval. // grad interior static int[][] a. aleg un nod u cu gi[u]=0 (gi=gradul interior) 2. } for(i=1.in"))). m=(int)st. gi=new int[n+1]. // culoare static int[] lista. unde (u.j. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("sortTopo.nextToken()..k<=m. st.2 // // // // // // // // Folosind gradele interioare Sortare Topologica = ordonare lineara a varfurilor (in digraf) (u.˘ 6. color=new int[n+1]..*." in lista ("u se termina inaintea lui v") Algoritm: cat_timp exista noduri neplasate in lista 1.k..i++) color[i]=WHITE.k++) { st.nextToken(). u . gi[j]++. a=new int[n+1][n+1]. // color[u]=BLACK ==> u in lista static int n. decrementez toate gi[v].io. BLACK=1.

}//main static int nodgi0() // nod cu gradul interior zero { int v. for(v=1. for(v=1. } for(i=1.out. for(k=1. break.174 CAPITOLUL 6.v<=n. System.i<=n.out. color[u]=BLACK.i++) System. } }//class /* 6 4 6 3 1 2 3 4 5 6 */ 1 2 5 6 3 4 6.v<=n. GRAFURI pozl=1.k++) // pun cate un nod in lista { u=nodgi0().k<=n.v++) // lista de adiacenta este mai buna !!! if(color[v]==WHITE) if(a[u][v]==1) gi[v]--. micsorezGrade(u).nod=-1.print(lista[i]+" ").5 Componente conexe ¸ si tare conexe ˆ In cazul grafurilor neconexe.} return nod. } static void micsorezGrade(int u) { int v.println(). lista[pozl++]=u. ne poate interesa aflarea componentelor sale .v++) // coada cu prioritati (heap) este mai buna !!! if(color[v]==WHITE) if(gi[v]==0) {nod=v.

determinˆ andu-se componenta conex˘ a din care acesta face parte.nval.1 Componente conexe Pentru a afla componentele conexe ale unui graf neorientat se poate proceda astfel: mai ˆ ıntˆ ai se face o parcurgere ˆ ın adˆ ancime din nodul 1. COMPONENTE CONEXE S ¸ I TARE CONEXE 175 conexe.io. ¸ si tot a¸ sa pˆ an˘ a cˆ and au fost vizitate toate nodurile. ˆ ınt ¸elegem o mult ¸ime maximal˘ a de noduri ale grafului.5.in"))). for(k=1. // determinarea componentelor conexe class CompConexe { static int n.k++) { st.k. import java. a=new int[n+1][n+1]. a[i][j]=a[j][i]=1. st. m=(int)st. st.nval.j. ncc=0.i++) if(cc[i]==0) { . StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("compConexe.*. st. apoi se face o nou˘ a parcurgere dintrun nod nevizitat ˆ ınc˘ a. determinˆ andu-se componenta conex˘ aˆ ın care se afl˘ a acest nod.nextToken(). static int [] cc.m.nextToken().k<=m.6.i<=n.ncc. public static void main(String[] args) throws IOException { int i. static int[][] a.nval. } cc=new int[n+1]. j=(int)st.nextToken(). cu proprietatea c˘ aˆ ıntre oricare dou˘ a noduri ale sale exist˘ a cel putin un drum. i=(int)st. 6.5.nextToken(). n=(int)st. Printr-o component˘ a conex˘ a a unui graf.nval. for(i=1.

Algoritmul Roy-Warshall se poate folosi dar complexitatea lui (O(n3 )) este mult prea mare.j<=n.out.out. GRAFURI 1 : 1 2 3 2 : 4 5 3 : 6 7 8 9 6.out. } for(i=1.print(i+" : "). conex(i). // determinarea componentelor tare conexe (in graf orientat!) // Algoritm: 1. System. for(int v=1.i++) { System. } }//main static void conex(int u) { cc[u]=ncc. }//conex }//class /* 9 7 1 2 2 3 3 1 4 5 6 7 7 8 8 9 */ CAPITOLUL 6.println().i<=ncc. dfs(G) pentru calculul f[u] // 2. dfs(G_transpus) in ordinea descrescatoare a valorilor f[u] .176 ncc++.v++) if((a[u][v]==1)&&(cc[v]==0)) conex(v).print(j+" "). for(j=1.2 Componente tare conexe Determinarea conexit˘ a¸ tii ˆ ın cazul grafurilor orientate (ˆ ın acest caz se nume¸ ste tare conexitate) este ceva mai dificil˘ a.v<=n.j++) if(cc[j]==i) System.5.

static int[][] a. j=(int)st. color=new int[n+1]. ctc=new int[n+1]. for(i=1. for(i=1.color.pozLista. } for(i=1.nextToken(). at[j][i]=1.i++) { .i<=nctc. static int [] ctc.k++) { st.i<=n.i++) color[i]=WHITE. for(k=1. BLACK=2.5.j. GRAY=1.nextToken().nextToken().nctc. nctc=0.nval.i++) if(color[lista[i]]==WHITE) { nctc++.lista.nval.i<=n.t=0. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("compTareConexe.k<=m.i++) if(color[i]==WHITE) dfsa(i). i=(int)st. dfsat(lista[i]).io. class CompTareConexe { static final int WHITE=0. m=(int)st. st. // matricea grafului transpus (se poate folosi numai a !) public static void main(String[] args) throws IOException { int i.m. pozLista=n.f. // transpusa } for(i=1.i<=n.nextToken(). lista=new int[n+1]. n=(int)st. a=new int[n+1][n+1].*. st.i<=n. at=new int[n+1][n+1].6. COMPONENTE CONEXE S ¸ I TARE CONEXE // OBS: G_transpus are arcele din G "intoarse ca sens" // Lista este chiar o sortare topologica !!! 177 import java. f=new int[n+1]. for(i=1. static int n. // matricea grafului static int[][] at. st.k.nval.i++) color[i]=WHITE.in"))). a[i][j]=1.nval.

System. ctc[u]=nctc. color[u]=GRAY.print(j+" ").println(). color[u]=GRAY.j++) if((at[u][lista[j]]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]).3 Noduri de separare Un vˆ arf v al unui graf neorientat conex este un nod de separare sau un punct .j++) if(ctc[j]==i) System. } }//class /* 9 10 1 2 2 3 3 1 4 5 6 7 7 8 8 9 5 4 7 6 8 7 */ 1 2 3 4 : : : : 6 7 8 9 4 5 1 2 3 6.print(i+" : "). //"a" color[u]=BLACK. } static void dfsat(int u) // se poate folosi "a" inversand arcele ! { int j.5.out. for(j=1.out.178 CAPITOLUL 6. } }//main static void dfsa(int u) { int v. //"at" //if((a[lista[j]][u]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]). for(v=1. lista[pozLista--]=u. f[u]=++t.j<=n.v++) if((a[u][v]==1)&&(color[v]==WHITE)) dfsa(v). color[u]=BLACK. GRAFURI System.out.v<=n. for(j=1.j<=n.

m=(int)st. Un graf neorientat este biconex (sau nearticulat) dac˘ a este conex ¸ si nu are puncte de articulare. a[i][j]=a[j][i]=1.k<=m. }//main static boolean econex(int nodscos) { int i. // determinarea nodurilor care strica conexitatea // in graf neorientat conex public static void main(String[] args) throws IOException { int i.i++) if(!econex(i)) System. aceasta ne garanteaz˘ a c˘ a ret ¸eaua continu˘ a s˘ a funct ¸ioneze chiar ¸ si dup˘ a ce echipamentul dintrun vˆ arf s-a defectat. static int[][] a. for(i=1.i<=n. a=new int[n+1][n+1]. for(k=1. out.in"))).nextToken().i<=n. ncc=0.5. st.nextToken().*.nextToken(). static int [] cc. COMPONENTE CONEXE S ¸ I TARE CONEXE 179 de articulare. n=(int)st.k. import java.nval.nval.j.out. st.m.close(). PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("noduriSeparare.k++) { st. Grafurile biconexe au importante aplicat ¸ii practice: dac˘ ao ret ¸ea de telecomunicat ¸ii poate fi reprezentat˘ a printr-un graf biconex. dac˘ a subgraful obt ¸inut prin eliminarea lui v ¸ si a muchiilor care plec˘ a din v nu mai este conex.nval. int[] cc=new int[n+1]. class NoduriSeparare { static int n.nval.print(i+" "). StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("noduriSeparare.io. i=(int)st.out"))). st.6.nextToken(). } for(i=1. j=(int)st.i++) if(i!=nodscos) if(cc[i]==0) { .

Exist˘ a algoritmi eficient ¸i pentru determinarea acestora dar aici este prezentat un algoritm foarte simplu: se elimin˘ a pe rˆ and fiecare muchie ¸ si se testeaz˘ a conexitatea grafului astfel obt ¸inut. i=(int)st. conex(i.5.nval. public static void main(String[] args) throws IOException { int i.*. for(int v=1.nval. }//conex }//class 6.et.nextToken().k. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("muchieRupere. st.m.int et. st.nextToken().4 Muchii de separare Muchile care prin eliminarea lor stric˘ a proprietatea de conexitate a unui graf neorientat conex se numesc muchii de separare. static int [] cc. a=new int[n+1][n+1]. st. static int[][]a. m=(int)st.ncc.180 ncc++.io.k<=m. sau de rupere.v<=n. for(k=1. CAPITOLUL 6. }// econex() static void conex(int u.k++) { st.nval.cc.nval. import java.nodscos). GRAFURI } if(ncc>1) return false.nextToken().cc. else return true. if(ncc>1) break.nodscos).out"))). // determinarea muchiilor care strica conexitatea class MuchieRupere // in graf neorientat conex { static int n.v++) if(v!=nodscos) if((a[u][v]==1)&&(cc[v]==0)) conex(v.in"))). .int[]cc.int nodscos) { cc[u]=et. n=(int)st. j=(int)st.nextToken(). StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("muchieRupere.j.

a[i][j]=a[j][i]=0. a[i][j]=a[j][i]=1. }//conex }//class /* 9 10 7 2 5 1 1 8 181 1 2 3 7 8 7 9 9 .6. for(i=1. a[j][i]=1.v<=n.println(i+" "+j). cc=new int[n+1].v++) if((a[u][v]==1)&&(cc[v]==0)) conex(v. ncc.i<=n.5.j<=n. if(ncc>1) break. } } if(ncc==1) return true.i++) { if(cc[i]==0) { ncc++. ncc=0.out. conex(i.et).i<=n. }// econex() static void conex(int u.j++) { if(a[i][j]==0) continue. COMPONENTE CONEXE S ¸ I TARE CONEXE a[i][j]=1. for(int v=1. else return false.ncc).i++) for(j=i+1. } for(i=1.close(). if(!econex()) System.int et) { cc[u]=et. } out. }//main static boolean econex() { int i.

// (u.ncb. // noduri = 1. // pozitia varfului stivei unde este deja incarcat un nod (root) fs[vs]=root. // culoarea nodului int[] fs. m=muchii.out. d[u]=moment descoperire nod u int[][] B.print("Puncte de articulare : ").n class Biconex // liste de adiacenta pentru graf { // vs=varf stiva. root=3. // puncte de articulare int[] color. int n. // tata_root=0 (nu are!) t=0.0).root. // componente biconexe int[] A. // fs=fiu stiva.182 9 4 6 9 6 4 4 1 9 5 9 7 9 3 */ CAPITOLUL 6.println("Graful este Biconex").println("Graful NU este Biconex"). // initializare time.d. // root=radacina arborelui DFS int[][] G.t. GRAFURI 6.ndr.*.vs..tatau) tatau=0 ==> nu exista tatau if(ncb==1) System.ts.out.. ts=tata stiva public static void main(String[] args) throws IOException { init(). numerotarea nodurilor in DFS dfs(root. else { System. . t=time in parcurgerea DFS static static static static static static static static final int WHITE=0.io. // liste de adiacenta int[] grad.5. afisv(A). if(ndr>1) A[root]=1. low[]. // radacina arborelui (de unde declansez DFS) vs=0.. // grad nod. GRAY=1.m=0.BLACK=2. ncb=nr componente biconexe // ndr=nr descendenti radacina (in arbore DFS). System.out..low. // pun in stiva "root" si ts[vs]=0.5 Componente biconexe // Componenta biconexa = componenta conexa maximala fara muchii de rupere import java.

out.nval. for(int i=1. st. d=new int[n+1]. B=new int[n+1][n+1].i<=grad[u]. n=(int)st. d[u]=++t. } }//Init() static void dfs(int u.k<=m. st.out. for(i=1.println(ncb). /* calculeaza d si low */ // fiuu = un descendent al lui u .k. } } }//main() static int minim(int a. G[i][++grad[i]]=j.nextToken(). color=new int[n+1].nextToken(). fs=new int[m+1]. int tatau) { int fiuu. color[u]=GRAY. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("biconex. // vectorii sunt initializati cu zero grad=new int[n+1]. low=new int[n+1].i<=ncb. low[u]=d[u]. st. ! for(k=1.j. System.i++) { fiuu=G[u][i]. // 0=WHITE G=new int[n+1][n+1]. i=(int)st. int b) { return a<b?a:b.print("Componenta Biconexa "+i+" : ").out. COMPONENTE CONEXE S ¸ I TARE CONEXE System. j=(int)st.print("Numar componente Biconexe : ").nval. m=(int)st.5. afisv(B[i]).nval. } // minim() 183 static void init() throws IOException { int i.i.nextToken().6. ts=new int[m+1].nval.i++) { System.in"))).k++) { st. G[j][++grad[j]]=i. A=new int[n+1].nextToken().

} color[u]=BLACK. } } else // (u.184 CAPITOLUL 6. ncb++. // root=caz special (maresc nrfiiroot) dfs(fiuu. GRAFURI if(fiuu==tatau) continue. // root = caz special compBiconexa(fiuu.d[fiuu]). ts[vs]=u. do { tatas=ts[vs]. B[ncb][tatas]=1.out. . fius=fs[vs]. } if(color[fiuu]==WHITE) /* fiuu nu a mai fost vizitat */ { if(u==root) ndr++.u)..fiuu) = bridge (pod) System. } // dfs(. am identificat o componenta biconexa ce contine muchiile din stiva pana la (u.fiuu) */ if(low[fiuu]!=low[u]) // (u.fiuu) */ vs++. fs[vs]=fiuu.fiuu) = back edge low[u]=minim(low[u]. if(u!=root) A[u]=1. // este aceeasi muchie if((color[fiuu]==WHITE)|| // fiuu nedescoperit sau (d[fiuu]<d[u])) // (u. if(low[fiuu]>=d[u]) // "=" ==> fiuu intors in u ==> ciclu "agatat" in u !!! // ">" ==> fiuu nu are drum de rezerva !!! { /* u este un punct de articulatie.fius.low[fiuu]).u)..fiuu) este muchie de intoarcere { /* insereaza in stiva muchia (u. B[ncb][fius]=1. vs--. // acum este terminat tot subarborele cu radacina fiuu !!! low[u]=minim(low[u]. int tata) { int tatas.println("Bridge: "+fiuu+" "+u).) static void compBiconexa(int fiu.

println().6 Distant ¸e minime ˆ ın grafuri Algoritmii de drum minim se ˆ ımpart ˆ ın patru categorii: • surs˘ a unic˘ a . } // compBiconexa(.destinat ¸ie unic˘ a: pentru determinarea drumului minim de la un nod numit surs˘ a la un nod numit destinat ¸ie.i++) if(x[i]==1) System.6. { for(int i=1.destinat ¸ie unic˘ a nu au o complexitate mai bun˘ a decˆ at cei de tip sursa unic˘ a . Algoritmii de tip surse multiple .) }//class /* 8 9 <-. .destinat ¸ie unic˘ a: pentru determinarea drumurilor minime de la toate nodurile grafului la un nod numit destinat ¸ie. inversˆ and sensul arcelor ˆ ın cazul grafurilor orientate..destinat ¸ii multiple: pentru determinarea drumurilor minime de la un nod numit surs˘ a la toate celelalte noduri ale grafului.print(i+" "). DISTANT ¸ E MINIME ˆ IN GRAFURI } while(!((tata==tatas)&&(fiu==fius))).out..n m 1 8 1 2 1 3 6 3 4 | \ 2 4 | 5 --3 5 | / 5 7 7 5 6 6 7 */ 185 8 | 1 / 3 \ 4 \ 2 / Bridge: 8 1 Bridge: 5 3 Graful NU este Biconex Puncte de articulare : 1 3 5 Numar componente Biconexe : 4 Componenta Biconexa 1 : 1 8 Componenta Biconexa 2 : 1 2 3 4 Componenta Biconexa 3 : 5 6 7 Componenta Biconexa 4 : 3 5 6.destinat ¸ie unic˘ a sunt echivalent ¸i cu cei de tip sursa unic˘ a .i<=n.out.destinat ¸ii multiple... • surs˘ a unic˘ a .) static void afisv(int[] x) // indicii i pentru care x[i]=1. System. • surse multiple .6.destinat ¸ii multiple: pentru determinarea drumurilor minime ˆ ıntre oricare dou˘ a noduri ale grafului.destinat ¸ii multiple. Algoritmii de tip sursa unic˘ a . }// afisv(. • surse multiple .

m=(int)st. O alt˘ a clasificare a algoritmilor de drum minim este ˆ ın funct ¸ie de costul muchiilor. GRAFURI Algoritmii de tip surse multiple . 6.k++) { st. Pentru aceast˘ a problem˘ a exist˘ a¸ si algoritmi mai eficient ¸i. deci costul pe muchii este unitar.nval. for(i=1. d=new int[n+1][n+1].k. • graful este ponderat cu toate costurile pozitive.i<=n.j++) d[i][j]=oo. import java.nval.destinat ¸ii multiple pot fi implementat ¸i folosind algoritmii de tipul sursa unic˘ a .out"))).j. for(k=1. public static void main(String[]args) throws IOException { int i.186 CAPITOLUL 6. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("RoyFloydWarshall.m. n=(int)st. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("RoyFloydWarshall.io. st.j<=n.in"))).destinat ¸ii multiple pentru fiecare nod al grafului dat. • graful este ponderat cu costurile pe muchii pozitive si negative. Exist˘ a patru situat ¸ii: • graful este neponderat. ˆ ıns˘ a f˘ ar˘ a cicluri negative. st.1 Algoritmul Roy-Floyd-Warshall // Lungime drum minim intre oricare doua varfuri in graf orientat ponderat. • graful este ponderat cu costurile pe muchii pozitive ¸ si negative ¸ si cont ¸ine cicluri negative. class RoyFloydWarshall // O(n^3) { static final int oo=0x7fffffff. .k<=m.i++) for(j=1.nval.*.nextToken(). i=(int)st.6.nextToken().nextToken(). static int[][] d. static int n.

out.close().nval.print("*"+" ").out. for(i=1. st..6.2 Algoritmul lui Dijkstra Algoritmul lui Dijkstra este cel mai cunoscut algoritm (dar ¸ si unul dintre cei mai eficient ¸i) de tip surs˘ a unic˘ a .destinat ¸ii multiple pentru grafuri cu muchii de cost pozitiv.nextToken().nval.k++) for(i=1. System. DISTANT ¸ E MINIME ˆ IN GRAFURI st. algoritmul Dijkstra le poate genera ˆ ın orice ordine.i<=n.6. else System.j<=n. 2. } out.print(d[i][j]+" ").j++) if(d[i][j]<oo) System. Algoritmul lui Dijkstra folose¸ ste o mult ¸ime S care ret ¸ine nodurile pentru care (la un anumit moment) s-au generat drumurile minime de la surs˘ a la nodurile .6. }//main }//class /* 6 6 1 2 1 3 2 3 4 5 5 6 4 6 */ 187 3 1 2 1 3 2 2 3 1 * * * 3 4 2 * * * 1 2 2 * * * * * * 2 1 2 * * * 1 2 3 * * * 2 3 4 6. ˆ ın funct ¸ie de modul ˆ ın care au fost memorate muchiile grafului.i++) { for(j=1. k if((d[i][k]<oo)&&(d[k][j]<oo)) if(d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j].i++) // drumuri intre i si j care trec for(j=1. .k<=n.nextToken().println().j<=n.out.i<=n. } for(k=1. d[i][j]=d[j][i]=(int)st..j++) // numai prin nodurile 1.. j=(int)st. ˆ In cazul ˆ ın care dou˘ a drumuri au acela¸ si cost. Acest algoritm determin˘ a toate drumurile minime de la un nod s (surs˘ a) la toate celelalte noduri ale grafului ˆ ın ordine crescatoare a lungimii acestor drumuri.

... . . Pentru a demonstra aceast˘ a afirmat ¸ie vom presupune prin absurd c˘ a exist˘ a un nod w care apart ¸ine acestui drum ¸ si care nu apart ¸ine lui S . w) poate fi chiar muchia (v.. v ) apart ¸in lui S .. // drumuri minime de la nodSursa class Dijkstra // O(n^2) { static final int oo=0x7fffffff. Atunci drumul (s. . se termin˘ aˆ ıntr-un nod v care nu apart ¸ine lui S ¸ si cont ¸ine numai noduri ce apart ¸in lui S .. ˆ In momentul ˆ ın care se genereaz˘ a un nou drum minim...*. v ). .. w).. .... Drumul (v. nodul v .. aceasta este completat˘ a rˆ and pe rˆ and cu toate celelalte noduri ale grafului.. acel drum ˆ ıncepe din s... GRAFURI respective. Atunci toate nodurile intermediare care apart ¸in drumului (s. .. ˆ ın ordine cresc˘ atoare fat ¸˘ a de lungimea drumului minim de la s (nodul surs˘ a) la acele noduri. dup˘ a generarea unui drum minim pentru un nod v .. .. w. . w) poate fi descompus ˆ ın dou˘ a drumuri minime (s. w) deoarece pe parcursul algoritmului se va ˆ ıncerca optimizarea lungimii drumului minim de la s la w prin toate nodurile v din S ... . // distante de la nod catre nodSursa public static void main(String[]args) throws IOException . este introdus ˆ ın S un nod care nu apart ¸ine mult ¸imii ¸ si pentru care distant ¸a pˆ an˘ a la s este minim˘ a.. v ) poate fi ˆ ımp˘ art ¸it ˆ ın dou˘ a drumuri minime (s. .. w) ¸ si (w. v. Toate nodurile de pe drumul minim (s. La un anumit moment.. Modul ˆ ın care este implementat˘ a mult ¸imea S este decisiv pentru o bun˘ a complexitate a algoritmului Dijkstra. .. w). v. Apoi. Algoritmul lui Dijkstra cu distant ¸e. Este corect s˘ a alegem muchia (v... // predecesor in drum static int[] d. Dac˘ a acest drum se schimb˘ a.. atunci ˆ ınseamn˘ a c˘ a exist˘ a un drum mai scurt de la s la nodul respectiv..io. iar lungimea drumului (s. v ).. Algoritmul Dijkstra are n − 1 pasi (unde n este num˘ arul vˆ arfurilor). . static int[] p.... .. ...m. static int n. w) este mai mic˘ a decˆ at cea a drumului (s. v ) va fi retinut˘ a eventual ˆ ıntrun vector de distant ¸e... Drumul (s. Dar lungimea drumului (s. acesta este introdus ˆ ın mult ¸imea S . Mult ¸imea S este init ¸ializat˘ a cu nodul surs˘ a... .188 CAPITOLUL 6. w) trebuie s˘ a apartin˘ a lui S ...... static int[][] cost. deci lungimea unui drum minim de la nodul s la un nod w care nu apartine lui S ¸ si cu toate celelalte noduri intermediare ˆ ın S se poate modifica (mai precis se poate micsora). v ) ¸ si (v. ˆ ın graf neorientat import java.. . static boolean[] esteFinalizat. w. care trece prin ultimul nod introdus ˆ ın S .. Rezult˘ a contradict ¸ie cu faptul c˘ a nodurile sunt introduse ˆ ın S ˆ ın ordine crescatoare a distant ¸ei fat ¸˘ a de s. La fiecare pas.

j) if(d[jmin]+cost[jmin][j]<d[j]) { .out"))).6.i<=n. j. st.nextToken().i++) d[i]=oo. } d[nodSursa]=0.j<=n. cost[i][j]=cost[j][i]=(int)st. } }//for j esteFinalizat[jmin]=true. m=(int)st. st.nextToken().j<=n. for(j=1. st.jmin=0. d=new int[n+1]. DISTANT ¸ E MINIME ˆ IN GRAFURI { int nodSursa=1. k. for(j=1.nval.nval. 189 StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dijkstraNeorientat.j<=n. for(i=1. j=(int)st.i++) for(j=1.k<=n.nextToken().nextToken(). cost=new int[n+1][n+1].nval.k++) // O(n) { min=oo. p=new int[n+1]. esteFinalizat=new boolean [n+1]. if(min>d[j]) { min=d[j].j++) // actualizez distantele nodurilor // O(n) { if(esteFinalizat[j]) continue.nval.k<=m.6. i=(int)st. min. for(i=1. jmin=j.nextToken(). // j este deja in arbore if(cost[jmin][j]<oo) // am muchia (jmin.nval.j++) // O(n) { if(esteFinalizat[j]) continue. n=(int)st. st.i<=n.in"))).k++) { st. for(k=1. for(k=1.j++) cost[i][j]=oo. // nod start int i. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dijkstraNeorientat.

// hnod[i]= nodul din pozitia i din heap static int[] pozh.*. --> k { if(p[k]!=0) drum(p[k]). // hd[i]=distanta din pozitia i din heap static int[] hnod.m. // matricea costurilor static int[]d. p[j]=jmin..out. } }//class /* 6 7 1-->1 dist=0 drum: 1 1 2 4 1-->2 dist=3 drum: 1 3 2 1 3 1 1-->3 dist=1 drum: 1 3 2 3 2 1-->4 dist=2 drum: 1 3 4 3 4 1 1-->5 dist=3 drum: 1 3 4 5 5 4 1 1-->6 dist=4 drum: 1 3 4 6 5 6 3 4 6 2 */ Algoritmul lui Dijkstra cu heap. static int[][]w.k++) { System.190 d[j]=d[jmin]+cost[jmin][j]. ˆ ın graf neorientat import java. System. // distante minime de la nodSursa class DijkstraNeorientatHeap // pastrate in MinHeap ==> O(n log n) ==> OK !!! { static final int oo=0x7fffffff. // pozh[i]=pozitia din heap a nodului i .. static int n. CAPITOLUL 6.print(nodSursa+"-->"+k+" dist="+d[k]+" drum: "). }//main static void drum(int k) // s --> .out. System. } out.close().println(). GRAFURI } } }//for k for(k=1. drum(k).k<=n. // predecesorul nodului in arbore static int[] hd.out.io.print(k+" "). // distante de la nod catre arbore static int[]p.

} nodSursa=1. st. dijkstra(nodSursa).k<=n. st.u++) { hnod[u]=pozh[u]=u. drum(k).v.nval. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dijkstraNeorientat. cost=(int)st.k++) { System.6. DISTANT ¸ E MINIME ˆ IN GRAFURI 191 public static void main(String[]args) throws IOException { int i.k. w=new int[n+1][n+1].nextToken(). StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dijkstraNeorientat.nextToken(). j=(int)st.aux. st. . pozh=new int[n+1]. w[j][i]=w[i][j]=cost.close().nextToken().j++) w[i][j]=oo. p=new int [n+1].6. }//main static void dijkstra(int nods) { int u.u<=n. for(k=1.nextToken().nval.i<=n.k++) { st.println(). d=new int [n+1].nval. hnod=new int[n+1].i++) for(j=1. st.j.nextToken().print(nodSursa+"-->"+k+" dist="+d[k]+" drum: ").out"))).nval.j<=n. n=(int)st.in"))). i=(int)st.cost. m=(int)st.out.out.nval. for(u=1.k<=m. for(i=1.nodSursa.q. for(k=1. } out. System.costa=0. hd=new int[n+1].

} q--.q if(w[u][hnod[v]]<oo) // cost finit ==> exista arc (u. hd[pozh[v]]=d[v]. hd[1]=hd[q].fiu1. // q = noduri nefinalizate = dimensiune heap d[nods]=0. GRAFURI } q=n.hnod[v]). for(v=1.fiu2. fiu2=2*tata+1.) static int extragMin(int q) // in heap 1... p[v]=u.nod.. hd[q]=aux.tata..q { // returnez valoarea minima (din varf!) si refac heap in jos int aux. p[u]=0.out. aux=hd[1].v++) // noduri nefinalizate = in heap 1. break. pozh[hnod[1]]=1. CAPITOLUL 6. . pozh[hnod[q]]=q. tata=1.fiu.v) relax(u. hnod[q]=aux. // 1 <---> q aux=hnod[1]. while(q>0) // la fiecare pas adaug un varf in arbore { u=extragMin(q). // relax si refac heap } }// dijkstra() static void relax(int u. hnod[1]=hnod[q]. } }// relax(. urcInHeap(pozh[v]). fiu1=2*tata. hd[pozh[nods]]=0. if(u==-1) { System. urcInHeap(pozh[nods]).v<=q.println("graf neconex !!!").int v) { if(d[u]+w[u][v]<d[v]) { d[v]=d[u]+w[u][v].192 hd[u]=d[u]=oo.

tata. fiu1=2*tata. } // urcInHeap(. DISTANT ¸ E MINIME ˆ IN GRAFURI 193 while(fiu1<=q-1) //refac heap de sus in jos pana la q-1 { fiu=fiu1..) static void urcInHeap(int nodh) { int aux. hnod[fiu]=hnod[tata]. pozh[hnod[fiu]]=tata. tata=fiu.6.. fiu2=2*tata+1. hd[fiu]=hd[tata].6.fiu. aux=hd[fiu]. --> k { . tata=fiu/2. aux=hnod[fiu]. } pozh[nod]=fiu. aux=hd[fiu]. // hnod[1] a ajuns deja (!) in hnod[q] } // extragMin(. fiu=tata.. tata=fiu/2. if(fiu2<=q-1) if(hd[fiu2]<hd[fiu1]) fiu=fiu2.. hd[fiu]=hd[tata]. hnod[fiu]=nod. nod=hnod[nodh]. hnod[fiu]=hnod[tata]. pozh[hnod[tata]]=fiu.nod. while((tata>0)&&(hd[fiu]<hd[tata])) { pozh[hnod[tata]]=fiu.. } return hnod[q]. hd[tata]=aux. fiu=nodh. hd[tata]=aux. hnod[tata]=aux.. if(hd[tata]<=hd[fiu]) break.) static void drum(int k) // s --> .

PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dijkstraOrientat.out.nval. // drumuri minime de la nodSursa class Dijkstra // O(n^2) { static final int oo=0x7fffffff.nextToken(). j.io. // distante de la nod catre nodSursa public static void main(String[]args) throws IOException { int nodSursa=1.194 if(p[k]!=0) drum(p[k]).nextToken(). min. ˆ ın graf orientat import java. } }//class /* 6 7 1 2 1 3 2 3 3 4 5 4 5 6 4 6 */ CAPITOLUL 6.nval. p=new int[n+1]. static int[][] cost. st. cost=new int[n+1][n+1].print(k+" "). static boolean[] esteFinalizat.*. . esteFinalizat=new boolean [n+1]. System. // predecesor in drum static int[] d. static int[] p.m. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dijkstraOrientat.jmin=0. GRAFURI 4 1 2 1 1 3 2 1-->1 1-->2 1-->3 1-->4 1-->5 1-->6 dist=0 dist=3 dist=1 dist=2 dist=3 dist=4 drum: drum: drum: drum: drum: drum: 1 1 1 1 1 1 3 3 3 3 3 2 4 4 5 4 6 Algoritmul lui Dijkstra cu distant ¸e. static int n. m=(int)st.out"))). k. n=(int)st.in"))). // nod start int i. st.

jmin=j.k<=n.print("Nu exista drum!").println().close(). if(min>d[j]) { min=d[j].i++) for(j=1.print(nodSursa+"-->"+k+" dist="+d[k]+" drum: ").out.k<=m. } }//for j esteFinalizat[jmin]=true. cost[i][j]=(int)st.j++) // actualizez distantele nodurilor // O(n) { if(esteFinalizat[j]) continue. p[j]=jmin. for(k=1. } } }//for k for(k=1. DISTANT ¸ E MINIME ˆ IN GRAFURI d=new int[n+1].out. for(i=1.i<=n. j=(int)st.nextToken().out. } d[nodSursa]=0. else System.i<=n. if(d[k]<oo) drum(k).k++) { System.j) if(d[jmin]+cost[jmin][j]<d[j]) { d[j]=d[jmin]+cost[jmin][j]. // j este deja in arbore if(cost[jmin][j]<oo) // am muchia (jmin. st. 195 for(k=1.nval. for(i=1.6.j<=n.nval.j++) cost[i][j]=oo.j<=n.nval. st. } out.k++) { st.j<=n.k++) // O(n) { min=oo. }//main . i=(int)st.6. System.nextToken(). for(j=1.i++) d[i]=oo.k<=n. for(j=1.j++) // O(n) { if(esteFinalizat[j]) continue.nextToken().

GRAFURI static void drum(int k) // s --> .j. m=(int)st. . // matricea costurilor // distante de la nod catre arbore // predecesorul nodului in arbore // hd[i]=distanta din pozitia i din heap // hnod[i]= nodul din pozitia i din heap // pozh[i]=pozitia din heap a nodului i public static void main(String[]args) throws IOException { int i.nval. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dijkstraOrientat. // distante minime de la nodSursa class DijkstraOrientatHeap // pastrate in MinHeap ==> O(n log n) ==> OK !!! { static final int oo=0x7fffffff.in"))).nval. st. ˆ ın graf orientat import java.costa=0. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dijkstraOrientat.out"))). static int[][]w.io.nextToken(). n=(int)st. static int[]d.nextToken().k. } }//class /* 6 7 1-->1 dist=0 drum: 1 1 2 4 1-->2 dist=4 drum: 1 2 1 3 1 1-->3 dist=1 drum: 1 3 2 3 2 1-->4 dist=2 drum: 1 3 4 3 4 1 1-->5 dist=2147483647 drum: Nu exista drum! 5 4 1 1-->6 dist=4 drum: 1 3 4 6 5 6 3 4 6 2 */ Algoritmul lui Dijkstra cu heap..196 CAPITOLUL 6. st.cost. static int[] hnod. static int[]p. static int[] hd.nodSursa.. static int[] pozh. static int n.m.out. --> k { if(p[k]!=0) drum(p[k]). System.print(k+" ").*.

i<=n. } nodSursa=1.6. cost=(int)st. dijkstra(nodSursa).nval.aux.u<=n.i++) for(j=1. for(i=1.out.nextToken(). // q = noduri nefinalizate = dimensiune heap d[nods]=0.v.j<=n. hd[u]=d[u]=oo. for(u=1.print(nodSursa+"-->"+k+" Nu exista drum! "). DISTANT ¸ E MINIME ˆ IN GRAFURI w=new int[n+1][n+1].close(). p[u]=0.j++) w[i][j]=oo. hnod=new int[n+1]. d=new int [n+1]. st.6. st. drum(k). w[i][j]=cost.nextToken(). i=(int)st. 197 for(k=1.k<=n.q. for(k=1. } q=n.println(). } else System. j=(int)st.nval.k++) { if(d[k]<oo) { System. } out. p=new int [n+1]. System.k<=m.print(nodSursa+"-->"+k+" dist="+d[k]+" drum: ").nval.k++) { st.u++) { hnod[u]=pozh[u]=u.nextToken(). .out. hd=new int[n+1]. pozh=new int[n+1]. }//main static void dijkstra(int nods) { int u.out.

// 1 <---> q aux=hnod[1].hnod[v]). pozh[hnod[q]]=q.fiu1. for(v=1.println("graf neconex !!!"). hd[1]=hd[q].. while(fiu1<=q-1) { fiu=fiu1.) static int extragMin(int q) // in heap 1. fiu1=2*tata. urcInHeap(pozh[nods]).v++) if(w[u][hnod[v]]<oo) relax(u. } }// relax(. } }//dijkstra(. // noduri nefinalizate = in heap 1.fiu. if(u==-1) { System.q { // returnez valoarea minima (din varf!) si refac heap in jos int aux.. hd[pozh[v]]=d[v].v<=q.. hnod[1]=hnod[q].nod. urcInHeap(pozh[v]). aux=hd[1]..out.int v) { if(d[u]+w[u][v]<d[v]) { d[v]=d[u]+w[u][v]. } q--.v) // relax si refac heap //refac heap de sus in jos pana la q-1 .fiu2. pozh[hnod[1]]=1.. hnod[q]=aux. hd[q]=aux.) static void relax(int u. break. CAPITOLUL 6.q // cost finit ==> exista arc (u.tata.. p[v]=u. GRAFURI while(q>0) // la fiecare pas adaug un varf in arbore { u=extragMin(q). tata=1. fiu2=2*tata+1.198 hd[pozh[nods]]=0.

fiu=tata. tata=fiu. fiu=nodh. hnod[tata]=aux. hnod[fiu]=nod. tata=fiu/2.) static void urcInHeap(int nodh) { int aux.) static void drum(int k) // s --> . } pozh[nod]=fiu. tata=fiu/2. hd[fiu]=hd[tata].. if(hd[tata]<=hd[fiu]) break. while((tata>0)&&(hd[fiu]<hd[tata])) { pozh[hnod[tata]]=fiu.. aux=hd[fiu]. 199 . } return hnod[q]. System. } // urcInHeap(.6..print(k+" "). pozh[hnod[tata]]=fiu..tata.. hd[tata]=aux. hnod[fiu]=hnod[tata]. hd[tata]=aux. hnod[fiu]=hnod[tata].out. DISTANT ¸ E MINIME ˆ IN GRAFURI if(fiu2<=q-1) if(hd[fiu2]<hd[fiu1]) fiu=fiu2. pozh[hnod[fiu]]=tata.fiu. aux=hnod[fiu]. hd[fiu]=hd[tata]. --> k { if(p[k]!=0) drum(p[k]).. // hnod[1] a ajuns deja (!) in hnod[q] } // extragMin(. fiu2=2*tata+1. nod=hnod[nodh].nod.6. aux=hd[fiu]. fiu1=2*tata.

deci un nod pentru care cunoa¸ stem drumul minim de la s la el. deci complexitatea algoritmului este O(nm).6. Dac˘ a nodul w nu se afl˘ a deja ˆ ın coad˘ a. Fiecare nod poate fi scos din coad˘ a de cel mult n ori. Apoi. deoarece ˆ ın momentul ˆ ın care un nod a fost scos pentru a n + 1 oar˘ a din coad˘ a. el va rula la infinit ˆ ın cazul ciclurilor de cost negativ. deci nu putem ret ¸ine . vom folosi un vector care ret ¸ine distant ¸a minim˘ a g˘ asit˘ a la un moment dat de la s la celelalte noduri.200 } }//class /* 6 7 1 2 1 3 2 3 3 4 5 4 5 6 4 6 */ CAPITOLUL 6. ˆ ın ordine crescatoare a distant ¸ei fat ¸˘ a de aceasta. algoritmul scoate un nod v din coad˘ a ¸ si g˘ ase¸ ste toate nodurile w a c˘ aror distant ¸˘ a de la sursa la acestea poate fi optimizat˘ a prin folosirea nodului v . w) o muchie de cost negativ. Fie w un nod care nu apartine lui S ¸ si (v. care ˆ ın plus determin˘ a ¸ si existent ¸a ciclurilor de cost negativ care pot fi atinse pornind din nodul surs˘ a. care este init ¸ializat˘ a cu nodul s.ca ˆ ın cazul algoritmului Dijkstra aceast˘ a mult ¸ime S . De asemenea. GRAFURI 4 1 2 1 1 3 2 1-->1 1-->2 1-->3 1-->4 1-->5 1-->6 dist=0 drum: 1 dist=4 drum: 1 2 dist=1 drum: 1 3 dist=2 drum: 1 3 4 Nu exista drum! dist=4 drum: 1 3 4 6 6. . Fie v ultimul nod introdus ˆ ın mult ¸imea S . el este ad˘ augat acesteia. Obt ¸inem astfel contradict ¸ie cu faptul c˘ a vˆ arfurile de pe drumul minim de la s la v apart ¸in lui S .3 Algoritmul Belmann-Ford Dac˘ a graful cont ¸ine muchii de cost negativ. la fiecare pas. Aceasta problem˘ a se rezolv˘ a foarte usor. Ace¸ sti pa¸ si se repet˘ a pˆ ana cˆ ad coada devine vid˘ a. deoarece nu putem g˘ asi nodurile cele mai apropiate de surs˘ a. algoritmul lui Dijkstra nu mai funct ¸ioneaz˘ a corect. drum ce cont ¸ine numai noduri din S . algoritmul foloseste o coad˘ a Q. atunci ˆ ın mod sigur exist˘ a un ciclu negativ ˆ ın graful dat. Ca ¸ si ˆ ın cazul algoritmului Dijkstra. Asa cum este prezentat algoritmul. v ). Atunci drumul minim de la s la v va fi compus din drumul minim de la s la w urmat de muchia (w. S˘ a presupunem prin reducere la absurd c˘ a putem determina ¸ si ˆ ın acest caz multimea S folosit˘ a de algoritmul Dijkstra. Aceast˘ a problem˘ a este rezolvat˘ a de algoritmul Bellman-Ford.

// varfuri.io. // nod sursa.nextToken().v) // daca d[v]>d[u]+w[u][v] // OK=false // 5. for(i=1.v.// initializare ! for(k=1.j<=n.v) // 3. init // 2. costul arcului int u. static int[] d. OK=true // 4. // matricea costurilor // d[u]=dist(nods.m. return OK import java.6.u) // p[u]=predecesorul nodului u public static void main(String[] args) throws IOException { int i.i++) for(j=1. // infinit static int n. DISTANT ¸ E MINIME ˆ IN GRAFURI Algoritmul Belmann-Ford pentru grafuri neorientate 201 // drum scurt in graf neorientat cu costuri negative (dar fara ciclu negativ!) // Algoritm: 1.k. class BellmanFord { static final int oo=0x7fffffff. d=new int[n+1].k++) { . int nods=1.v) // relax(u.i<=n.j++) w[i][j]=oo.*.nval.j. repeta de n-1 ori // pentru fiecare arc (u. muchii static int[][] w. st. w=new int[n+1][n+1]. st. pentru fiecare muchie (u. cost.6.nval.k<=m. p=new int[n+1]. static int[] p. n=(int)st. m=(int)st. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("BellmanFordNeorientat.in"))).nextToken().

u++) { d[u]=oo.v++) if(w[u][v]<oo) relax(u.nextToken().v).v<=n.u++) for(v=u+1. st.v<=n.u<n. } }//main static void init(int s) { int u. for(u=1. }// init(.. } if(!cicluNegativ) for(k=1. w[j][i]=cost.println(). st.nextToken().v)=muchie if(d[u]<oo) // atentie !!! oo+ceva=??? if(d[v]>d[u]+w[u][v]) { cicluNegativ=true.nextToken(). d[s]=0.print(nods+"-->"+k+" dist="+d[k]+" drum: "). i=(int)st.u<n.202 CAPITOLUL 6. cost=(int)st. j=(int)st.u<=n.k++) { System.k<=n-1. drum(k).out.out. // // // // de n-1 ori !!! vectorii muchiilor erau mai buni ! lista de adiacenta era mai buna ! (u.k<=n.v)=arc(u-->v) { .int v) // (u. System..nval. GRAFURI st. // numai pentru graf neorientat } init(nods).nval. break.v)=muchie si u<v boolean cicluNegativ=false.k++) for(u=1.u++) for(v=u+1.v++) if(w[u][v]<oo) // (u. for(k=1.nval. for(u=1. w[i][j]=cost. } static void relax(int u.) p[u]=-1.

p[v]=u..i<=n. OK=true // 4. --> k { if(p[k]!=-1) drum(p[k]). for(i=1.i++) System.print(k+" ").. System.v) // daca d[v]>d[u]+w[u][v] . } static void afisv(int[] x) { int i. DISTANT ¸ E MINIME ˆ IN GRAFURI if(d[u]<oo) // oo+ceva ==> ??? if(d[u]+w[u][v]<d[v]) { d[v]=d[u]+w[u][v].v) // relax(u. init // 2.v) // 3. pentru fiecare arc (u.println().out. repeta de n-1 ori // pentru fiecare arc (u.print(x[i]+" ").6.out. } } static void drum(int k) // s --> . System. } }//class /* 6 7 1 2 1 3 2 3 3 4 4 5 5 6 4 6 */ 203 -3 1 2 1 1 -3 2 1-->1 1-->2 1-->3 1-->4 1-->5 1-->6 dist=0 drum: 1 dist=-3 drum: 1 2 dist=-1 drum: 1 2 3 dist=0 drum: 1 2 3 4 dist=1 drum: 1 2 3 4 5 dist=-2 drum: 1 2 3 4 5 6 Algoritmul Belmann-Ford pentru grafuri orientate // drumuri scurte in graf orientat cu costuri negative (dar fara ciclu negativ!) // Dijkstra nu functioneaza daca apar costuri negative ! // Algoritm: 1.out.6.

v++) if(w[u][v]<oo) relax(u. // d[u]=dist(nods. } init(nods). int nods=1.v<=n. // nod sursa. costul arcului int u. cost=(int)st. cost.nextToken().in"))). muchii static int[][] w. // matricea costurilor static int[] d.k<=n-1.nextToken(). for(u=1.u<=n.nval. // varfuri. st. return OK CAPITOLUL 6.204 // // OK=false 5.nextToken(). w[i][j]=cost.v<=n.i++) for(j=1.io.u++) for(v=1. // // // // de n-1 ori !!! vectorii arcelor erau mai buni ! lista de adiacenta era mai buna ! (u.nval.v++) . for(i=1.j<=n. st. m=(int)st. st.j. static int n.nval. j=(int)st.u++) for(v=1. for(k=1.v).k. i=(int)st. class BellmanFord { static final int oo=0x7fffffff. st. d=new int[n+1].nval.v)=arc boolean cicluNegativ=false. // p[u]=predecesorul nodului u public static void main(String[] args) throws IOException { int i. p=new int[n+1].u<=n.k++) for(u=1.k<=m.nval.v. n=(int)st.nextToken().*. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("BellmanFordNeorientat. w=new int[n+1][n+1]. GRAFURI import java.u) static int[] p.k++) { st.i<=n.j++) w[i][j]=oo. // initializare ! for(k=1.m.nextToken().

.. } }//main static void init(int s) { int u.6.print("Nu exista drum!").. DISTANT ¸ E MINIME ˆ IN GRAFURI 205 if(w[u][v]<oo) // (u.u<=n.) static void drum(int k) // s --> .v)=arc(u-->v) { if(d[u]<oo) // oo+ceva ==> ??? if(d[u]+w[u][v]<d[v]) { d[v]=d[u]+w[u][v]. } d[s]=0.out.i<=n.out. System. if(d[k]<oo) drum(k). else System. p[u]=-1. .k++) { System. }// drum(.u++) { d[u]=oo. }// init() static void relax(int u. p[v]=u. if(!cicluNegativ) for(k=1. System. } System.out. for(u=1.print(nods+"-->"+k+" dist="+d[k]+" drum: ").out. } }// relax(. break.) static void afisv(int[] x) { int i.println(cicluNegativ).i++) System.6.out.println()..k<=n.print(k+" ").int v) // (u. --> k { if(p[k]!=-1) drum(p[k]).. for(i=1.out.v)=arc if(d[u]<oo) // atentie !!! oo+ceva=??? if(d[v]>d[u]+w[u][v]) { cicluNegativ=true.print(x[i]+" ")..

t.u) static int[] p.k. pozitie in lista static int[] color.v) OBS: O(n+m) import java. class BellmanFordDAG { static final int oo=0x7fffffff.pozl. BLACK=1.m.out. // p[u]=predecesorul nodului u public static void main(String[] args) throws IOException { int i. costul arcului . // culoare static int[] lista. // varfuri. init(G.206 System.w.io. // grad interior static int[][] w.*. int nods=1.j. static final int WHITE=0. pentru toate nodurile u in ordine topologica pentru toate nodurile v adiacente lui u relax(u. GRAFURI -3 1 6 1 1 -3 2 -5 false 1-->1 1-->2 1-->3 1-->4 1-->5 1-->6 dist=0 drum: 1 dist=-4 drum: 1 3 2 dist=1 drum: 1 3 dist=2 drum: 1 3 4 dist=2147483647 drum: Nu exista drum! dist=4 drum: 1 3 4 6 Algoritmul Belmann-Ford pentru grafuri orientate aciclice // // // // // // // // Cele mai scurte drumuri in digraf (graf orientat ACICLIC) Dijkstra nu functioneaza daca apar costuri negative ! Algoritm: 1. // color[u]=BLACK ==> u in lista static int n. muchii. // nod sursa.println(). // lista static int[] gi. time. sortare topologica O(n+m) 2. cost.s) 3. // matricea costurilor static int[] d. // d[u]=dist(nods. } }//class /* 6 8 1 2 1 3 2 3 3 4 5 4 5 6 4 6 3 2 */ CAPITOLUL 6.

gi=new int[n+1].. for(k=1. } : ").. m=(int)st. i=(int)st. . for(v=1. for(i=1. afisv(d).i<=n.print("Lista init(nods).nval. d=new int[n+1]. color=new int[n+1]..nval.k<=m. System.nextToken().out.out.j<=n. for(i=1. st.print(k+" : "+d[k]+" . DISTANT ¸ E MINIME ˆ IN GRAFURI 207 int u.nextToken(). j=(int)st.6. else System. "). lista=new int[n+1]..i++) { u=lista[i].print("Distante : "). n=(int)st.nextToken(). st.v<=n.nval.nval. "). gi[j]++. w=new int[n+1][n+1]. } topoSort().i<=n. afisv(lista).6. // initializare ! for(k=1. p=new int[n+1].in"))).k++) { if(d[k]<oo) System.print(k+": oo .j++) w[i][j]=oo.v.out. st.k++) { st.i++) for(j=1.v++) if(w[u][v]<oo) relax(u.nextToken(). st. cost=(int)st. // lista de adiacenta era mai buna ! System. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("BellmanFordDAG.nextToken().nval.k<=n. w[i][j]=cost.v).out.

System. dar ..u++) { d[u]=oo.) static void drum(int k) // s --> .pozl. } static void topoSort() { int u.k<=n.. } d[s]=0. if(d[k]<oo) System. --> k { if(p[k]!=-1) drum(p[k]).) CAPITOLUL 6. !!! color[i]=WHITE. GRAFURI static void relax(int u. color[u]=BLACK.out... for(i=1.i++) // oricum era initializat implicit.i.u<=n.int v) // (u. } }//main static void init(int s) { int u. p[u]=-1. for(k=1.i<=n.k. for(u=1. } }// topoSort() static int nodgi0() // nod cu gradul interior zero . lista[pozl++]=u.v)=arc(u-->v) { if(d[u]<oo) // oo+ceva ==> ??? if(d[u]+w[u][v]<d[v]) { d[v]=d[u]+w[u][v]. }// init(.out. micsorezGrade(u)..k++) // pun cate un nod in lista { u=nodgi0()...println(). pozl=1.print(k+" ")..208 drum(k). p[v]=u. } }// relax(.

.v++) // lista de adiacenta este mai buna !!! if(color[v]==WHITE) if(w[u][v]<oo) gi[v]--. break. for(v=1.v<=n..6.. 1 2 : -4 .. }// nodgi0() static void micsorezGrade(int u) { int v. 1 3 2 3 : 1 .... 1 3 4 5: oo .i++) if(x[i]<oo) System.. for(i=1.) static void afisv(int[] x) { int i.out.} return nod. 1 3 4 6 . DISTANT ¸ E MINIME ˆ IN GRAFURI { 209 int v.6....... else System. }// micsorezGrade(.v<=n.print("oo ").println().nod=-1..v++) // coada cu prioritati (heap) este mai buna !!! if(color[v]==WHITE) if(gi[v]==0) {nod=v.out.) }//class /* 6 7 1 2 1 3 3 4 5 4 5 6 4 6 3 2 */ -3 1 1 1 -3 2 -5 Lista : 1 3 2 5 4 6 Distante : 0 -4 1 2 oo 4 1 : 0 .out. }// afisv(.. 1 3 4 : 2 .i<=n. System. for(v=1. 6 : 4 .print(x[i]+" ").

210

CAPITOLUL 6. GRAFURI

Capitolul 7

Fluxuri ˆ ın ret ¸ele
Printr-o ret ¸ea de transport ˆ ıntelegem un graf orientat care cont ¸ine dou˘ a vˆ arfuri speciale numite surs˘ a¸ si destinat ¸ie ¸ si care are proprietatea c˘ a orice nod al s˘ au face parte dintr-un drum de la surs˘ a la destinat ¸ie. Sursa unei ret ¸ele de transport se noteaz˘ a cu s, iar destinat ¸ia cu t. Exemplul clasic de ret ¸ea de transport este reprezentat de o serie de conducte dispuse ˆ ıntre un robinet s ¸ si un canal de scurgere t. Fiecare conduct˘ a (i, j ) este caracterizat˘ a prin cantitatea maxim˘ a de ap˘ a c(i, j ), care poate s˘ a treac˘ a la un moment dat prin conduct˘ a. Problema afl˘ arii fluxului maxim presupune determinarea cantit˘ a¸ tii maxime de ap˘ a care poate fi pompat˘ a prin robinetul s astfel ˆ ıncˆ at pe nici o conduct˘ a s˘ a nu se dep˘ a¸ seasc˘ a capacitatea maxim˘ a permis˘ a. Pentru a rezolva problema fluxului maxim se poate folosi algoritmul FordFulkerson. Dubl˘ am arcele grafului, introducˆ and un arc de sens contrar ˆ ıntre oricare dou˘ a noduri ˆ ıntre care exist˘ a un arc. Vom numi aceste arce introduse de algoritm arce speciale. ˆ In fiecare moment suma celor dou˘ a arce care unesc dou˘ a noduri i, j ale grafului este egal˘ a cu capacitatea maxim˘ a dintre cele doua vˆ arfuri c(i, j ). Vom nota cu a(i, j ) ponderea arcului care exist˘ a initial ˆ ın graf ¸ si cu a(j, i) ponderea arcului special. ˆ In fiecare moment al algoritmului, a(i, j ) va reprezenta cantitatea de ap˘ a care mai poate fi pompat˘ aˆ ıntre nodurile i ¸ si j , iar a(j, i) va reprezenta valoarea fluxului dintre i ¸ si j . Deci n ˆ orice moment avem a(i, j ) + a(j, i) = c(i, j ). Prin drum de cre¸ stere ˆ ıntr-o ret ¸ea de transport ˆ ıntelegem un drum de la s la t care cont ¸ine arce de cost pozitiv, care pornesc atˆ at din cele init ¸iale, cˆ at ¸ si din cele speciale. Valoarea unui drum de cre¸ stere este egal˘ a cu valoarea arcului de cost minim de pe drumul respectiv, numit arc critic. 211

212

CAPITOLUL 7. FLUXURI ˆ IN RET ¸ ELE

7.1

Algoritmul Edmonds-Karp

Algoritmul Edmonds-Karp este o implementare eficienta a algoritmului FordFulkerson. Acest algoritm, ˆ ın loc s˘ a g˘ aseasc˘ a un drum de crestere la ˆ ıntˆ amplare, va gasi la fiecare pas un drum de crestere cu num˘ ar minim de arce. Acest lucru se realizeaz˘ a foarte u¸ sor printr-o parcurgere ˆ ın l˘ a¸ time. // Ford-Fulkerson (Edmonds-Karp) import java.io.*; class FluxMaxim { static final int WHITE=0, GRAY=1, BLACK=2; static final int MAX_NODES=10; static final int oo=0x7fffffff; static int n, m; // nr noduri, nr arce static int[][] c=new int[MAX_NODES+1][MAX_NODES+1]; // capacitati static int[][] f=new int [MAX_NODES+1][MAX_NODES+1]; // flux static int[] color=new int[MAX_NODES+1]; // pentru bfs static int[] p=new int[MAX_NODES+1]; // predecesor (ptr. drum crestere) static int ic, sc; // inceput coada, sfarsit coada static int[] q=new int[MAX_NODES+2]; // coada public static void main(String[] args) throws IOException { int s,t,i,j,k,fluxm; // fluxm=flux_maxim StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("fluxMaxim.in"))); PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("fluxMaxim.out"))); st.nextToken(); n=(int)st.nval; st.nextToken(); m=(int)st.nval; st.nextToken(); s=(int)st.nval; st.nextToken(); t=(int)st.nval; for(k=1;k<=m;k++) { st.nextToken(); i=(int)st.nval; st.nextToken(); j=(int)st.nval; st.nextToken(); c[i][j]=(int)st.nval; } fluxm=fluxMax(s,t);

7.1. ALGORITMUL EDMONDS-KARP System.out.println("\nfluxMax("+s+","+t+") = "+fluxm+" :"); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) System.out.print(maxim(f[i][j],0)+"\t"); System.out.println(); } out.print(fluxm); out.close(); }// main() static int fluxMax(int s, int t) { int i, j, u, min, maxf = 0; for(i=1; i<=n; i++) for(j=1; j<=n; j++) f[i][j]=0;

213

// Cat timp exista drum de crestere a fluxului (in graful rezidual), // mareste fluxul pe drumul gasit while(bfs(s,t)) { // Determina cantitatea cu care se mareste fluxul min=oo; for(u=t; p[u]!=-1; u=p[u]) min=minim(min,c[p[u]][u]-f[p[u]][u]); // Mareste fluxul pe drumul gasit for(u=t; p[u]!=-1; u=p[u]) { f[p[u]][u]+=min; f[u][p[u]]-=min; // sau f[u][p[u]]=-f[p[u]][u]; } maxf += min; System.out.print("drum : "); drum(t); System.out.println(" min="+min+" maxf="+maxf+"\n"); }// while(...) // Nu mai exista drum de crestere a fluxului ==> Gata !!! System.out.println("Nu mai exista drum de crestere a fluxului !!!"); return maxf; }// fluxMax(...) static boolean bfs(int s, int t) // s=sursa t=destinatie { // System.out.println("bfs "+s+" "+t+" flux curent :"); // afism(f);

print(a[i][j]+"\t").j++) System. p[u]=-1. } // System. // coada vida incoada(s).print(u+" "). }// drum(. }//while return gasitt.. v++) if(color[v]==WHITE && ((c[u][v]-f[u][v])>0)) { incoada(v). break.. u++) { color[u]=WHITE. }// afism(.j.. v.println(). v<=n.v) este pozitiva for(v=1. p[s]=-1.) .) static void afism(int[][] a) { int i.} } if(gasitt) break. p[v]=u.out..) static void drum(int u) { if(p[u]!=-1) drum(p[u])..println().out.i++) { for(j=1.j<=n. boolean gasitt=false. // Cauta nodurile albe v adiacente cu nodul u si pune v in coada // cand capacitatea reziduala a arcului (u. u<=n.i<=n..out.out. FLUXURI ˆ IN RET ¸ ELE for(u=1. }// bfs(. while(ic!=sc) { u=dincoada(). } ic=sc=0. if(v==t) { gasitt=true. for(i=1. System. CAPITOLUL 7.214 int u. System.

7. return u. } static void incoada(int u) { q[sc++]=u. Un cuplaj maxim reprezint˘ a cuplajul pentru care cardinalul mult ¸imii muchiilor alese este cˆ at mai mare posibil. } }// class /* 6 10 1 6 1 2 16 1 3 13 2 3 4 2 4 12 3 2 10 3 5 14 4 3 9 4 6 20 5 4 7 5 6 4 */ drum : 1 2 4 6 min=12 maxf=12 drum : 1 3 5 6 min= 4 maxf=16 drum : 1 3 5 4 6 min= 7 maxf=23 Nu mai exista drum de crestere a fluxului !!! fluxMax(1. } static int dincoada() { int u=q[ic++]. int y) { return (x<y) ? x : y. int y) { return (x>y) ? x : y. CUPLAJ MAXIM 215 static int minim(int x. Un graf bipartit este un graf ale c˘ arui noduri pot fi partit ¸ionate ˆ ın dou˘ a submult ¸imi disjuncte astfel ˆ ıncˆ at s˘ a nu existe nici o muchie (arc) care s˘ a uneasc˘ a dou˘ a noduri aflate ˆ ın aceeai ¸ submult ¸ime. } static int maxim(int x.2. color[u]=GRAY. . color[u]=BLACK.6) = 23 : 0 12 11 0 0 0 0 0 0 12 0 0 0 0 0 0 11 0 0 0 0 0 0 19 0 0 0 7 0 4 0 0 0 0 0 0 7.2 Cuplaj maxim Un cuplaj ˆ ıntr-un graf poate fi definit ca o submult ¸ime a mult ¸imii muchiilor astfel ˆ ıncˆ at aceast˘ a muli ¸me s˘ a nu coni ¸n˘ a muchii adiacente.

Ei doresc s˘ a formeze perechi din care fac parte un cerculet ¸¸ si un p˘ atr˘ a¸ tel astfel ˆ ıncˆ at s˘ a se obt ¸ina cˆ at mai multe perechi. Vom stabili c˘ a fiecare dintre aceste arce va avea capacitatea 1. Apoi au ˆ ınceput s˘ a se joace cu p˘ atr˘ a¸ telele ¸ si cerculet ¸ele. vom putea determina cuplajul maxim. Pentru a obt ¸ine o ret ¸ea de transport avem nevoie de o surs˘ a¸ si o destinat ¸ie. De la surs˘ a vor pleca arce spre toate nodurile din prima dintre submult ¸imi (consider˘ am c˘ a prima submult ¸ime este cea care cont ¸ine noduri din care pleac˘ a arce ˆ ın graful bipartit). Datorit˘ a faptului c˘ a arcele adiacente sursei ¸ si destinat ¸iei au capacitatea 1. Dup˘ a construirea ret ¸elei de transport vom determina fluxul maxim ˆ ın ret ¸eaua obinut˘ a. Pentru grafurile bipartite cuplajul va consta din stabilirea unei ”corespondent ¸e” ˆ ıntre nodurile din prima mult ¸ime ¸ si nodurile din cea de-a doua. Aceste noduri vor fi introduse artificial ¸ si vor fi legate de nodurile grafului bipartit. fiecare nod va ap˘ area o singur˘ a dat˘ a ca extremitate a unui arc pentru care fluxul este nenul. Fiec˘ arui nod din prima mult ¸ime ˆ ıi va corespunde cel mult un nod din cea de-a doua. Au decis c˘ a un cerculet ¸ poate fi amplasat pe un p˘ atr˘ atel dac˘ a exist˘ a cel put ¸in o culoare care apare pe ambele. vom stabili o orientare a muchiilor (care vor deveni arce) astfel ˆ ıncˆ at ele s˘ a plece de la noduri aflate ˆ ın una dintre mult ¸imi ¸ si s˘ a ajung˘ a la noduri aflate ˆ ın cealalt˘ a mult ¸ime. Toate arcele care pleac˘ a de la surs˘ a¸ si toate arcele care ajung la destinat ¸ie vor avea capacitatea 1. Putem s˘ a rezolv˘ am aceast˘ a problem˘ a transformˆ and graful bipartit ˆ ıntr-o ret ¸ea de transport. ca fiind format din muchiile pe care exist˘ a fluxuri nenule. Num˘ arul muchiilor care fac parte din cuplajul maxim este limitat de cardinalul celei mai ”mici” dintre cele dou˘ a mult ¸imi disjuncte. Datele de intrare . dup˘ a determinarea fluxului maxim.216 CAPITOLUL 7. ˆ In cazul grafurilor orientate toate arcele vor porni de la noduri aflate ˆ ın una dintre cele dou˘ a submult ¸imi ¸ si vor ajunge la noduri aflate ˆ ın cealalt˘ a submult ¸ime. Ca urmare. FLUXURI ˆ IN RET ¸ ELE ˆ In cazul grafurilor neorientate toate muchiile vor uni perechi de noduri aflate ˆ ın submult ¸imi diferite. Ca exemplu consider˘ am urm˘ atoarea problem˘ a: Culori Doi elfi au pus pe o mas˘ a n p˘ atr˘ a¸ tele si m cerculet ¸e. Pentru sursa ¸ si destinat ¸ia introduse artificial sunt folosite uneori denumirile de surs˘ a virtual˘ a¸ si destinat ¸ie virtual˘ a. Pentru a determina cuplajul maxim ˆ ıntr-un graf bipartit va trebui s˘ a alegem cˆ at mai multe muchii. Dac˘ a graful bipartit este neorientat. deci aceast˘ a corespondent ¸˘ a trebuie s˘ a fie o funct ¸ie injectiv˘ a. f˘ ar˘ a ca printre muchiile alese s˘ a avem dou˘ a care au aceea¸ si extremitate. Unul a ales p˘ atr˘ a¸ telele ¸ si cel˘ alalt cerculet ¸ele ¸ si au desenat pe ele mai multe benzi colorate. iar la destinat ¸ie vor ajunge arce dinspre toate nodurile din cea de-a doua submult ¸ime.

Restrict ¸ii ¸ si preciz˘ ari • num˘ arul p˘ atr˘ a¸ telelor este cuprins ˆ ıntre 1 ¸ si 100. iar urm˘ atoarele b numere reprezint˘ a codurile celor b culori. P˘ atr˘ a¸ telele ¸ si cerculet ¸ele vor fi descrise ˆ ın ordinea dat˘ a de num˘ arul lor de ordine. • num˘ arul cerculet ¸elor este cuprins ˆ ıntre 1 ¸ si 100.2 4 \ 2 1 2 3 1 3 2 3 4 1 4 . Urm˘ atoarea linie cont ¸ine num˘ arul m al cerculet ¸elor. Pe fiecare dintre urmatoarele n linii sunt descrise benzile corespunzatoare unui p˘ atr˘ a¸ tel. iar urmatoarele b numere reprezint˘ a codurile celor b culori.TXT OUTPUT.2.TXT 3 2 1 1 1 1 1 1 2 3 2 / 1 3 s=0 . separate printr-un spat ¸iu. . Pe fiecare dintre urmatoarele m linii sunt descrise benzile corespunz˘ atoare unui cerculet ¸.n+m+1=t / / 3 / / / 4 / Timp de execut ¸ie: 0. 1 \ \ 2 --. . Exemplu: INPUT. . . • dac˘ a exist˘ a mai multe solut ¸ii trebuie determinat˘ a doar una dintre acestea. reprezentˆ and numerele de ordine ale unui p˘ atr˘ a¸ tel.txt cont ¸ine pe prima linie num˘ arul n al p˘ atr˘ a¸ telelor. • p˘ atr˘ a¸ telele sunt identificate prin numere cuprinse ˆ ıntre 1 ¸ si n.txt trebuie s˘ a cont ¸in˘ a pe prima linie num˘ arul k al perechilor formate.5 secunde/test Rezolvare: . Primul num˘ ar de pe o astfel de linie este num˘ arul b al benzilor. Numerele de pe o linie vor fi separate prin cˆ ate un spatiu. . • cerculet ¸ele sunt identificate prin numere cuprinse ˆ ıntre 1 ¸ s i m. respectiv cerc. . CUPLAJ MAXIM 217 Fi¸ sierul de intrare input.7. care formeaz˘ a o pereche. Primul num˘ ar de pe o astfel de linie este num˘ arul b al benzilor. Fiecare dintre urm˘ atoarele k va cont ¸ine cˆ ate dou˘ a numere. Datele de ie¸ sire Fi¸ sierul de ie¸ sire output. • num˘ arul benzilor colorate de pe cerculet ¸e ¸ si p˘ atr˘ a¸ tele este cuprins ˆ ıntre 1 ¸ si 10. • un p˘ atr˘ a¸ tel sau un cerc nu poate face parte din mai mult decˆ at o pereche.

.. for(k=1.k<=nc.*. static int[][] c.i<=m. cc[i][j]=true.io. } } st..n class CuplajMaximCulori // u=1. } } }// citire() .nval.. cp[i][j]=true. for(i=1. p.k<=nc.i++) { st. // cp = culoare patratel.nval. st.n ==> v=n+1. // capacitati..nval..nval. nc=(int)st. // predecesor. m. flux static boolean[][] cp..nextToken()..n+m+1)).nval..k++) { st. n=(int)st.n+m ==> v=1..i++) { st. // u=0 ==> v=1. sc.k. }// main() static void citire() throws IOException { int i. cp=new boolean[n+1][11].i<=n.nextToken(). static final int oo=0x7fffffff. q.n sau n+m+1(=t) static final int WHITE=0..nval. f.j.nextToken().2. cc=new boolean[m+1][11]. cc = culoare cerc static int[] color.nc. BLACK=2. ic.. FLUXURI ˆ IN RET ¸ ELE import java.nextToken(). cc.. m=(int)st.k++) { st.218 CAPITOLUL 7.nextToken(). for(i=1.nextToken(). j=(int)st.. j=(int)st. for(k=1.2. capacitati().. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("CuplajMaximCulori.n+m { // u=n+1. coada public static void main(String[] args) throws IOException { citire(). scrie(fluxMax(0.in"))). GRAY=1. nc=(int)st. static int n.

q=new int[n+m+2].j<=m.2.i<=n. int t) { int i. int t) . }// capacitati() static int fluxMax(int s. for(i=0.i<=n+m+1. }// while(. p=new int[n+m+2].j++) c[j+n][n+m+1]=1.p[u]!=-1. for(ic=1. // sau f[u][p[u]]=-f[p[u]][u].jc.j++) if(cc[j][ic]) c[i][j+n]=1.j<=m..i++) { c[0][i]=1.... for(u=t. c=new int[n+m+2][n+m+2].ic<=10.i++) for(j=0.j<=n+m+1.j. for(i=1.c[p[u]][u]-f[p[u]][u]).ic++) if(cp[i][ic]) for(j=1.u. for(u=t.) static boolean bfs(int s.) return maxf.u=p[u]) min=minim(min.ic. f[u][p[u]]-=min.u=p[u]) { f[p[u]][u]+=min.7. 219 while(bfs(s. CUPLAJ MAXIM static void capacitati() { int i.t)) { min=oo.j++) f[i][j]=0. color=new int[n+m+2].maxf=0. } for(j=1.j. } maxf+=min. f=new int[n+m+2][n+m+2].min. }// fluxMax(.p[u]!=-1.

v>=1.u++) {color[u]=WHITE. } } else if(u<=n) { for(v=n+1. for(u=0. // incoada(v). while(ic!=sc) { u=q[ic++].v<=n+m.} } if(gasitt) break. if(u==0) { for(v=1. }// bfs() .v--) if((color[v]==WHITE)&&((c[u][v]-f[u][v])>0)) { q[sc++]=v. color[u]=BLACK. if(v==t) {gasitt=true. // incoada(v). p[v]=u. p[u]=-1.v++) if((color[v]==WHITE)&&((c[u][v]-f[u][v])>0)) { q[sc++]=v. } } else { for(v=n+m+1. color[v]=GRAY.220 { CAPITOLUL 7. q[sc++]=s. p[v]=u. // s --> coada p[s]=-1.v++) if((color[v]==WHITE)&&((c[u][v]-f[u][v])>0)) { q[sc++]=v.u<=n+m+1.v<=n. FLUXURI ˆ IN RET ¸ ELE int u. color[s]=GRAY.} ic=sc=0. color[v]=GRAY. // din while ! } }// while() return gasitt. boolean gasitt=false. break. color[v]=GRAY. p[v]=u. // incoada(v). v.

) }// class . int y) { return (x>y) ? x : y. }// scrie(. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("CuplajMaximCulori.close().out"))).i++) if(f[0][i]>0) for(j=1.2. for (i=1.println(fluxm).i<=n. out. CUPLAJ MAXIM 221 static int minim(int x.j..j++) if(f[i][j+n]>0) { out. } out.7.println(i+" "+j).. } static void scrie(int fluxm) throws IOException { int i. int y) { return (x<y) ? x : y. break. } static int maxim(int x.j<=m.

FLUXURI ˆ IN RET ¸ ELE .222 CAPITOLUL 7.

http://www.E.fr/informatique/ [12] Cormen. E. L´ evy.. Editura Petrion. Algorithmes et Programmation... Ordinul de complexitate al unui algoritm.. Second Edition. 1995 [6] Atanasiu. Hopcroft.. Informatic˘ a . Universit˘ a¸ tii Bucure¸ sti. J. 1993 [5] Atanasiu. Rivest. A. Kalisz. Data strutures and algorithms. Teora.polytechnique.. 2002 [10] Cerchez. Libris... 2000 [13] Cormen. Paunoiu. Ed.. 1999 [8] Calude C. Bucure¸ sti. A. Teorie ¸ si aplicatii.Culegere de probleme pentru liceu. Polycopi´ e.H. 1992 [15] Erickson J.. Teoria algoritmilor. 1994 [14] Cristea. R. M.. 1995 [4] Apostol C.6.. Ro¸ sca I. Leiserson C. The Random Access Machine. Ed Agora... Hopcroft. E.E. Ed. o perspectiv˘ a C++. 1983 [2] Aho. Introducere ˆ ın Algoritmi. Algoritmi fundamentali. Ed... version 1. A.. Pseudo-Code Language. Prentice Hall.. Bucure¸ sti.. Ullman. V.. J..uiuc. R.L. Giumale. Addison Wesley.D.. Ullman.Bibliografie [1] Aho. 1987 [9] Cerchez. Polirom.J. T.H.1/1993 [7] ..manual pentru clasa a X-a.: Java for Students. http://w3. C.. Gh. J. Perr M..D. Gˆ arbacea I. 1974 [3] Andonie R. A..Bell D. Concursuri de informatic˘ a. Al. T. Ed.. 2000 [11] Cori.. J. Editura . Combinatorial Algorithms.L. Ro¸ sca V. Leiserson C. S ¸ erban. Rivest. Polirom. Ia¸ si. Ed. Ia¸ si. J. Limbajul C standard.. Gazeta de Informatic˘ a nr.edu.edu/~jeffe/ 223 . Ghilic-Micu B..... E... R. Informatic˘ a . Introducere ˆ ın programare.

Computer Press Agora. J. Vii ¸nescu. A Framework for Programming and Problem Solving. Joy. D. 2000.E. A.. Leu.M.. 1: Algoritmi fundamentali. [26] Niemeyer. I. Analiza ¸ si sinteza algoritmilor. Algoritmi de sortare. Ed. Editura Didactic˘ a ¸ si Pedagogic˘ a.. D. Puncte de articulat ¸ie ¸ si punt ¸i ˆ ın grafuri. P. Probleme de combinatoric˘ a¸ si teoria grafurilor. I. I... Matematic˘ a aplicat˘ aˆ ın tehnica de calcul. Smeureanu.. Tg.. All.. I. Peck J. Ed. [21] Knuth. O’Reilly... 1999 [25] Livovschi...224 [16] Flanagan. Ed. 1997.E. Programarea avansat˘ a a calculatoarelor personale.K. 2: Algoritmi seminumerici. O’Reilly. Steele. Microinformatica. 3: Sortare ¸ si c˘ autare. Programare dinamic˘ a . [27] Od˘ agescu. Mures.E. The Java Language Specification. Ed. Gazeta de Informatic˘ a nr. C.. 5/1993 [30] Rotariu E. Teora. S ¸ tef˘ anescu. 1999... 1998 [29] Popescu Anastasiu. Bucure¸ sti. A. Computer Lobris Agora. B. I. Negreanu L.. Java. D... D.Polirom.. 2004 [19] Giumale C. R. 1996 [31] Tomescu. Georgescu H. D. 2001.. PWS Publishing. G. 1986. Cluj. [22] Knuth. Limbajul Java.. Ed. 1981 [32] Tomescu.... V. 1996. vol. Java examples in a Nutshell. 1997. Programarea in JAVA.. 1997 [20] Gosling. Editura Didactic˘ a¸ si Pedagogic˘ a. [17] Flanagan. Ed. GInfo nr. L. Osborne. Introducere ˆ ın Analiza Algoritmilor.M. Arta programarii calculatoarelor. Teora.. I. Bucure¸ sti 1993 [28] Od˘ agescu. Arta programarii calculatoarelor. C. [24] Lambert. 1999 [34] ii ¸nescu. Exploring Java. Arta program˘ arii calculatoarelor. Ed. Java in a Nutshell. [23] Knuth. BIBLIOGRAFIE [18] Giumale. Bucure¸ sti. Addison Wesley. 1982 [33] Vaduva.. Proiectarea ¸ si analiza algoritmilor. vol. 1997. O’Reilly. Militar˘ a. Teora. vol.. D. Bucure¸ sti.teorie ¸ si aplicat ¸ii.. 15/4 2005 .. C˘ alinoiu S. Ed. Enciclopedic˘ a.. Metode ¸ si tehnici de programare.

The Benjamin/Cummings Publishing Company.. P.. 1993 [37] Weis.abordare modern˘ a.. [38] Winston.. GInfo. S. Gazeta de Informatic˘ a. Conceptul de algoritm . Ed. Addison-Wesley. Prentice Hall. M.Gazeta de Informatic˘ a.3 2003 [36] Vlada. California.. 1995.BIBLIOGRAFIE 225 [35] Vlada. 13/2. Narasimhan.. 1996 [39] Wirth N. Data structures and Algorithm Analysis. M.A. Editura Libris. M. 1991-2005 .H. Redwoods City.: On to JAVA. Grafuri neorientate ¸ si aplicat ¸ii. Algorithms + Data Structures = Programs. Inc 1976 [40] *** . Inc.

Sign up to vote on this title
UsefulNot useful