ALGORITMI SI STRUCTURI DE DATE ¸ Note de curs

(uz intern - draft v2.3)

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

ALGORITMICA (model virtual)

PROGRAMARE

Gandire algoritmica

Experienta (rezolvarea de probleme)

Cursul de Algoritmi ¸i structuri de date este util (¸i chiar necesar) pentru s s formarea competentelor ¸i abilit˘¸ilor unui bun programator sau profesor de ¸ s at informatic˘. Pentru a vedea care sunt aceste competente ¸i abilit˘¸i putem, de a ¸ s at
1 M. Vlada; E-Learning ¸i Software educational; Conferinta National˘ de ˆ a¸amˆnt Virtual, s ¸ ¸ ¸ a Inv˘t˘ a Bucure¸ti, 2003 s

exemplu, s˘ citim Programa pentru informatic˘ - Concursul national unic pentru a a ¸ ocuparea posturilor didactice declarate vacante ˆ ˆ a¸amˆntul preuniversitar.2 ın ınv˘t˘ a ˆ Intr-un fel, primul semestru al cursului Algoritmi ¸i structuri de date este s echivalent cu ceea ce se pred˘ la informatic˘ ˆ clasa a IX-a iar al doilea semestru cu a a ın clasa a X-a (specializarea: matematic˘-informatic˘, intensiv informatic˘). Diferenta a a a ¸ este dat˘ ˆ primul rˆnd de dificultatea problemelor abordate de c˘tre noi ˆ cadrul a ın a a ın acestui curs. Din aceast˘ cauz˘ vom avea ˆ vedere ¸i ce prevede Pograma ¸olar˘ a a ın s s a pentru clasa a IX-a, Profil real, Specializarea: Matematic˘-informatic˘, intensiv a a informatic˘. De asemenea, merit˘ s˘ vedem ce p˘reri au cei care au terminat de a a a a curˆnd o facultate cu un profil de informatic˘ ¸i care au un ˆ a a s ınceput de carier˘ a reu¸it. Vom ˆ ¸elege de ce acest curs este orientat pe rezolvarea de probleme. s ınt Alegerea limbajului Java pentru prezentarea implement˘rilor algoritmilor a a fost f˘cut˘ din cˆteva considerente. Java verific˘ validitatea indicilor tablourilor a a a a (programele nu se pot termina printr-o violare de memorie sau eroare de sistem). Java realizeaz˘ gestiunea automat˘ a memoriei (recupereaz˘ automat memoria a a a care nu mai este necesar˘ programului) ceea ce simplific˘ scrierea programelor a a ¸i permite programatorului s˘ se concentreze asupra esentei algoritmului. Exist˘ s a ¸ a documentatie pe internet. Compilatorul de Java este gratuit. Un program scris ˆ ¸ ın Java poate fi executat pe orice calculator (indiferent de arhitectur˘ sau sistem de a operare). Studentii nu sunt obligati s˘ realizeze implement˘rile algoritmilor ˆ Java; ¸ ¸ a a ın ei pot folosi Pascal sau C/C++. Algoritmii prezentati ˆ curs sunt descri¸i ˆ limbaj ¸ ın s ın natural sau ˆ limbaj algoritmic iar implement˘rile sunt ˆ limbajul de programare ın a ın Java. Java este un limbaj orientat-obiect, dar noi vom utiliza foarte putin aceast˘ ¸ a particularitate. Sunt prezentate toate elementele limbajului de programare Java necesare pentru acest curs dar ecesta nu este un curs de programare ˆ Java. ın Cuno¸tintele minimale acceptate la sfˆr¸itul cursului rezult˘ din Legea nr. s ¸ as a 288 din 24 iunie 2004 privind organizarea studiilor universitare ¸i, de exemplu, s din Ghidul calit˘¸ii ˆ ˆ a¸amˆntul superior3 . Aici se precizeaz˘ faptul c˘ diploma at ın ınv˘t˘ a a a de licent˘ se acord˘ unui absolvent al programului de studii care: demonstreaz˘ ¸a a a acumulare de cuno¸tinte ¸i capacitatea de a ˆ ¸elege aspecte din domeniul s ¸ s ınt de studii ˆ care s-a format, poate folosi atˆt cuno¸tintele acumulate precum ın a s ¸ ¸i capacitatea lui de ˆ ¸elegere a fenomenelor printr-o abordare profesional˘ s ınt a ˆ domeniul de activitate, a acumulat competente necesare demonstr˘rii, ın ¸ a argument˘rii ¸i rezolv˘rii problemelor din domeniul de studii considerat, ¸i-a a s a s dezvoltat deprinderi de ˆ a¸are necesare procesului de educatie continu˘. ınv˘t ¸ a

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

2 Aprobat˘ a

Cuprins
1 Notiuni fundamentale ¸ 1.1 Programe ciudate . . . . . . . . . . . . . 1.1.1 Un program ciudat ˆ Pascal . . ın 1.1.2 Un program ciudat ˆ C++ . . . ın 1.1.3 Un program ciudat ˆ Java . . . ın 1.1.4 Structura unui program Java . . 1.2 Conversii ale datelor numerice . . . . . 1.2.1 Conversia din baza 10 ˆ baza 2 . ın 1.2.2 Conversia din baza 2 ˆ baza 10 . ın 1.2.3 Conversii ˆ ıntre bazele 2 ¸i 2r . . s 2 Structuri de date 2.1 Date ¸i structuri de date . . . . . . . . s 2.1.1 Date . . . . . . . . . . . . . . . 2.1.2 Structuri de date . . . . . . . . 2.2 Structuri ¸i tipuri de date abstracte . . s 2.2.1 Structuri de date abstracte . . 2.2.2 Tipuri de date abstracte . . . . 2.3 Structuri de date elementare . . . . . 2.3.1 Liste . . . . . . . . . . . . . . . 2.3.2 Stive ¸i cozi . . . . . . . . . . . s 2.3.3 Grafuri . . . . . . . . . . . . . 2.3.4 Arbori binari . . . . . . . . . . 2.3.5 Heap-uri . . . . . . . . . . . . . 2.3.6 Structuri de multimi disjuncte ¸ 3 Algoritmi 3.1 Etape ˆ rezolvarea problemelor . ın 3.2 Algoritmi . . . . . . . . . . . . . 3.2.1 Ce este un algoritm? . . . 3.2.2 Propriet˘¸ile algoritmilor at 3.2.3 Tipuri de prelucr˘ri . . . a v . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 1 2 3 4 5 5 6 6 7 7 7 9 10 10 10 11 11 12 13 14 15 16 17 17 18 18 20 20

3.3

3.4

3.5

Descrierea algoritmilor . . . . . . . . . . . . . . . 3.3.1 Limbaj natural . . . . . . . . . . . . . . . 3.3.2 Scheme logice . . . . . . . . . . . . . . . . 3.3.3 Pseudocod . . . . . . . . . . . . . . . . . Limbaj algoritmic . . . . . . . . . . . . . . . . . . 3.4.1 Declararea datelor . . . . . . . . . . . . . 3.4.2 Operatii de intrare/ie¸ire . . . . . . . . . ¸ s 3.4.3 Prelucr˘ri liniare . . . . . . . . . . . . . . a 3.4.4 Prelucr˘ri alternative . . . . . . . . . . . a 3.4.5 Prelucr˘ri repetitive . . . . . . . . . . . . a 3.4.6 Subalgoritm . . . . . . . . . . . . . . . . . 3.4.7 Probleme rezolvate . . . . . . . . . . . . . 3.4.8 Probleme propuse . . . . . . . . . . . . . Instructiuni corespondente limbajului algoritmic ¸ 3.5.1 Declararea datelor . . . . . . . . . . . . . 3.5.2 Operatii de intrare/ie¸ire . . . . . . . . . ¸ s 3.5.3 Prelucr˘ri liniare . . . . . . . . . . . . . . a 3.5.4 Prelucr˘ri alternative . . . . . . . . . . . a 3.5.5 Prelucr˘ri repetitive . . . . . . . . . . . . a 3.5.6 Subprograme . . . . . . . . . . . . . . . . 3.5.7 Probleme rezolvate . . . . . . . . . . . . . 3.5.8 Probleme propuse . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

20 21 22 22 23 23 23 24 24 25 26 27 30 32 32 34 35 35 35 36 37 52 55 55 57 57 58 58 60 61 62 62 62 62 63 64 66 67 67 69

4 Analiza complexit˘¸ii algoritmilor at 4.1 Scopul analizei complexit˘¸ii . . . . . . . . . . . . . . . . at 4.1.1 Complexitatea spatiu . . . . . . . . . . . . . . . ¸ 4.1.2 Complexitatea timp . . . . . . . . . . . . . . . . 4.2 Notatia asimptotic˘ . . . . . . . . . . . . . . . . . . . . ¸ a 4.2.1 Definire ¸i propriet˘¸i . . . . . . . . . . . . . . . s at 4.2.2 Clase de complexitate . . . . . . . . . . . . . . . 4.2.3 Cazul mediu ¸i cazul cel mai defavorabil . . . . . s 4.2.4 Analiza asimptotic˘ a structurilor fundamentale a 4.3 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Calcularea maximului . . . . . . . . . . . . . . . 4.3.2 Sortarea prin selectia maximului . . . . . . . . . ¸ 4.3.3 Sortarea prin insertie . . . . . . . . . . . . . . . . ¸ 4.3.4 Sortarea rapid˘ (quicksort) . . . . . . . . . . . . a 4.3.5 Problema celebrit˘¸ii . . . . . . . . . . . . . . . . at 4.4 Probleme . . . . . . . . . . . . . . . . . . . . . . . . . . 4.4.1 Probleme rezolvate . . . . . . . . . . . . . . . . . 4.4.2 Probleme propuse . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

5 Recursivitate 5.1 Functii recursive . . . . . . . ¸ 5.1.1 Functii numerice . . . ¸ 5.1.2 Functia lui Ackerman ¸ 5.1.3 Recursii imbricate . . 5.2 Proceduri recursive . . . . . .

. . . . .

. . . . .

. . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71 71 71 74 74 75 77 77 78 78 80 80 81 82 84 87 93 93 93 94 95 95 95 96 97 97 98 98 98 100 100 100 101 101 102 104 104 105 106 107 107 107

6 Analiza algoritmilor recursivi 6.1 Relatii de recurent˘ . . . . . . . . ¸ ¸a 6.1.1 Ecuatia caracteristic˘ . . . ¸ a 6.1.2 Solutia general˘ . . . . . . ¸ a 6.2 Ecuatii recurente neomogene . . . ¸ 6.2.1 O form˘ simpl˘ . . . . . . . a a 6.2.2 O form˘ mai general˘ . . . a a 6.2.3 Teorema master . . . . . . 6.2.4 Transformarea recurentelor ¸ 6.3 Probleme rezolvate . . . . . . . . .

7 Algoritmi elementari 7.1 Operatii cu numere . . . . . . . . . . . . . . . . ¸ 7.1.1 Minim ¸i maxim . . . . . . . . . . . . . s 7.1.2 Divizori . . . . . . . . . . . . . . . . . . 7.1.3 Numere prime . . . . . . . . . . . . . . 7.2 Algoritmul lui Euclid . . . . . . . . . . . . . . . 7.2.1 Algoritmul clasic . . . . . . . . . . . . . 7.2.2 Algoritmul lui Euclid extins . . . . . . . 7.3 Operatii cu polinoame . . . . . . . . . . . . . . ¸ 7.3.1 Adunarea a dou˘ polinoame . . . . . . . a 7.3.2 ˆ Inmultirea a dou˘ polinoame . . . . . . ¸ a 7.3.3 Calculul valorii unui polinom . . . . . . 7.3.4 Calculul derivatelor unui polinom . . . . 7.4 Operatii cu multimi . . . . . . . . . . . . . . . ¸ ¸ 7.4.1 Apartenenta la multime . . . . . . . . . ¸ ¸ 7.4.2 Diferenta a dou˘ multimi . . . . . . . . ¸ a ¸ 7.4.3 Reuniunea ¸i intersectia a dou˘ multimi s ¸ a ¸ 7.4.4 Produsul cartezian a dou˘ multimi . . . a ¸ 7.4.5 Generarea submultimilor unei multimi . ¸ ¸ 7.5 Operatii cu numere ˆ ¸ ıntregi mari . . . . . . . . . 7.5.1 Adunarea ¸i sc˘derea . . . . . . . . . . . s a 7.5.2 Inmultirea ¸i ˆ artirea . . . . . . . . . ¸ s ımp˘ 7.5.3 Puterea . . . . . . . . . . . . . . . . . . 7.6 Operatii cu matrice . . . . . . . . . . . . . . . . ¸ 7.6.1 ˆ Inmultirea . . . . . . . . . . . . . . . . . ¸ 7.6.2 Inversa unei matrice . . . . . . . . . . .

8 Algoritmi combinatoriali 8.1 Principiul includerii ¸i al excluderii ¸i aplicatii s s ¸ 8.1.1 Principiul includerii ¸i al excluderii . . s 8.1.2 Num˘rul functiilor surjective . . . . . a ¸ 8.1.3 Num˘rul permut˘rilor f˘r˘ puncte fixe a a aa 8.2 Principiul cutiei lui Dirichlet ¸i aplicatii . . . s ¸ 8.2.1 Problema subsecventei . . . . . . . . . ¸ 8.2.2 Problema sub¸irurilor strict monotone s 8.3 Numere remarcabile . . . . . . . . . . . . . . 8.3.1 Numerele lui Fibonacci . . . . . . . . 8.3.2 Numerele lui Catalan . . . . . . . . . 8.4 Descompunerea ˆ factori primi . . . . . . . . ın 8.4.1 Functia lui Euler . . . . . . . . . . . . ¸ 8.4.2 Num˘rul divizorilor . . . . . . . . . . a 8.4.3 Suma divizorilor . . . . . . . . . . . . 8.5 Partitia numerelor . . . . . . . . . . . . . . . ¸ 8.5.1 Partitia lui n ˆ exact k termeni . . . ¸ ın 8.5.2 Partitia lui n ˆ cel mult k termeni . . ¸ ın 8.5.3 Partitii multiplicative . . . . . . . . . ¸ 8.6 Partitia multimilor . . . . . . . . . . . . . . . ¸ ¸ 8.7 Probleme rezolvate . . . . . . . . . . . . . . . 9 Algoritmi de c˘utare a 9.1 Problema c˘ut˘rii . . a a 9.2 C˘utarea secvential˘ a ¸ a 9.3 C˘utare binar˘ . . . a a 9.4 Inserare ˆ tabel˘ . . ın a 9.5 Dispersia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

109 109 109 110 112 113 113 114 114 115 116 119 119 121 121 122 122 123 123 123 124 127 127 127 129 130 131 133 133 134 139 139 141 142 143 145 145 151 155 157 159

10 Algoritmi elementari de sortare 10.1 Introducere . . . . . . . . . . . . . . . . . . . 10.2 Sortare prin selectie . . . . . . . . . . . . . . ¸ 10.3 Sortare prin insertie . . . . . . . . . . . . . . ¸ 10.3.1 Insertie direct˘ . . . . . . . . . . . . . ¸ a 10.3.2 Insertie binar˘ . . . . . . . . . . . . . ¸ a 10.4 Sortare prin interschimbare . . . . . . . . . . 10.5 Sortare prin mic¸orarea incrementului - shell s 11 Liste 11.1 Liste liniare . . . . . . . . . . . 11.2 Cozi . . . . . . . . . . . . . . . 11.3 Stive . . . . . . . . . . . . . . . 11.4 Evaluarea expresiilor aritmetice 11.5 Operatii asupra listelor . . . . . ¸ . . . . . . . . . . . . . . . . . . prefixate . . . . . . . . . . . . . . . .

12 Algoritmi divide et impera 12.1 Tehnica divide et impera . . . . . . . . . . . 12.2 Ordinul de complexitate . . . . . . . . . . . 12.3 Exemple . . . . . . . . . . . . . . . . . . . . 12.3.1 Sortare prin partitionare - quicksort 12.3.2 Sortare prin interclasare - MergeSort 12.3.3 Placa cu g˘uri . . . . . . . . . . . . a 12.3.4 Turnurile din Hanoi . . . . . . . . . 12.3.5 ˆ Injum˘t˘¸ire repetat˘ . . . . . . . . a at a

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

163 163 164 165 165 166 168 169 173 177 177 180 180 185 191 197 204 208 213 213 214 215 215 216 217 217 217 218 219 219 220 220 220 228 230 241 246 250 255 260

13 Algoritmi BFS-Lee 13.1 Prezentare general˘ . . . . . . . . . . . . . . . . a 13.2 Probleme rezolvate . . . . . . . . . . . . . . . . 13.2.1 Romeo ¸i Julieta - OJI2004 clasa a X-a s 13.2.2 Sudest - OJI2006 clasa a X-a . . . . . . 13.2.3 Muzeu - ONI2003 clasa a X-a . . . . . . 13.2.4 P˘ianjen ONI2005 clasa a X-a . . . . . a 13.2.5 Algoritmul Edmonds-Karp . . . . . . . 13.2.6 Cuplaj maxim . . . . . . . . . . . . . . 14 Metoda optimului local - greedy 14.1 Metoda greedy . . . . . . . . . . . . . . . . . 14.2 Algoritmi greedy . . . . . . . . . . . . . . . . 14.3 Exemple . . . . . . . . . . . . . . . . . . . . . 14.3.1 Problema continu˘ a rucsacului . . . . a 14.3.2 Problema plas˘rii textelor pe o band˘ a a 14.3.3 Problema plas˘rii textelor pe m benzi a 14.3.4 Maximizarea unei sume de produse . . 14.3.5 Problema statiilor . . . . . . . . . . . ¸ 14.3.6 Problema cutiilor . . . . . . . . . . . . 14.3.7 Problema sub¸irurilor . . . . . . . . . s 14.3.8 Problema intervalelor disjuncte . . . . 14.3.9 Problema alegerii taxelor . . . . . . . 14.3.10 Problema acoperirii intervalelor . . . . 14.3.11 Algoritmul lui Prim . . . . . . . . . . 14.3.12 Algoritmul lui Kruskal . . . . . . . . . 14.3.13 Algoritmul lui Dijkstra . . . . . . . . . 14.3.14 Urgenta - OJI2002 cls 11 . . . . . . . ¸ 14.3.15 Reactivi - OJI2004 cls 9 . . . . . . . . 14.3.16 Pal - ONI2005 cls 9 . . . . . . . . . . 14.3.17 Sant - ONI2006 cls 9 . . . . . . . . . . ¸ ¸ 14.3.18 Cezar - OJI2007 cls 11 . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

15 Metoda backtracking 15.1 Generarea produsului cartezian . . . . . . . . . . . 15.1.1 Generarea iterativ˘ a produsului cartezian . a 15.1.2 Generarea recursiv˘ a produsului cartezian a 15.2 Metoda bactracking . . . . . . . . . . . . . . . . . 15.2.1 Bactracking iterativ . . . . . . . . . . . . . 15.2.2 Backtracking recursiv . . . . . . . . . . . . 15.3 Probleme rezolvate . . . . . . . . . . . . . . . . . . 15.3.1 Generarea aranjamentelor . . . . . . . . . . 15.3.2 Generarea combin˘rilor . . . . . . . . . . . a 15.3.3 Problema reginelor pe tabla de ¸ah . . . . . s 15.3.4 Turneul calului pe tabla de ¸ah . . . . . . . s 15.3.5 Problema color˘rii h˘rtilor . . . . . . . . . a a¸ 15.3.6 Problema vecinilor . . . . . . . . . . . . . . 15.3.7 Problema labirintului . . . . . . . . . . . . 15.3.8 Generarea partitiilor unui num˘r natural . ¸ a 15.3.9 Problema parantezelor . . . . . . . . . . . . 15.3.10 Algoritmul DFS de parcurgere a grafurilor . 15.3.11 Determinarea componentelor conexe . . . . 15.3.12 Determinarea componentelor tare conexe . 15.3.13 Sortare topologic˘ . . . . . . . . . . . . . . a 15.3.14 Determinarea nodurilor de separare . . . . 15.3.15 Determinarea muchiilor de rupere . . . . . 15.3.16 Determinarea componentelor biconexe . . . 15.3.17 Triangulatii - OJI2002 clasa a X-a . . . . . ¸ 15.3.18 Partitie - ONI2003 clasa a X-a . . . . . . . ¸ 15.3.19 Scufita - ONI2003 clasa a X-a . . . . . . . . ¸ 16 Programare dinamic˘ a 16.1 Prezentare general˘ . . . . . . . . . . . . . . . a 16.2 Probleme rezolvate . . . . . . . . . . . . . . . 16.2.1 Inmultirea optimal˘ a matricelor . . . ¸ a 16.2.2 Sub¸ir cresc˘tor maximal . . . . . . . s a 16.2.3 Sum˘ maxim˘ ˆ triunghi de numere . a a ın 16.2.4 Sub¸ir comun maximal . . . . . . . . . s 16.2.5 Distanta minim˘ de editare . . . . . . ¸ a 16.2.6 Problema rucsacului (0 − 1) . . . . . . 16.2.7 Problema schimbului monetar . . . . . 16.2.8 Problema travers˘rii matricei . . . . . a 16.2.9 Problema segment˘rii vergelei . . . . . a 16.2.10 Triangularizarea poligoanelor convexe 16.2.11 Algoritmul Roy-Floyd-Warshall . . . . 16.2.12 Oracolul decide - ONI2001 cls 10 . . . 16.2.13 Pav˘ri - ONI2001 clasa a X-a . . . . . a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

269 269 269 274 277 279 279 280 280 284 294 296 298 301 303 306 310 311 313 314 316 320 321 323 326 330 336 343 343 345 345 348 352 353 360 366 367 368 370 373 374 375 381

. . .1 Prezentare general˘ . . . . . . . . . . . . . . . . . . . . . . 493 19. . .1.2 Exemple . . . . . . . .1. . . . . . . . . . . . . .2 Antena .1 Algoritmul Belmann-Ford pentru grafuri neorientate 20. . . . .1 Seceta . . .7 Dreptunghi minim de acoperire a punctelor .ONI2005 clasa a IX-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6. .1 Prezentare general˘ . . . . . . . . . . 17 Potrivirea ¸irurilor s 17. . . . . . . . . . . . . .OJI2005 clasa a X-a a 16. . .2 Scanarea Craham . 18. . .9. . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . 19 Teoria jocurilor 493 19. .3 Mo¸ia lui P˘cal˘ . . . . .9. . . . . . . . . . 20. . . . . . . . . . . . . . . . . . . . . .2. . . . . .8 Cerc minim de acoperire a punctelor . . . . . . . . . . . .4 Partitie . . . . . . . . . . . . .2. .6. . . . . . . . 18 Geometrie computational˘ ¸ a 18. . . . .2 Algoritmul Belmann-Ford . . . . . . .ONI2006 baraj . . . . . . 17. . . . . . . . . . . 383 387 393 401 412 416 429 429 431 436 436 438 445 445 446 446 446 447 448 448 452 462 463 463 463 477 482 483 487 . . . . . . . . . 18. . . 16. .2 Exemple . . . . . . . . 17. . . . . . . . . . . . . . . . 16. . . . . .5 Triunghi . . . . 18. . . . . . Inf˘s a a 18. . . . . .2 Alg Belmann-Ford pentru grafuri orientate . . . . . . . . . .1 Determinarea orient˘rii . . . . . . . ¸ 16. . . 18. . . . .15 Aliniere ONI2002 clasa a X-a . . . . . . . . . . . . . . . .1 Circular . . . . . 17. . . . . .ONI2003 cls 10 . . . . . .19 Suma . . .17 L˘custa . . . .2 Cifru . . . . . . . . . . .5 Pozitia unui punct fat˘ de un poligon concav . . . . . . . Runda 6 . . . . . . . . . . . . . . . . . . .ONI2007 cls 9 . . 493 20 Alti algoritmi ¸ 20. . .2 Un algoritm eficient .ONI2005 clasa a X-a . .1 ˆ Impachetarea Jarvis . . . . .ONI2006 baraj . . . . .3. . . . . . . . . . 18. . a 20. . 493 a 19. . . . . . . . . . . . . . .2. . . . . . . . .Campion 2003-2004 17. . . . . . . . . .1 Jocul NIM . . . . . .2. . . . . . . . . . . . . 20.3 Aria poligoanelor convexe . . .3.2 Testarea convexit˘¸ii poligoanelor . . . . . . . . . . . . . . . ¸ 18. .1 Un algoritm ineficient . .1 Secvent˘ de sum˘ maxim˘ . . . . . . . . . 18. . . . . . .18 Avere ONI2005 cls 10 . . .14 Balanta ONI2002 clasa a X-a . . . . . . . . . . . . . . . .OJI2004 clasa a XI-a s a a 18. . . . . . . . . . . . . . . . ¸ ¸a 18. . . . 18.KMP . . . . . . . . .4 Pozitia unui punct fat˘ de un poligon convex . . .ONI2005 cls 10 . . . . . . . . 495 495 495 495 495 495 498 . . . . . . . . . . . . . .xi 16. . . . . . . . . . . 16. . . .16 Munte . . . . . . . . . . . . . . . . . . . .9. . . . .2. . . . . . . . . .6 ˆ a¸ur˘toarea convex˘ . a 18. . . . . . . . . . . ¸a a a 20. . . . . 18. . . at 18.2. . . . . . . . . . . . . . . . . . . . .9. . .9. .2. . . . . . . .1. . . . . . . . . . . . . . . .9 Probleme rezolvate . . . .1.3 Probleme rezolvate . . . . . ¸ ¸a 18. . . . . . . . . . . . .

xii 20.2. . 501 . . . .3 Alg Belmann-Ford pentru grafuri orientate aciclice .

ın 1. trebuie s˘ ¸tim ˆ primul rˆnd cum se reprezint˘ a s ın a a numerele ˆ memoria calculatorului.1 Programe ciudate Dac˘ nu suntem atenti la valorile pe care le pot lua variabilele cu care lucr˘m. a ın a a var x.Capitolul 1 Notiuni fundamentale ¸ ˆ general. C/C++ ¸i Java. De¸i ne a¸teptam s˘ apar˘ ca rezultat 50. Altfel putem avea surprize ciudate.z). write(x. surpriza este c˘ pe ecran apare s s a a a 20000+30000=-15536 1 .’+’. Nu are prea mare a ın important˘ dac˘ este Java.y. Prezent˘m astfel de situatii ˆ Pascal.000. C/C++. a ¸ a putem obtine rezultate gre¸ite chiar dac˘ modalitatea de rezolvare a problemei este ¸ s a corect˘.000. Pascal sau alt limbaj de programare. Noi vom prezenta implement˘rile algoritmilor ˆ Java. studentii din anul I au cuno¸tinte de programare ˆ Pascal sau In ¸ s ¸ ın C/C++. y:=30000.1. END. z:=x+y.z:integer.1 Un program ciudat ˆ Pascal ın Iat˘ un program Pascal ˆ care dorim s˘ calcul˘m suma 20. a a ¸ ın s 1.y.’=’. BEGIN x:=20000.000 + 30. Oricare ¸a a ar fi limbajul de programare.

h> int main() { int x. x=20000. } Iat˘ un program ˆ C++ ˆ care dorim s˘ calcul˘m suma 20. z=x+y.1: Un program ciudat ˆ Pascal ın 1.y.000.2 Un program ciudat ˆ C++ ın #include<iostream. y=30000.000. NOTIUNI FUNDAMENTALE ¸ Figura 1.000 + 30. return 0.2 CAPITOLUL 1. cout << x <<"+"<<y<<"="<<z. a ın ın a a De¸i ne a¸teptam s˘ apar˘ ca rezultat 50.2: Un program ciudat ˆ C++ ın .z.1. surpriza este c˘ pe ecran apare s s a a a 20000+30000=-15536 Figura 1.

1.1. PROGRAME CIUDATE

3

1.1.3

Un program ciudat ˆ Java ın

class Ciudat { public static void main(String args[]) { int x,y,z; x=200000; y=300000; z=x*y; System.out.println(x+"*"+y+"="+z); } }

Iat˘ un program ˆ C++ ˆ care dorim s˘ calcul˘m suma 200.000 ∗ 300.000. a ın ın a a

De¸i ne a¸teptam s˘ apar˘ ca rezultat 60.000.000.000, surpriza este c˘ pe ecran s s a a a apare 200000*300000=-129542144

Figura 1.3: Un program ciudat ˆ Java ın Calculul cu numerele ˆ ıntregi este relativ simplu. Calculele sunt f˘cute ˆ a ıntr-o aritmetic˘ modulo N = 2n unde n este num˘rul de biti ai cuvˆntului ma¸in˘. a a ¸ a s a Exist˘ ma¸ini pe 16, 32 ¸i 64 biti pentru care N este aproximativ egal cu 6 × 104 , a s s ¸ 4 × 109 ¸i respectiv 2 × 1019 . s Se pot reprezenta ¸i numerele ˆ s ıntregi negative. Modul curent de reprezentare este ˆ complement fat˘ de 2. ˆ notatie binar˘, bitul cel mai semnificativ este ın ¸a In ¸ a bitul de semn. Numerele negative sunt cuprinse ˆ ıntre −2n−1 ¸i 2n−1 − 1. s Atunci cˆnd valorile obtinute din calcule dep˘¸esc marginile permise de tipul a ¸ as variabilelor implicate ˆ respectivele calcule, se pot obtine rezultate eronate. ın ¸

4

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.1.4

Structura unui program Java

Un program simplu ˆ Java are urm˘toarea structur˘: ın a a class numeClasa { public static void main(String args[]) { // declar˘ri de variabile a // instructiuni ¸ } } Programul prezentat ˆ sectiunea anterioar˘ se poate scrie sub forma: ın ¸ a class Ciudat { public static void main(String args[]) { // declar˘ri de variabile a int x,y,z; // instructiuni ¸ x=200000; y=300000; z=x*y; System.out.println(x+”*”+y+”=”+z); } } Clasa este elementul de baz˘ ˆ Java. Cel mai simplu program ˆ Java este a ın ın format dintr-o clas˘ (numele clasei este la latitudinea programatorului; singura a recomandare este s˘ ˆ a ınceap˘ cu liter˘ mare) ¸i functia main. a a s ¸ ˆ exemplul de mai sus sunt declarate trei variabile (x, y ¸i z) de tip int In s (adic˘ de tip ˆ a ıntreg cu semn). Spatiul alocat variabilelor de tip int este de 4 octeti ¸ ¸ (32 biti). Aceasta ˆ ¸ ınseamn˘ c˘ o astfel de variabil˘ poate avea valori ˆ a a a ıntre −263 ¸i s 263 − 1. Valoarea maxim˘ este de aproximativ 2 miliarde. a ˆ programul anterior x are valoarea 200.000 iar y are valoarea 300.000, deci In produsul are valoarea 60 miliarde care dep˘¸e¸te cu mult valoarea maxim˘ de 2 as s a miliarde. ˆ binar, 60 miliarde se scrie (folosint 36 biti) sub forma In ¸ 110111111000010001110101100000000000 dar sunt retinuti numai 32 biti din partea dreapt˘, adic˘ ¸ ¸ ¸ a a 11111000010001110101100000000000 Primul bit reprezint˘ bitul de semn (1 reprezint˘ semnul - iar 0 reprezint˘ a a a semnul +). Aceast˘ reprezentare trebuie gˆndit˘ ca fiind o reprezentare ˆ cod a a a ın

1.2. CONVERSII ALE DATELOR NUMERICE

5

complementar (ea este ˆ memoria calculatorului ¸i toate numerele ˆ ın s ıntregi cu semn sunt reprezentate ˆ acest cod). ın Reprezentarea ˆ cod direct se obtine din reprezentarea ˆ cod complementar ın ¸ ın (mai precis, trecˆnd prin reprezentarea ˆ cod invers ¸i adunˆnd, ˆ binar, 1): a ın s a ın 11111000010001110101100000000000 (cod complementar) 10000111101110001010011111111111 (cod invers) 10000111101110001010100000000000 (cod direct) Din codul direct se obtine -129542144 ˆ baza 10. Aceasta este explicatia acelui ¸ ın ¸ rezultat ciudat!

1.2

Conversii ale datelor numerice

1.2.1

Conversia din baza 10 ˆ baza 2 ın

Fie x = an ...a0 num˘rul scris ˆ baza 10. Conversia ˆ baza 2 a num˘rului x a ın ın a se efectueaz˘ dup˘ urm˘toarele reguli: a a a • Se ˆ ımparte num˘rul x la 2 iar restul va reprezenta cifra de ordin 0 a a num˘rului scris ˆ noua baz˘ (b0 ). a ın a • Cˆtul obtinut la ˆ artirea anterioar˘ se ˆ a ¸ ımp˘ ¸ a ımparte la 2 ¸i se obtine s ¸ cifra de ordin imediat superior a num˘rului scris ˆ noua baz˘. Secventa a ın a ¸ de ˆ artiri se repet˘ pˆn˘ cˆnd se ajunge la cˆtul 0. ımp˘ ¸ a a a a a • Restul de la a k-a ˆ artire va reprezenta cifra bk−1 . Restul de ımp˘ ¸ la ultima ˆ artire reprezint˘ cifra de ordin maxim ˆ reprezentarea ımp˘ ¸ a ın num˘rului ˆ baza 2. a ın Metoda conduce la obtinerea rezultatului dup˘ un num˘r finit de ˆ artiri, ¸ a a ımp˘ ˆ ıntrucˆt ˆ mod inevitabil se ajunge la un cˆt nul. ˆ plus, toate resturile obtinute a ın a In ¸ apartin multimii {0, 1}. ¸ ¸ Exemplu. Fie x = 13 num˘rul ˆ baza 10. Secventa de ˆ artiri este: a ın ¸ ımp˘ ¸ (1) se ˆ ımparte 13 la 2 ¸i se obtine cˆtul 6 ¸i restul 1 (deci b0 = 1) s ¸ a s (2) se ˆ ımparte 6 la 2 ¸i se obtine cˆtul 3 ¸i restul 0 (deci b1 = 0) s ¸ a s (3) se ˆ ımparte 3 la 2 ¸i se obtine cˆtul 1 ¸i restul 1 (deci b2 = 1) s ¸ a s (4) se ˆ ımparte 1 la 2 ¸i se obtine cˆtul 0 ¸i restul 1 (deci b3 = 1). s ¸ a s Prin urmare (13)10 = (1101)2 .

6

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.2.2

Conversia din baza 2 ˆ baza 10 ın

Dac˘ y = bn ...b1 b0 este un num˘r ˆ baza 2 , atunci reprezentarea ˆ baza 10 a a ın ın se obtine efectuˆnd calculul (ˆ baza 10): ¸ a ın x = bn 2n + ... + b1 2 + b0 . Exemplu. Fie y = 1100. Atunci reprezentarea ˆ baza 10 va fi ın x = 1 · 23 + 1 · 22 + 0 · 21 + 0 · 20 = 12.

1.2.3

Conversii ˆ ıntre bazele 2 ¸i 2r s

Pentru conversia unui num˘r din baza p ˆ baza q se poate converti num˘rul a ın a din baza p ˆ baza 10, iar acesta se converte¸te ˆ baza q. ın s ın ˆ cazul conversiei unui num˘r din baza p = 2 ˆ baza q = 2r se poate evita In a ın trecerea prin baza 10 procedˆndu-se ˆ modul urm˘tor: se formeaz˘ grupuri de a ın a a cˆte r cifre pornind de la ultima cifr˘ din dreapta, ˆ a a ınspre stˆnga. Fiecare grup de a r cifre va fi convertit ˆ ıntr-o cifr˘ a bazei q. a Fie, spre exemplu: p = 2, q = 16 = p4 ¸i x = (1011010)2 . s Se obtin urm˘toarele grupuri de cˆte 4 cifre binare: ¸ a a (1010)2 = A16 ¸i (0101)2 = 516 . s Deci scrierea num˘rului x ˆ baza 16 este: (5A)16 . a ın Se observ˘ c˘ a fost completat˘ cu 0, spre stˆnga, cea mai din stˆnga grup˘, a a a a a a pˆn˘ la formarea grupei complete de 4 cifre binare. a a ˆ cazul conversiei unui num˘r din baza p = 2r ˆ baza q = 2 se poate de In a ın asemenea evita trecerea prin baza 10 procedˆndu-se ˆ modul urm˘tor: fiecare cifr˘ a ın a a din reprezentarea ˆ baza p = 2r se ˆ ın ınlocuie¸te cu r cifre binare care reprezint˘ s a scrierea respectivei cifre ˆ baza 2. ın Fie, spre exemplu: p = 16 = 24 , q = 2 ¸i x = (3A)16 . s Se fac urm˘toarele ˆ a ınlocuiri de cifre: 3 → 0011, A → 1010. Deci scrierea num˘rului x ˆ baza 2 este: (111010)2 . a ın Se observ˘ c˘ nu apar cifrele 0 din stˆnga scrierii brute (00111010)2 obtinute a a a ¸ prin ˆ ınlocuiri.

Capitolul 2

Structuri de date
ˆ Inainte de a elabora un algoritm, trebuie s˘ ne gˆndim la modul ˆ care a a ın reprezent˘m datele. a

2.1

Date ¸i structuri de date s

2.1.1

Date

Datele sunt entit˘¸i purt˘toare de informatie. ˆ informatic˘, o dat˘ este un at a ¸ In a a model de reprezentare a informatiei, accesibil unui anumit procesor (om, unitate ¸ central˘, program), model cu care se poate opera pentru a obtine noi informatii a ¸ ¸ despre fenomenele, procesele ¸i obiectele lumii reale. ˆ functie de modul lor de s In organizare, datele pot fi: elementare (simple) sau structurate. Datele elementare au caracter atomic, ˆ sensul c˘ nu pot fi descompuse ˆ ın a ın alte date mai simple. Astfel de date sunt cele care iau ca valori numere sau ¸iruri s de caractere. O dat˘ elementar˘ apare ca o entitate indivizibil˘ atˆt din punct de a a a a vedere al informatiei pe care o reprezint˘ cˆt ¸i din punct de vedere al procesorului ¸ a a s care o prelucreaz˘. a O dat˘ elementar˘ poate fi privit˘ la nivel logic (la nivelul procesorului uman) a a a sau la nivel fizic (la nivelul calculatorului). Din punct de vedere logic, o dat˘ poate fi definit˘ ca un triplet de forma a a (identif icator, atribute, valori). Din punct de vedere fizic, o dat˘ poate fi definit˘ ca o zon˘ de memorie de o a a a anumit˘ lungime, situat˘ la o anumit˘ adres˘ absolut˘, ˆ care sunt memorate ˆ a a a a a ın ın timp ¸i ˆ s ıntr-o form˘ specific˘ valorile datei. a a 7

8

CAPITOLUL 2. STRUCTURI DE DATE

Identificatorul este un simbol asociat datei pentru a o distinge de alte date ¸i s pentru a o putea referi ˆ cadrul programului. ın Atributele sunt propriet˘¸ii ale datei ¸i precizeaz˘ modul ˆ care aceasta va fi at s a ın tratat˘ ˆ cadrul procesului de prelucrare. Dintre atribute, cel mai important este a ın atributul de tip care define¸tete apartenenta datei la o anumit˘ clas˘ de date. s ¸ a a O clas˘ de date este definit˘ de natura ¸i domeniul valorilor datelor care fac a a s parte din clasa respectiv˘, de operatiile specifice care se pot efectua asupra datelor a ¸ ¸i de modelul de reprezentare intern˘ a datelor. Astfel, exist˘ date de tip ˆ s a a ıntreg, de tip real, de tip logic, de tip ¸ir de caractere, etc. s O multime de date care au acelea¸i caracteristici se nume¸te tip de date. ¸ s s Evident, un tip de date este o clas˘ de date cu acela¸i mod de interpretare logic˘ a s a ¸i reprezentare fizic˘ ¸i se caracterizeaz˘ prin valorile pe care le pot lua datele ¸i s as a s prin operatiile care pot fi efectuate cu datele de tipul respectiv. ¸ De exemplu, tipul ˆ ıntreg se caracterizeaz˘ prin faptul c˘ datele care ˆ apartin a a ıi ¸ pot lua doar valori ˆ ıntregi, ¸i asupra lor pot fi efectuate operatii aritmetice clasice s ¸ (adunare, sc˘dere, ˆ a ınmultire, ˆ artire ˆ multimea numerelor ˆ ¸ ımp˘ ¸ ın ¸ ıntregi, comparatii). ¸ Se poate considera c˘ datele organizate sub forma tablourilor unidimensionale a formeaz˘ tipul vector iar datele organizate sub forma tablourilor bidimensionale a formeaz˘ tipul matrice. a ˆ functie de natura elementelor care o compun, o structur˘ de date poate fi: In ¸ a • omogen˘, atunci cˆnd toate elementele au acela¸i tip; a a s • neomogen˘, atunci cˆnd elementele componente au tipuri diferite. a a • static˘, atunci cˆnd num˘rul de componente este fixat; a a a

ˆ functie de num˘rul datelor care o compun, o structur˘ de date poate fi: In a a • dinamic˘, atunci cˆnd num˘rul de componente este variabil. a a a

Din punct de vedere al modului ˆ care sunt utilizate datele pot fi: ın • Constante. Valoarea lor nu este ¸i nu poate fi modificat˘ ˆ cadrul s a ın algoritmului, fiind fixat˘ de la ˆ a ınceputul acestuia. O constant˘ este a o dat˘ care p˘streaz˘ aceea¸i valoare pe tot parcursul procesului de a a a s prelucrare. Pentru constantele care nu au nume, ˆ a¸i valoarea lor ıns˘s este cea prin care se identific˘. Constante care au nume (identificator) a sunt initializate cu o valoare ˆ momentul declar˘rii. ¸ ın a • Variabile. Valoarea lor poate fi modificat˘ ˆ cadrul algoritmului. a ın ˆ momentrul declar˘rii lor, variabilele pot fi initializate (li se atribuie In a ¸ o valoare) sau pot fi neinitializate (nu li se atribuie nici o valoare). ¸ O variabil˘ este o dat˘ care nu p˘streaz˘ neap˘rat aceea¸i valoare pe a a a a a s parcursul procesului de prelucrare. Tipul unei date trebuie s˘ fie precizat, ˆ cadrul programului de prelucrare, a ın printr-o declaratie de tip ce precede utilizarea respectivei constante sau variabile. ¸ Valorile datei pot fi numere, sau valori de adev˘r, sau ¸iruri de caractere, etc. a s

2.1. DATE SI STRUCTURI DE DATE ¸

9

2.1.2

Structuri de date

Datele apar frecvent sub forma unor colectii de date de diferite tipuri, menite ¸ s˘ faciliteze prelucrarea ˆ cadrul rezolv˘rii unei anumite probleme concrete. a ın a Datele structurate, numite uneori ¸i structuri de date, sunt constituite din mai s multe date elementare (uneori de acela¸i tip, alteori de tipuri diferite), grupate cu s un anumit scop ¸i dup˘ anumite reguli. s a Exemple. 1. Un ¸ir finit de numere reale a1 , a2 , ..., an poate fi reprezentat ca o dat˘ s a structurat˘ (tablou unidimensional sau vector). a 2. O matrice  a1,1  a2,1    ··· am,1 a1,2 a2,2 ··· am,1 ··· ··· .. . ···  a1,n a2,n    ···  am,n

poate fi reprezentat˘ ca o dat˘ structurat˘ (tablou bidimensional) specificˆ fiecare a a a ınd element prin doi indici (de linie ¸i de coloan˘). s a O structur˘ de date este deci o colectie de date, eventual de tipuri diferite, a ¸ pe care s-a definit o anumit˘ organizare ¸i c˘reia ˆ este specific un anumit mod de a s a ıi identificare a elementelor componente. Componetele unei structuri de date pot fi identificate prin nume sau prin ordinea pe care o ocup˘ ˆ cadrul structurii. a ın Dac˘ accesul la o anumit˘ component˘ a structurii de date se poate face f˘r˘ a a a aa s˘ ¸inem seama de celelalte componente, vom spune c˘ structura de date este cu at a acces direct. ˆ schimb, dac˘ accesul la o component˘ a structurii de date se poate In a a face numai ¸inˆnd cont de alte cˆmpuri ale structurii (ˆ conformitate cu ordinea t a a ın structurii, printr-un proces de traversare) atunci vom spune c˘ structura este cu a acces secvential. ¸ Structurile de date pot fi create pentru a fi depozitate ˆ memoria intern˘ ın a (aceste structuri de date se numesc structuri interne) sau ˆ memoria extern˘ (se ın a numesc structuri externe, sau fi¸iere). Structurile interne au un caracter de date s temporare (ele dispar odat˘ cu ˆ a ıncetarea activit˘¸ii de prelucrare) iar cele externe at au un caracter de date permanente (mai bine spus, de lung˘ durat˘). a a Dac˘ pe lˆng˘ componentele structurii se ˆ a a a ınregistreaz˘ pe suport ¸i alte date a s suplimentare care s˘ materializeze relatia de ordonare, atunci structura de date a ¸ respectiv˘ este explicit˘, ˆ caz contrar este implicit˘. De exemplu, structura de a a ın a date de tip tablou este o structur˘ implicit˘ de date iar structura de date de tip a a list˘ liniar˘ este o structur˘ explicit˘ de date. a a a a Asupra structurilor de date se pot efectua operatii care se refer˘ structura ¸ a respectiv˘ sau la valorile datelor componente. Cele mai importante operatii sunt: a ¸ − operatia de creare, care const˘ ˆ memorarea pe suportul de memorie a ¸ a ın structurii de date ˆ forma sa initial˘, ın ¸ a

10

CAPITOLUL 2. STRUCTURI DE DATE

− operatia de consultare, care const˘ ˆ accesul la elementele structurii ˆ ¸ a ın ın vederea prelucr˘rii valorilor acestora, ¸i a s − operatia de actualizare, care const˘ ˆ ad˘ugarea de noi elemente, sau ¸ a ın a eliminarea elementelor care nu mai sunt necesare, sau modificarea valorilor unor componente ale structurii. Toate structurile de date la fel organizate ¸i pe care s-au definit acelea¸i s s operatii, poart˘ numele de tip de structur˘ de date. Dac˘ analiz˘m ˆ a operatiile ¸ a a a a ıns˘ ¸ care se efectueaz˘ asupra unei structuri de date, vom putea vedea c˘ toate acestea a a se reduc la executarea, eventual repetat˘, a unui grup de operatii specifice numite a ¸ operatii de baz˘. ¸ a

2.2

Structuri ¸i tipuri de date abstracte s

2.2.1

Structuri de date abstracte

Abstractizarea datelor reprezint˘ de fapt concentrarea asupra esentialului, a ¸ ignorˆnd detaliile (sau altfel spus, conteaz˘ ”ce” nu ”cum”). a a St˘pˆnirea aplicatiilor complexe se obtine prin descompunerea ˆ module. a a ¸ ¸ ın Un modul trebuie s˘ fie simplu, cu complexitatea ascuns˘ ˆ interiorul lui, ¸i s˘ a a ın s a aib˘ o interfat˘ simpl˘ care s˘ permit˘ folosirea lui f˘r˘ a cunoa¸te implementarea. a ¸a a a a aa s O structur˘ de date abstract˘ este un modul constˆnd din date ¸i operatii. a a a s ¸ Datele sunt ascunse ˆ interiorul modulului ¸i pot fi accesate prin intermediul ın s operatiilor. Structura de date este abstract˘ deoarece este cunoscut˘ numai interfata ¸ a a ¸ structurii, nu ¸i implementarea (operatiile sunt date explicit, valorile sunt definite s ¸ implicit, prin intermediul operatiilor). ¸

2.2.2

Tipuri de date abstracte

Procesul de abstractizare se refer˘ la dou˘ aspecte: a a • abstractizarea procedural˘, care separ˘ propriet˘¸ile logice ale unei actiuni de a a at ¸ detaliile implement˘rii acesteia a • abstractizarea datelor, care separ˘ propriet˘¸ile logice ale datelor de detaliile a at reprezent˘rii lor a O structur˘ de date abstracte are un singur exemplar (o singur˘ instanta). a a ¸˘ Pentru a crea mai multe exemplare ale structurii de date abstracte se define¸te un s tip de date abstract. ˆ Java, de exemplu, clasa asigur˘ un mod direct de definire In a a oric˘rui tip de date abstract. a

2.3. STRUCTURI DE DATE ELEMENTARE

11

2.3

Structuri de date elementare

2.3.1

Liste

O list˘ este o colectie de elemente de informatie (noduri) aranjate ˆ a ¸ ¸ ıntr-o anumit˘ ordine. Lungimea unei liste este num˘rul de noduri din list˘. Structura a a a corespunzatoare de date trebuie s˘ ne permit˘ s˘ determin˘m eficient care este a a a a primul/ultimul nod ˆ structur˘ ¸i care este predecesorul/succesorul unui nod dat ın as (dac˘ exist˘). Iat˘ cum arat˘ cea mai simpl˘ list˘, lista liniar˘: a a a a a a a
capul listei coada listei

Figura 2.1: List˘ liniar˘ a a O list˘ circular˘ este o list˘ ˆ care, dup˘ ultimul nod, urmeaz˘ primul nod, a a a ın a a deci fiecare nod are succesor ¸i predecesor. s Cˆteva dintre operatiile care se efectueaz˘ asupra listelor sunt: inserarea a ¸ a (ad˘ugarea) unui nod, extragerea (¸tergerea) unui nod, concatenarea unor liste, a s num˘rarea elementelor unei liste etc. a Implementarea unei liste se realizeaz˘ ˆ dou˘ moduri: secvential ¸i ˆ antuit. a ın a ¸ s ın˘ ¸ Implementarea secvential˘ se caracterizeaz˘ prin plasarea nodurilor ˆ locatii a a ın ¸ succesive de memorie, ˆ conformitate cu ordinea lor ˆ list˘. Avantajele acestui ın ın a mod de implementare sunt accesul rapid la predecesorul/succesorul unui nod ¸i s g˘sirea rapid˘ a primului/ultimului nod. Dezavantajele sunt modalit˘¸ile relativ a a at complicate de inserarea/¸tergere a unui nod ¸i faptul c˘, ˆ general, nu se folose¸te s s a ın s ˆ ıntreaga memorie alocat˘ listei. a Implementarea ˆ antuit˘ se caracterizeaz˘ prin faptul c˘ fiecare nod contine ınl˘ ¸ a a a ¸ dou˘ p˘rti: informatia propriu-zis˘ ¸i adresa nodului succesor. Alocarea memoriei a a¸ ¸ as pentru fiecare nod se poate face ˆ mod dinamic, ˆ timpul rul˘rii programului. ın ın a Accesul la un nod necesit˘ parcurgerea tuturor predecesorilor s˘i, ceea ce conduce a a la un consum mai mare de timp pentru aceast˘ operatie. ˆ schimb, operatiile a ¸ In ¸ de inserare/¸tergere sunt foarte rapide. Se consum˘ exact atˆt spatiu de memorie s a a ¸ cˆt este necesar dar, evident, apare un consum suplimentar de memorie pentru a ˆ ınregistrarea leg˘turii c˘tre nodul succesor. Se pot folosi dou˘ adrese ˆ loc de a a a ın una, astfel ˆ at un nod s˘ contin˘ pe lang˘ adresa nodului succesor ¸i adresa ıncˆ a ¸ a a s nodului predecesor. Obtinem astfel o list˘ dublu inl˘ntuit˘, care poate fi traversat˘ ¸ a a ¸ a a ˆ ambele directii. ın ¸ Listele ˆ antuite pot fi reprezentate prin tablouri. ˆ acest caz, adresele ınl˘ ¸ In nodurilor sunt de fapt indici ai tabloului.

12

CAPITOLUL 2. STRUCTURI DE DATE

O alternativ˘ este s˘ folosim dou˘ tablouri val ¸i next astfel: s˘ memor˘m a a a s a a informatia fiecarui nod i ˆ locatia val[i], iar adresa nodului s˘u succesor ˆ locatia ¸ ın ¸ a ın ¸ next[i]. Indicele locatiei primului nod este memorat ˆ variabila p. Vom conveni ca, ¸ ın pentru cazul listei vide, s˘ avem p = 0 ¸i next[u] = 0 unde u reprezint˘ ultimul nod a s a din list˘. Atunci, val[p] va contine informatia primului nod al listei, next[p] adresa a ¸ ¸ celui de-al doilea nod, val[next[p]] informatia din al doilea nod, next[next[p]] ¸ adresa celui de-al treilea nod, etc. Acest mod de reprezentare este simplu dar apare problema gestion˘rii locatiilor libere. O solutie este s˘ reprezent˘m locatiile a ¸ ¸ a a ¸ libere tot sub forma unei liste ˆ ınlantuite. Atunci, ¸tergerea unui nod din lista ¸ s initial˘ implic˘ inserarea sa ˆ lista cu locatii libere, iar inserarea unui nod ˆ lista ¸ a a ın ¸ ın initial˘ implic˘ ¸tergerea sa din lista cu locatii libere. Pentru implementarea listei ¸ a as ¸ de locatii libere, putem folosi acelea¸i tablouri dar avem nevoie de o alt˘ variabil˘, ¸ s a a f reehead, care s˘ contin˘ indicele primei locatii libere din val ¸i next. Folosim a ¸ a ¸ s acelea¸i conventii: dac˘ f reehead = 0 ˆ s ¸ a ınseamn˘ c˘ nu mai avem locatii libere, iar a a ¸ next[ul] = 0 unde ul reprezint˘ ultima locatie liber˘. a ¸ a Vom descrie in continuare dou˘ tipuri de liste particulare foarte des folosite. a

2.3.2

Stive ¸i cozi s

O stiv˘ este o list˘ liniar˘ cu proprietatea c˘ operatiile de inserare/extragere a a a a ¸ a nodurilor se fac ˆ ın/din coada listei. Dac˘ nodurile A, B, C sunt inserate ˆ a ıntr-o stiv˘ ˆ aceast˘ ordine, atunci primul nod care poate fi ¸ters/extras este C. ˆ mod a ın a s In echivalent, spunem c˘ ultimul nod inserat este singurul care poate fi ¸ters/extras. a s Din acest motiv, stivele se mai numesc ¸i liste LIFO (Last In First Out). s Cel mai natural mod de reprezentare pentru o stiv˘ este implementarea a secvential˘ ˆ ¸ a ıntr-un tablou S[1..n], unde n este num˘rul maxim de noduri. Primul a nod va fi memorat ˆ S[1], al doilea ˆ S[2], iar ultimul ˆ S[top], unde top este ın ın ın o variabil˘ care contine adresa (indicele) ultimului nod inserat. Initial, cˆnd stiva a ¸ ¸ a este vid˘, avem (prin conventie) top = 0. a ¸ O coad˘ este o list˘ liniar˘ ˆ care inser˘rile se fac doar ˆ capul listei, iar a a a ın a ın ¸tergerile/extragerile se fac doar din coada listei. Din acest motiv, cozile se mai s numesc ¸i liste FIFO (First In First Out). s O reprezentare secvential˘ pentru o coad˘ se obtine prin utilizarea unui ¸ a a ¸ tablou C[0..n − 1], pe care ˆ trat˘m ca ¸i cum ar fi circular: dup˘ locatia C[n − 1] ıl a s a ¸ urmeaz˘ locatia C[0]. Fie tail variabila care contine indicele locatiei predecesoare a ¸ ¸ ¸ primei locatii ocupate ¸i fie head variabila care contine indicele locatiei ocupate ¸ s ¸ ¸ ultima oar˘. Variabilele head ¸i tail au aceea¸i valoare atunci ¸i numai atunci cˆnd a s s s a coada este vid˘. Initial, avem head = tail = 0. a ¸ Trebuie s˘ observ˘m faptul c˘ testul de coad˘ vid˘ este acela¸i cu testul de a a a a a s coad˘ plin˘. Dac˘ am folosi toate cele n locatii la un moment dat, atunci nu am a a a ¸ putea distinge ˆ ıntre situatia de ”coad˘ plin˘” ¸i cea de ”coad˘ vid˘”, deoarece ˆ ¸ a a s a a ın ambele situatii am avea head = tail. ˆ consecint˘, vom folosi efectiv, ˆ orice ¸ In ¸a ın moment, cel mult n − 1 locatii din cele n ale tabloului C. ¸

unde V este o multime de vˆrfuri.. j] valoarea lungimii muchiei dintre vˆrfurile i ¸i j. Dac˘ num˘rul muchiilor ˆ a a a ın graf este mic. j] = true dac˘ vˆrfurile i ¸i j ¸˘ ın a a s sunt adiacente. Aceasta necesit˘ n operatii (unde n este num˘rul de vˆrfuri ˆ graf). a a a Pentru grafuri orientate.. dac˘ dorim s˘ s a a a a a a afl˘m toate vˆrfurile adiacente unui vˆrf dat. Un vˆrf care este a a a extremitatea unei singure muchii se nume¸te vˆrf terminal.3. STRUCTURI DE DATE ELEMENTARE 13 2. ˆ a Intrun graf cu m muchii. . suma lungimilor listelor de adiacent˘ este 2m. Pe de alt˘ parte. aceast˘ notiune este ˆ arit˘: un graf orientat este tare a ınt˘ a conex. . ceea ce a a a s a este mai putin eficient decˆt consultarea unei valori logice ˆ matricea de adiacenta. a a a s dac˘ graful este neorientat. a3 ). an ) sau de forma {a1 . lista de adiacent˘ a lui j). a3 }. Totu¸i. b). b}. dac˘ graful a a este neorientat. a2 ). iar muchiilor a s ¸ li se pot ata¸a informatii numite uneori lungimi sau costuri.. considerand A[i. O muchie de la vˆrful a la vˆrful b este ¸ a a notat˘ cu perechea ordonat˘ (a. trebuie s˘ analiz˘m o ˆ a a a a a ıntreag˘ linie a din matrice. adic˘ prin ata¸area la fiecare vˆrf i a listei de vˆrfuri ¸˘ a s a a adiacente (pentru grafuri orientate. Un ciclu este un drum care este simplu. a a a • Prin liste de adiacenta. trebuie s a a a s s˘ analiz˘m lista de adiacent˘ a lui i (¸i. a aa Un graf neorientat este conex. posibil. dac˘ ˆ a ıntre oricare dou˘ vˆrfuri i ¸i j exist˘ un drum de la i la j ¸i un drum a a s a s de la j la i. ¸ a ın ¸˘ • Printr-o list˘ de muchii. respectiv m. Un drum simplu este un drum ˆ care nici un a ıl ın vˆrf nu se repet˘. s ¸ Exist˘ cel putin trei moduri de reprezentare ale unui graf: a ¸ • Printr-o matrice de adiacenta A. dac˘ graful este orientat. cu exceptia primului ¸i a a ¸ s ultimului vˆrf. Lungimea drumului este egal˘ cu a a num˘rul muchiilor care ˆ constituie. . an } dup˘ cum graful este orientat sau neorientat. pentru a determina dac˘ dou˘ vˆrfuri i ¸i j sunt adiacente. dac˘ graful este orientat. ¸i cu multimea {a. j] = f alse ˆ caz contrar. Aceast˘ reprezentare este eficient˘ atunci cˆnd a a a a avem de examinat toate muchiile grafului. O alt˘ variant˘ este s˘-i d˘m lui ın a a a a A[i. {an−1 .3. a ¸ a a ın independent de num˘rul de muchii care conecteaz˘ vˆrful respectiv. s a Un drum este o succesiune de muchii de forma (a1 .. iar A[i.2.. a Dou˘ vˆrfuri unite printr-o muchie se numesc adiacente. dac˘ ˆ a ıntre oricare dou˘ vˆrfuri exist˘ un drum. care coincid. M >. este necesar ca muchia s˘ plece din i). {a2 .3 Grafuri Un graf este o pereche G =< V. j] = +∞ a s atunci cˆnd cele dou˘ vˆrfuri nu sunt adiacente.. ˆ care A[i. Cu aceast˘ reprezentare. aceast˘ reprezentare este preferabil˘ din punct de vedere al memoriei a a necesare. putem a a a a verifica u¸or dac˘ dou˘ vˆrfuri sunt adiacente. (a2 . Un graf aciclic este un graf f˘r˘ cicluri. a2 }. (an−1 . Vˆrfurilor unui graf li se pot ata¸a informatii (numite valori). iar ¸ a M ⊆ V × V este o multime de muchii.

a a Un arbore binar de ˆ altime k are cel mult 2k+1 − 1 vˆrfuri. . . Pe fiecare nivel i > 0 sunt s a a plasate vˆrfurile pentru care lungimea drumurilor care le leag˘ de r˘d˘cin˘ este i. atunci vˆrful i se nume¸te ascendent al lui j. un arbore s este un graf neorientat ˆ care exist˘ exact un drum ˆ ın a ıntre oricare dou˘ vˆrfuri. a a Dac˘ exist˘ un drum de la un vˆrf i de pe nivelul ni la un vˆrf j de pe nivelul a a a a nj > ni.2: Arbore binar plin Un arbore binar cu n vˆrfuri ¸i de ˆ altime k este complet. STRUCTURI DE DATE 2. dac˘ este cazul. adˆncimea unui vˆrf a a a a a este lungimea drumului dintre r˘d˘cin˘ ¸i acest vˆrf iar ˆ altimea unui vˆrf este a a as a ın˘ ¸ a lungimea celui mai lung drum dintre acest vˆrf ¸i un vˆrf terminal. Sau. iar vˆrful j se nume¸te a s a s descendent al lui i. Vˆrfurile care a a a aa ¸ a nu sunt terminale se numesc neterminale. a a Un arbore reprezentat pe niveluri se nume¸te arbore cu r˘d˘cin˘. a a a a a Vˆrfurile de pe un nivel i > 0 legate de acela¸i vˆrf j de pe nivelul i − 1 se a s a numesc descendentii directi (fiii) vˆrfului j iar vˆrful j se nume¸te ascendent direct ¸ ¸ a a s (tat˘) al acestor vˆrfuri. Un arbore ˆ care orice vˆrf are cel mult doi descendenti se nume¸te arbore ın a ¸ s binar. ˆ Intr-un arbore cu r˘d˘cin˘ (reprezentat pe niveluri). numerotarea se face ˆ arbore de la stˆnga la dreapta. 2k+1 − 1.3. Vˆrful s a a a a plasat pe nivelul 0 se nume¸te r˘d˘cina arborelui. aciclic ¸i conex.14 CAPITOLUL 2.. n + 2. In˘ ¸ ın˘ ¸ a a ˆ Intr-un arbore binar.. Un vˆrf terminal (sau frunz˘) este un vˆrf f˘r˘ descendenti. se nume¸te arbore plin. echivalent. a vˆrfurilor ın˘ ¸ a a numerotate cu n + 1. num˘rul maxim de vˆrfuri aflate pe nivelul k este 2k . ın a 1 nivelul 0 2 3 nivelul 1 4 5 6 7 nivelul 2 8 9 10 11 12 13 14 15 nivelul 3 Figura 2. a s a ˆ altimea arborelui este ˆ altimea r˘d˘cinii. Pentru acela¸i ın s nivel. prin eliminarea.. iar dac˘ are exact ın˘ ¸ a a 2k+1 − 1 vˆrfuri. a s Varfurile unui arbore plin se numeroteaza ˆ ordinea nivelurilor.4 Arbori binari Un arbore este un graf neorientat. dac˘ se obtine a s ın˘ ¸ a ¸ din arborele binar plin de ˆ altime k.

.3.3: Arbore binar complet Tat˘l unui vˆrf reprezentat ˆ T [i]. a a a Un min-heap este un arbore binar complet ˆ care valoarea fiecarui vˆrf este ın a mai mic˘ sau egal˘ cu valoarea fiec˘rui fiu al s˘u..4: Max-heap . a a a a ın ¸ . T [2k +1]. de la stˆnga la dreapta. ¸ punˆnd vˆrfurile de adˆncime k. i > 0. STRUCTURI DE DATE ELEMENTARE 15 Acest tip de arbore se poate reprezenta secvential folosind un tablou T . T [2k+1 − 1] (cu posibila exceptie a ultimului nivel care poate fi incomplet). ˆ traducere aproximativ˘) este a a ın a un arbore binar complet.3. ın a a a ın s 2. ˆ pozitiile T [2k ]. Fiii unui vˆrf a a ın a ın a reprezentat ˆ T [i] se afl˘.5 Heap-uri Un max-heap (heap=”gramad˘ ordonat˘”. cu urm˘toarea proprietate: valoarea fiecarui vˆrf este a a mai mare sau egal˘ cu valoarea fiec˘rui fiu al s˘u.2. ¸ 1 nivelul 0 2 3 nivelul 1 4 5 6 7 nivelul 2 8 9 10 11 12 nivelul 3 Figura 2. se afl˘ ˆ T [i/2].. ˆ T [2i] ¸i T [2i + 1]. dac˘ exist˘. a a a a 11 nivelul 0 7 10 nivelul 1 7 7 9 2 nivelul 2 4 6 5 7 3 nivelul 3 Figura 2.

pentru modificarea valorii unui vˆrf. Reuniunea submultimilor etichetate cu a ¸i b se poate realiza astfel: ¸ s procedure reuniune(a. Avem atunci proprietatea: set[i] ≤ i. . ˆ ıntr-un max-heap. 5. iar prioritatea unui eveniment poate fi modificat˘ ˆ mod dinamic. indici intr-un tablou unde sunt memorate a valorile elementelor. este suficient s˘ schimb˘m ˆ as s a a a ıntre ele aceste dou˘ valori a ¸i s˘ continu˘m procedeul ˆ mod ascendent. Presupunem c˘ ne intereseaz˘ reuniunea a a a a a a dou˘ submultimi.. ˆ care fiec˘rei locatii set[i] i se atribuie eticheta ın a submultimii care contine elementul i.6 Structuri de multimi disjuncte ¸ S˘ presupunem c˘ avem N elemente. Vom conveni ca elementul minim al unei ¸ multimi s˘ fie eticheta multimii respective. numerotate de la 1 la N . a ¸ Deoarece submultimile sunt dou˘ cˆte dou˘ disjuncte. format˘ din submultimi a ¸ dou˘ cˆte dou˘ disjuncte: S1.N ]. ¸ a a Sunt exact operatiile de care avem nevoie pentru a implementa o list˘ dinamic˘ ¸ a a de priorit˘¸i: valoarea unui vˆrf va da prioritatea evenimentului corespunzator. Dac˘. pˆn˘ cˆnd proprietatea de heap este s a a ın a a a restabilit˘. 8} va fi numit˘ ¸ a ¸ a ”multimea 2”. j←b if i > j then interschimb˘ i ¸i j a s for k ← j to N do if set[k] = j then set[k] ← i . Astfel. dimpotriv˘. Vom aloca tabloul set[1. valoarea vˆrfului scade.16 CAPITOLUL 2. putem alege ca etichet˘ ¸ a a a a pentru o submultime oricare element al ei. a a a De exemplu. at a Evenimentul cu prioritatea cea mai mare/mic˘ se va afla mereu la radacina a heap-ului. astfel ˆ at devine mai mic˘ a a a a ıncˆ a decˆt valoarea cel putin a unui fiu. de exemplu. STRUCTURI DE DATE Acela¸i heap poate fi reprezentat secvential prin urm˘torul tablou: s ¸ a 11 7 10 7 7 9 2 4 6 5 7 3 Caracteristica de baz˘ a acestei structuri de date este c˘ modificarea valorii a a unui vˆrf se face foarte eficient. este suficient s˘ schimb˘m intre ele valoarea a ¸ a a modificat˘ cu cea mai mare valoare a fiiilor.. b) i ← a. pentru inserarea unui vˆrf. . multimea {3.3. pentru ¸ ¸ 1 ≤ i ≤ N . Numerele care a a identific˘ elementele pot fi. S2. dac˘ valoarea unui vˆrf cre¸te. astfel ˆ at a a s ıncˆ dep˘¸e¸te valoarea tat˘lui. a a a a Heap-ul este structura de date ideal˘ pentru extragerea maximului/minimului a dintr-o multime. 2. pˆn˘ cˆnd proprietatea de heap este restabilit˘. Si ∪ Sj . a ın 2.. Fie o partitie a acestor N elemente. p˘strˆndu-se proprietatea de heap. apoi s˘ continu˘m procesul ˆ mod a a a ın descendent.

ˆ R. a a ¸ (b) Vom folosi metoda de rezolvare a ecuatiei de gradul al doilea avˆnd ¸ a forma general˘ ax2 + bx + c = 0. a a ın ¸ (a) Datele initiale sunt reprezentate de c˘tre coeficientii ecuatiei iar ¸ a ¸ ¸ obiectivul este determinarea r˘d˘cinilor reale ale ecuatiei. ¸ a a (c) Aplicarea metodei pentru datele problemei (a = 1. dac˘ ∆ = 0 a atunci ecuatia are o r˘d˘cina real˘ dubl˘: x1. a ecuatiei x2 − 3x + 2 = 0. ¸ s (b) Alegerea metodei de rezolvare.2 = ¸ a a a a altfel ecuatia nu are r˘d˘cini reale. Aceast˘ metod˘ poate fi descris˘ a a a a astfel: Pasul 1.2 = ¸ a a a altfel. (c) Aplicarea metodei pentru date concrete.Capitolul 3 Algoritmi 3. x2 = 2. 17 −b 2a √ −b± ∆ 2a . c = 2) conduce la rezultatul: x1 = 1.1 Etape ˆ rezolvarea problemelor ın Principalele etape care se parcurg ˆ rezolvarea unei probleme sunt: ın (a) Stabilirea datelor initiale ¸i a obiectivului (ce trebuie determinat). a Pasul 2. b = −3. Exemplu. Dac˘ ∆ > 0 a atunci ecuatia are dou˘ r˘d˘cini reale distincte: x1. Se calculeaz˘ discriminantul: ∆ = b2 − 4ac. S˘ presupunem c˘ problema este rezolvarea.

etc) ori ¸ este asem˘n˘tor cu ceva ˆ a¸at mai ˆ a a ınv˘t ınainte (sortare. V. a . S˘ observ˘m c˘ nu apare ˆ definitie cuvˆntul ”calculator”. etc). Algoritmul lui Euclid pentru ın calculul celui mai mare divizor comun a dou˘ numere naturale este. aceast˘ descriere apare sub a a a denumirea de algoritm ˆ ”Elementele lui Euclid”. c˘utare binar˘. permit obtinerea rezultatului unei probleme ¸ din clasa celor pentru care a fost conceput. parcurgere a a ˆ adˆncime.2. algoritmii nu au a a a ın ¸ a neap˘rat leg˘tur˘ cu calculatorul. ˆ informatic˘ exist˘ de asemenea mai multe definitii pentru notiunea de In a a ¸ ¸ algoritm. ın a Secventa de pa¸i prin care este descris˘ metoda de rezolvare a ecuatiei de ¸ s a ¸ gradul al doilea (prezentat˘ ˆ sectiunea anterioar˘) este un exemplu de algoritm. Mai tˆrziu. ¸ a Descrierea unui algoritm presupune precizarea datelor initiale ¸i descrierea ¸ s prelucr˘rilor efectuate asupra acestora.memorie intern˘ format˘ din locatii de memorie ¸i utilizat˘ pentru a a ¸ s a stocarea temporar˘ a valorilor variabilelor. De exemplu.18 CAPITOLUL 3. a ın ¸ a Calculul efectuat la Pasul 1 este un exemplu de operatie aritmetic˘. iar analiza ¸ a semnului discriminantului (Pasul 2) este un exemplu de operatie logic˘. ¸ s aplicate asupra unor date. ALGORITMI 3. Altfel spus. Markov. Totu¸i.2 Algoritmi 3. Di. A. functii recursive. se poate spune c˘: a a algoritm = date + prelucr˘ri a Al-Khwarizmi a fost cel care a folosit pentru prima dat˘ reguli precise ¸i clare a s pentru a descrie procese de calcul (operatii aritmetice fundamentale) ˆ lucrarea ¸ ın sa ”Scurt˘ carte despre calcul algebric”. ˆ [35] notiunea de algoritm se define¸te astfel: ın ¸ s Un algoritm este sistemul virtual A = (M. Astfel. De.1 Ce este un algoritm? Un algoritm este o succesiune de operatii aritmetice ¸i/sau logice care. recursivitate. S-a demonstrat c˘ aceste definitii sunt ¸ a ¸ echivalente din punct de vedere matematic. ˆ acest curs ne vom concentra aproape a a a s ın exclusiv pe algoritmi care pot fi implementati rezonabil pe calculator. ¸ fiecare pas din algoritm trebuie astfel gˆndit ˆ at ori este suportat direct de c˘tre a ıncˆ a limbajul de programare favorit (operatii aritmetice. P. M i. se pare. primul a algoritm cunoscut ˆ matematic˘. R. sisteme POST. ma¸ina ¸ s Turing. algoritmul operational al lui A. M e) constituit din urm˘toarele elemente: a M . ın a ˆ matematic˘ notiunea de algoritm a primit mai multe definitii: algoritmul In a ¸ ¸ normal al lui A. A. cicluri. Leapunov.

3. limbajul pseudocod).). sunt stocate ˆ memoria M . Java).date de intrare care reprezint˘ valori ale unor parametri care a caracterizeaz˘ ipotezele de lucru/st˘rile initiale ale problemei ¸i a a ¸ s care sunt stocate ˆ memoria M prin intermediul instructiunilor ın ¸ de citire/intrare care utilizeaz˘ mediul de intrare M i. ¸ ın ¸i ˆ s ınregistrate pe un suport virtual prin intermediul instructiunilor ¸ de scriere/ie¸ire care utilizeaz˘ mediul de ie¸ire M e. etc). a M e . care ˆ a a a ımbinate cu tehnici de programare corespunz˘toare realizeaz˘ actiuni/procese logice.2. etc. . a a ¸ utilizˆnd memoria virtual˘ M ¸i multimea de variabile V . a ¸ Un program este un algoritm tradus ˆ ıntr-un limbaj de programare. Un limbaj de programare este un limbaj artificial. disc magnetic. s a s M i . a a s ¸ Di . care permite descrierea algoritmilor astfel ˆ at s˘ poat˘ fi transmi¸i ıncˆ a a s calculatorului cu scopul ca acesta s˘ efectueze operatiile specificate.date de ie¸ire care reprezint˘ valori ale unor parametri care s a caracterizeaz˘ solutia problemei/st˘rile finale. valorile datelor de a ¸ a ie¸ire sunt obtinute din valorile unor variabile generate de executia s ¸ ¸ instructiunilor din procesul de calcul P . romˆn˘.proces de calcul reprezentat de o colectie de instructiuni/comenzi ¸ ¸ exprimate ˆ ıntr-un limbaj de reprezentare (de exemplu.mediu de intrare care este un dispozitiv virtual de intrare/citire pentru preluarea valorilor datelor de intrare ¸i stocarea acestora ˆ s ın memoria virtual˘ M . C. limbaje algoritmice.multime de variabile definite ˆ conformitate cu rationamentul R. ¸ a s R . chimie etc. a P .). ALGORITMI V . etc. a 19 Un limbaj este un mijloc de transmitere a informatiei. hˆrtie. folosind memoria virtual˘ M ¸i multimea de variabile a s ¸ V . instructiunile implementeaz˘/codific˘ tehnicile ¸i metodele care ¸ a a s constituie rationamentul R. a De . ¸ Exist˘ mai multe tipuri de limbaje: limbaje naturale (englez˘. ¸ ın ¸ care utilizeaz˘ memoria M pentru stocarea valorilor din V .rationament de rezolvare exprimat prin diverse tehnici ¸i metode ¸ s specifice domeniului din care face parte clasa de probleme supuse rezolv˘rii (matematic˘. solutia problemei se afl˘ ˆ anumite ¸ ¸ a ın locatii de memorie corespunz˘toare datelelor de ie¸ire De. riguros ˆ ıntocmit. a a a a limbaje ¸tiintifice (de exemplu limbajul matematic). dup˘ executia a a a ¸ tuturor instructiunilor din P . executia instructiunilor procesului de ¸ ¸ ¸ calcul determin˘ o dinamic˘ a valorilor variabilelor. . limbaje s ¸ de programare (de exemplu Pascal.mediu de ie¸ire care este un dispozitiv virtual de ie¸ire/scriere s s pentru preluarea datelor din memoria virtual˘ M ¸i ˆ a s ınregistrarea acestora pe un suport virtual (ecran. fizic˘.

a ¸ a s 3. o metod˘ a a a s a a a care nu asigur˘ obtinerea rezultatului dup˘ un num˘r finit de pa¸i nu a ¸ a a s poate fi considerat˘ algoritm. nu au nici o important˘ ˆ elaborarea/proiectarea algoritmilor.20 CAPITOLUL 3. a • Finitudine. deci ei nu trebuie specificati ˆ ¸ ıntr-un limbaj de programare. Algoritmii au o serie de structuri .ˆ special a ın .2. Sunt prelucr˘ri caracterizate prin faptul c˘ a a aceea¸i prelucrare (simpl˘ sau structurat˘) este repetat˘ cˆt s a a a a timp este ˆ ındeplinit˘ o anumit˘ conditie.3 Tipuri de prelucr˘ri a Prelucr˘rile care intervin ˆ a ıntr-un algoritm pot fi simple sau structurate. ¸a ın Pe de alt˘ parte. atunci ori de cˆte ori se aplic˘ algoritmul aceluia¸i a a s set de date de intrare trebuie s˘ se obtin˘ acela¸i rezultat.3 Descrierea algoritmilor Algoritmii nu sunt programe. Un algoritm trebuie s˘ prevad˘. ALGORITMI 3. de exemplu din Pascal. Sunt secvente de prelucr˘ri simple sau structurate ¸ a care sunt efectuate ˆ ordinea ˆ care sunt specificate. Din aceast˘ cauz˘. f˘r˘ ambiguit˘¸i ¸i a a aa at s f˘r˘ neclarit˘¸i. Dac˘ ˆ cadrul algoritmului nu intervin a ın a ın elemente aleatoare. • Prelucr˘rile structurate pot fi de unul dintre tipurile: a − Liniare. eventual prin a evaluarea unor expresii. Orice algoritm trebuie s˘ permit˘ obtinerea rezultatului a a ¸ dup˘ un num˘r finit de prelucr˘ri (pa¸i). Detaliile sintactice.2 Propriet˘¸ile algoritmilor at Principalele propriet˘¸i pe care trebuie s˘ le aib˘ un algoritm sunt: at a a • Generalitate. ın ın − Alternative. a a ¸ 3. descrierea ˆ limba romˆn˘ (ca ¸i ˆ limba englez˘ [15]) ˆ a ın a a s ın a ın mod uzual nu este o idee mai bun˘. C/C++ sau Java. Sunt prelucr˘ri caracterizate prin faptul c˘ ˆ a a ın functie de realizarea sau nerealizarea unei conditii se alege ¸ ¸ una din dou˘ sau mai multe variante de prelucrare. a • Determinism. • Prelucr˘rile simple sunt atribuiri de valori variabilelor. a − Repetitive. Din a a a aceast˘ cauz˘. modul de solutionare a tuturor situatiilor care pot s˘ aa at ¸ ¸ a apar˘ ˆ rezolvarea problemei. nu numai pentru o problem˘ particular˘. o metod˘ de rezolvare a unei ecuatii particulare nu poate a a a ¸ fi considerat˘ algoritm.2. Un algoritm trebuie s˘ poat˘ fi utilizat pentru o clas˘ a a a ˆ ıntreag˘ de probleme.

¸i poate fi implementat˘ u¸or de s a s c˘tre orice programator competent ˆ orice limbaj de programare. Se consider˘ ca nou deˆ artit vechiul s ¸ a ımp˘ ¸ ˆ artitor ¸i ca nou ˆ ımp˘ ¸ s ımpartitor restul obtinut la ˆ artirea anterioar˘..1 Limbaj natural Exemple. s ¸ a Cea mai bun˘ metod˘ de a descrie un algoritm este utilizarea limbajului a a pseudocod.. repetitive.. La fel ca orice limb˘ vorbit˘. la fel ca ¸i un cod bun.. iar algoritmii trebuie s˘ fie at ınt s ¸ ¸ a descri¸i cu o acuratete maxim posibil˘.... Acesta folose¸te structuri ale limbajelor de programare ¸i matematicii s s pentru a descompune algoritmul ˆ pa¸i elementari (propozitii simple).3. mult mai s ınt s u¸or..3.. romˆna curat˘. a a a ascunde detaliile care nu sunt semnificative. subˆ ¸elesuri ¸i nuante de semnificatie. Un pseudocod bun. Permite determinarea cˆtului ¸i restului ˆ artirii unui a s ımp˘ ¸ polinom P [X] = an X n + an−1 X n−1 + .3.. DESCRIEREA ALGORITMILOR 21 conditionale.... nu este! Si atunci . el permite de asemenea. 2. a a a Modul exact de structurare a pseudocodului este o alegere personal˘. a2 a1 a0 b an bcn−1 + an−1 . Metoda de determinare a cmmdc poate a s fi descris˘ ˆ limbaj natural dup˘ cum urmeaz˘.. ⇓ . a ın a a Se ˆ ımparte a la b ¸i se retine restul r.. proba clar˘ se poate face numai pe baza unui program a a care s˘ dea rezultatele corecte! Oamenii sunt oameni! Cineva poate s˘ insiste c˘ a a a algoritmul lui este bun de¸i . s ımp˘ ¸ a Se observ˘ c˘ metoda descris˘ ˆ a a a ındepline¸te propriet˘¸ile unui algoritm: poate s at fi aplicat˘ oric˘rei perechi de numere naturale iar num˘rul de prelucr˘ri este finit a a a a (dup˘ un num˘r finit de ˆ artiri se ajunge la un rest nul). ak .. Ultimul rest nenul (care a fost ımp˘ ¸ a a a ¸ ¸i ultimul ˆ artitor) reprezint˘ rezultatul. a a ımp˘ ¸ De asemenea se observ˘ c˘ prelucrarea principal˘ a algoritmului este una a a a repetitiv˘. ck−1 ... program˘m! s ¸ a 3.care sunt departe de a putea fi descrise ¸ s prea u¸or ˆ limbaj natural. ⇓ ⇓ ⇓ cn−1 cn−2 . a O descriere foarte bun˘ a algoritmului arat˘ structura intern˘ a acestuia. Operatia ¸ ¸ ımp˘ ¸ a ¸ de ˆ artire continu˘ pˆn˘ se obtine un rest nul. O modalitate simpl˘ de a descrie metoda de rezolvare este schema urm˘toare: a a an an−1 . ¸i recursivitatea . chiar dac˘ el a ın a nu ˆ ¸elege ce face acel algoritm. bc2 + a2 bc1 + a1 bc0 + a0 ⇓ ⇓ . face ınt s algoritmul mult mai u¸or de ˆ ¸eles ¸i analizat. dar care ın s ¸ pot fi scrise folosind matematica. c1 c0 P [b] .. + a1 X + a0 = 0 la un binom de forma X − b. limba romˆna este plin˘ s ın a a a a de ambiguit˘¸i. conditia utilizat˘ pentru a analiza dac˘ s-a terminat prelucrarea fiind a ¸ a a egalitatea cu zero a restului. 1.. Permite determinarea celui mai mare divizor comun (cmmdc) a dou˘ numere naturale a ¸i b. descoperirea gre¸elilor. Schema lui Horner.. s s Pe de alt˘ parte. bck + ak . Algoritmul lui Euclid.. sau un amestec al celor dou˘.

Schemele logice au avantajul c˘ sunt sugestive dar ¸i dezavantajul c˘ pot a s a deveni dificil de urm˘rit ˆ cazul unor prelucr˘ri prea complexe. Un algoritm descris s ˆ pseudocod contine atˆt enunturi care descriu operatii ce pot fi traduse direct ın ¸ a ¸ ¸ ˆ ıntr-un limbaj de programare (unui enunt ˆ limbaj algoritmic ˆ corespunde o ¸ ın ıi instructiune ˆ program) cˆt ¸i enunturi ce descriu prelucr˘ri ce urmeaz˘ a fi ¸ ın a s ¸ a a detaliate abia ˆ momentul scrierii programului. 1. valorile n − 1.. sunt schemele logice ¸i limbajele s s algoritmice. Schemele logice sunt descrieri grafice ale algoritmilor ˆ care fiec˘rui ın a pas i se ata¸eaz˘ un simbol grafic.3. fac ca schemele logice s˘ fie s ¸ a din ce ˆ ce mai putin folosite (ˆ favoarea limbajelor algoritmice).22 CAPITOLUL 3. distanta[v] = infinit. ˆ aceast˘ ordine.3. cn−2 ... ˆ care apar enunturi de prelucr˘ri. fiecare a ın programator putˆnd s˘ conceap˘ propriul pseudocod. . ALGORITMI Valorile cn−1 . De exemplu: ın ¸ a for fiecare vˆrf v din V a { culoare[v] = alb. ca ˆ aa a ın cazul limbajelor de programare.. ˆ at ıntre limbajul natural sau cel matematic ¸i un limbaj de programare. c1 . predecesor[v]=-1. ca ˆ exemplele de mai sus. Se poate folosi sintaxa lima as a bajului de programare preferat. este dificil˘ ˆ ¸ ın a ıntrucˆt nu a sunt pu¸i ˆ evident˘ foarte clar pa¸ii algoritmului. s ın ¸a s Modalit˘¸i intermediare de descriere a algoritmilor. iar modul de ˆ antuire s a ınl˘ ¸ a blocurilor este specificat prin segmente orientate. 2.. numit bloc. a a a ın Si ˆ acest caz prelucrarea principal˘ este una repetitiv˘ constˆnd ˆ evaluarea ¸ ın a a a ın expresiei bck + ak pentru k luˆnd. } . 0. iar ultima valoare a ¸ a calculat˘ reprezint˘ valoarea restului (valoarea polinomului calculat˘ ˆ b). c0 reprezint˘ coeficientii cˆtului.3 Pseudocod Un limbaj algoritmic este o notatie care permite exprimarea logicii algorit¸ milor ˆ ıntr-un mod formalizat f˘r˘ a fi necesare reguli de sintax˘ riguroase. . ın ¸ ın 3. ın Nu exist˘ un anumit standard ˆ elaborarea limbajelor algoritmice. Un limbaj algoritmic mai este denumit ¸i pseudocod. cu conditia ca acesta s˘ a a a ¸ a permit˘ o descriere clar˘ ¸i neambigu˘ a algoritmilor.. a ın a dar ¸i evolutia modului de concepere a programelor.2 Scheme logice Scrierea unui program pornind de la un algoritm descris ˆ ıntr-un limbaj mai mult sau mai putin riguros. n − 2. Acest dezavantaj. a ın a 3.

3.4. LIMBAJ ALGORITMIC

23

3.4

Limbaj algoritmic
ˆ continuare prezent˘m un exemplu de limbaj algoritmic. In a

3.4.1

Declararea datelor

Datele simple se declar˘ sub forma: a <tip> <nume>; unde <tip> poate lua una dintre valorile: byte, short, int, long, float, double, boolean, char. Tablourile unidimensionale se declar˘ sub forma: a <tip> <nume> [n1 ..n2 ]; Elementele vectorului pot fi accesate cu ajutorul unui indice, care poate lua valori ˆ ıntre n1 ¸i n2 , sub forma: s <nume>[i] unde i poate lua orice valoare ˆ ıntre n1 ¸i n2 . s ˆ cazul tablourilor bidimensionale, o declaratie de forma: In ¸ <tip> <nume>[m1 ..m2 ][n1 ..n2 ]; specific˘ o matrice cu m2 − m1 + 1 linii ¸i n2 − n1 + 1 coloane. Fiecare element se a s specific˘ prin doi indici: a <nume>[i][j] unde i reprezint˘ indicele liniei ¸i poate avea orice valoare ˆ a s ıntre m1 ¸i m2 iar j s reprezint˘ indicele coloanei ¸i poate avea orice valoare ˆ a s ıntre n1 ¸i n2 . s

3.4.2

Operatii de intrare/ie¸ire ¸ s

Preluarea valorilor pentru datele de intrare este descris˘ sub forma: a read v1 , v2 , ...; unde v1 , v2 , ... sunt nume de variabile. Afi¸area rezultatelor este descris˘ sub forma: s a write e1 , e2 , ...;

24

CAPITOLUL 3. ALGORITMI

unde e1 , e2 , ... sunt expresii (ˆ particular pot fi constante sau variabile). ın Operatia de atribuire. Operatia de atribuire a unei valori c˘tre o variabil˘ ¸ ¸ a a se descrie prin: v = <expresie>; unde v este un nume de variabil˘, <expresie> desemneaz˘ o expresie aritmetic˘ a a a sau logic˘, iar ”=” este operatorul de atribuire. Pentru acesta din urm˘ pot fi a a folosite ¸i alte simboluri, ca de exemplu ”:=” sau ”←”. Expresiile pot fi descrise s conform regulilor utilizate ˆ matematic˘. ın a

3.4.3

Prelucr˘ri liniare a

O secvent˘ de prelucr˘ri se descrie ˆ modul urm˘tor: ¸a a ın a <prel 1>; <prel 2>; ... <prel n>; sau <prel 1>; <prel 2>; ... <prel n>; O astfel de scriere indic˘ faptul c˘ ˆ momentul executiei prelucr˘rile se a a ın ¸ a efectueaz˘ ˆ ordinea ˆ care sunt specificate. a ın ın

3.4.4

Prelucr˘ri alternative a

O prelucrare alternativ˘ complet˘ (cu dou˘ ramuri) este descris˘ prin: a a a a if <conditie> ¸ sau sub forma if <conditie> then <prel 1> else <prel 2>; ¸ unde <conditie> este o expresie relational˘. Aceast˘ prelucrare trebuie ˆ ¸eleas˘ ¸ ¸ a a ınt a ˆ modul urm˘tor: dac˘ conditia este adev˘rat˘ atunci se efectueaz˘ prelucrarea ın a a ¸ a a a <prel 1>, altfel se efectueaz˘ <prel 2>. a O prelucrare alternativ˘ cu o singur˘ ramur˘ se descrie prin: a a a if <conditie> <prel>; ¸ sau if <conditie> then <prel>; ¸ iar executia ei are urm˘torul efect: dac˘ conditia este satisfacut˘ atunci se efectueaz˘ ¸ a a ¸ a a prelucrarea specificat˘, altfel nu se efectueaz˘ nici o prelucrare ci se trece la a a urm˘toarea prelucrare a algoritmului. a <prel 1> else <prel 2>;

3.4. LIMBAJ ALGORITMIC

25

3.4.5

Prelucr˘ri repetitive a

Prelucr˘rile repetitive pot fi de trei tipuri: a • cu test initial, ¸ • cu test final ¸i s • cu contor. Prelucrarea repetitiv˘ cu test initial se descrie prin: Prelucrarea repetitiv˘ cu a ¸ a test initial se descrie prin: ¸ while <conditie> <prel>; ¸ sau while <conditie> do <prel>; ¸ ˆ momentul executiei, atˆt timp cˆt conditia este adevarat˘, se va executa In ¸ a a ¸ a instructiunea. Dac˘ conditia nu este la ˆ ¸ a ¸ ınceput satisf˘cut˘, atunci instructiunea a a ¸ nu se efectueaz˘ niciodat˘. a a Prelucrarea repetitiv˘ cu test final se descrie prin: a do <prel> while <conditie>; ¸ Prelucrarea se repet˘ pˆn˘ cˆnd conditia specificat˘ devine fals˘. ˆ acest caz a a a a ¸ a a In prelucrarea se efectueaz˘ cel putin o dat˘, chiar dac˘ conditia nu este satisfacut˘ a ¸ a a ¸ a la ˆ ınceput. Prelucrarea repetitiv˘ cu contor se caracterizeaz˘ prin repetarea prelucr˘rii a a a de un num˘r prestabilit de ori ¸i este descris˘ prin: a s a for i = i1 , i2 , ..., in <prel>; sau for i = i1 , i2 , ..., in do <prel>; unde i este variabila contor care ia, pe rˆnd, valorile i1 , i2 , ..., in ˆ aceast˘ a ın a ordine, prelucrarea fiind efectuat˘ pentru fiecare valoare a contorului. a Alte forme utilizate sunt: for i = vi to vf do <prel>;

ˆ care contorul ia valori consecutive cresc˘toare ˆ ın a ıntre vi ¸i vf , ¸i s s for i = vi downto vf do <prel>;

ˆ care contorul ia valori consecutive descresc˘toare ˆ ın a ıntre vi ¸i vf . s

26

CAPITOLUL 3. ALGORITMI

3.4.6

Subalgoritm

ˆ cadrul unui algoritm poate s˘ apar˘ necesitatea de a specifica de mai In a a multe ori ¸i ˆ diferite locuri un grup de prelucr˘ri. Pentru a nu le descrie ˆ s ın a ın mod repetat ele pot constitui o unitate distinct˘, identificabil˘ printr-un nume, a a care este numit˘ subalgoritm. Ori de cˆte ori este necesar˘ efectuarea grupului a a a de prelucr˘ri din cadrul subalgoritmului se specific˘ numele acestuia ¸i, eventual, a a s datele curente asupra c˘rora se vor efectua prelucrarile. Aceast˘ actiune se nume¸te a a ¸ s apel al subalgoritmului, iar datele specificate al˘turi de numele acestuia ¸i asupra a s c˘rora se efectueaz˘ prelucrarile se numesc parametri. a a ˆ urma traducerii ˆ In ıntr-un limbaj de programare un subalgoritm devine un subprogram. Un subalgoritm poate fi descris ˆ felul urm˘tor: ın a <nume subalg> (<tip> <nume p1>, <tip> <nume p2>, ... ) { ... /* prelucr˘ri specifice subalgoritmului */ a ... return <nume rezultat>; } unde <nume subalg> reprezint˘ numele subalgoritmului iar nume p1, nume p2, a ... reprezint˘ numele parametrilor. Ultimul enunt, prin care se returneaz˘ rezultatul a ¸ a calculat ˆ cadrul subalgoritmului, este optional. ın Modul de apel depinde de modul ˆ care subalgoritmul returneaz˘ rezultatele ın a sale. Dac˘ subalgoritmul returneaz˘ efectiv un rezultat, printr-un enunt de forma a a ¸ return <nume rezultat>; atunci subalgoritmul se va apela ˆ felul urm˘tor: ın a v=<nume subalg>(nume p1, nume p2, ...); Ace¸ti subalgoritmi corespund subprogramelor de tip functie. s ¸ Dac˘ ˆ subalgoritm nu apare un astfel de enunt, atunci el se va apela prin: a ın ¸ <nume subalg>(nume p1, nume p2, ...); variant˘ care corespunde subprogramelor de tip procedur˘. a a Observatie. Prelucr˘rile care nu sunt detaliate ˆ cadrul algoritmului sunt ¸ a ın descrise ˆ limbaj natural sau limbaj matematic. Comentariile suplimentare vor fi ın cuprinse ˆ ıntre /* ¸i */. Dac˘ pe o linie a descrierii algoritmului apare simbolul // s a atunci tot ce urmeaz˘ dup˘ acest simbol, pe aceea¸i linie cu el, este interpretat ca a a s fiind un comentariu (deci, nu reprezint˘ o prelucrare a algoritmului). a

3.4. LIMBAJ ALGORITMIC

27

3.4.7

Probleme rezolvate

1. Algoritmului lui Euclid. Descrierea ˆ pseudocod a algoritmului lui Euclid este urm˘toarea: ın a int a, b, d, i, r; read a, b; if (a<b) { d=a; i=b; } else { d=b; i=a; }; r = d % i; while (r ! = 0) { d=i; i=r; r=d % i; }; write i; 2. Schema lui Horner. Descrierea ˆ pseudocod a schemei lui Horner este urm˘toarea: ın a int n, a, b, i; read n, a, b; int a[0..n], c[0..n-1]; for i=n,0,-1 read a[i]; c[n-1]=b*a[n]; for i=1,n-1 c[n-i-1]=b*c[n-i]+a[n-i]; val:=b*c[1]+a[1]; write val; 3. Conversia unui num˘r natural din baza 10 ˆ baza 2. a ın Fie n un num˘r ˆ a ıntreg pozitiv. Pentru a determina cifrele reprezentarii ˆ ın baza doi a acestui num˘r se poate folosi urm˘toarea metod˘: a a a Se ˆ ımparte n la 2, iar restul va reprezenta cifra de rang 0. Cˆtul obtinut la a ¸ ˆ ımpartirea anterioar˘ se ˆ a ımparte din nou la 2, iar restul obtinut va reprezenta cifra ¸ de ordin 1 ¸.a.m.d. Secventa de ˆ artiri continu˘ pˆ a la obtinerea unui cˆt nul. s ¸ ımp˘ ¸ a ın˘ ¸ a Descrierea ˆ pseudocod a acestui algoritm este: ın int n, d, c, r; read n; d = n; c = d / 2; /* cˆtul ˆ artirii ˆ a ımp˘ ¸ ıntregi a lui d la 2 */ r = d % 2; /* restul ˆ artirii ˆ ımp˘ ¸ ıntregi a lui d la 2 */ write r; while (c != 0) { d = c; c = d / 2; /* cˆtul ˆ artirii ˆ a ımp˘ ¸ ıntregi a lui d la 2 */ r = d % 2; /* restul ˆ artirii ˆ ımp˘ ¸ ıntregi a lui d la 2 */ write r; } 4. Conversia unui num˘r ˆ a ıntreg din baza 2 ˆ baza 10. ın

28

CAPITOLUL 3. ALGORITMI

Dac˘ bk bk−1 ...b1 b0 reprezint˘ cifrele num˘rului ˆ baza 2, atunci valoarea ˆ a a a ın ın baza 10 se obtine efectuˆ calculul: ¸ ınd (bk bk−1 ...b1 b0 )10 = bk 2k + bk−1 2k−1 + ... + b1 2 + b0 De¸i calculul de mai sus este similar cu evaluarea pentru X = 2 a polinomului s P [X] = bk X k + bk−1 X k−1 + ... + b1 X + b0 prelucrare pentru care ar putea fi folosit algoritmul corespunz˘tor schemei lui a Horner, ˆ continuare prezent˘m o alt˘ variant˘ de rezolvare a acestei probleme, ın a a a care folose¸te un subalgoritm pentru calculul puterilor unui num˘r ˆ s a ıntreg: int k, i, s; putere(int a, int n) read k; { int b[0..k]; int i, p; read b; p = 1; s = 0; for i=2,n p = p*a; for i=0,k s = s+b[i] * putere(2,i); return p; write s; } 5. S˘ se scrie un algoritm pentru determinarea tuturor divizorilor naturali ai a unui num˘r ˆ a ıntreg. Rezolvare. Fie n num˘rul ai c˘rui divizori trebuie determinati. Evident 1 ¸i a a ¸ s |n| sunt divizori ai lui n. Pentru a determina restul divizorilor este suficient ca ace¸tia s˘ fie c˘utati printre elementele multimii {2, 3, ..., [|n|]} cu [x] desemnˆnd s a a ¸ ¸ a partea ˆ ıntreag˘ a lui x. a Algoritmul poate descris ˆ modul urm˘tor: ın a int n, d; read n; write 1; /* afi¸area primului divizor */ s for d = 2, [|n|/2] if (d divide pe n) then write d; write |n| /* afi¸area ultimului divizor */ s 6. S˘ se scrie un algoritm pentru determinarea celui mai mare element dintra un ¸ir de numere reale. s Rezolvare. Fie x1 , x2 , ..., xn ¸irul analizat. Determinarea celui mai mare eles ment const˘ ˆ initializarea unei variabile de lucru max (care va contine valoarea a ın ¸ ¸ maximului) cu x1 ¸i compararea acesteia cu fiecare dintre celelalte elemente ale s ¸irului. Dac˘ valoarea curent˘ a ¸irului, xk , este mai mare decˆt valoarea variaas a a s a bilei max atunci acesteia din urm˘ i se va da valoarea xk . Astfel, dup˘ a k − 1 a a comparatie variabila max va contine valoarea maxim˘ din sub¸irul x1 , x2 , ..., xk . ¸ ¸ a s Algoritmul poate fi descris ˆ modul urm˘tor: ın a

3.4. LIMBAJ ALGORITMIC int k, n; read n; double x[1..n], max; /* vectorul ¸i variabila de lucru */ s read x; /* preluarea elementelor ¸irului */ s max = x[1]; for k = 2, n if (max < x[k]) then max = x[k]; write max; 7. S˘ se aproximeze, cu precizia ε, limita ¸irului a s
n

29

sn =
k=0

1 . k!

Rezolvare. Calculul aproximativ (cu precizia ε) al limitei ¸irului sn const˘ ˆ s a ın 1 calculul sumei finite sk , unde ultimul termen al sumei, tk = k! , are proprietatea tk a ¸ a tk < ε. ˆ Intrucˆt tk+1 = k+1 , aceast˘ relatie va fi folosit˘ pentru calculul valorii a termenului curent (permitˆnd mic¸orarea num˘rului de calcule). ¸a s a double eps, t, s; int k; k=1; /* initializare indice */ ¸ t=1; /* initializare termen */ ¸ s=1; /* initializare suma */ ¸ do { s=s+t; /* ad˘ugarea termenului curent */ a k=k+1; t=t/k; /* calculul urm˘torului termen */ a } while (t ≥ eps); s=s+t; (* ad˘ugarea ultimului termen *) a write s; 8. Fie A o matrice cu m linii ¸i n coloane, iar B o matrice cu n linii ¸i p coloane, s s ambele avˆnd elemente reale. S˘ se determine matricea produs C = A × B. a a Rezolvare. Matricea C va avea m linii ¸i p coloane, iar fiecare element se s determin˘ efectuˆnd suma: a a
n

ci,j =
k=1

ai,k · bk,j ,

1 ≤ i ≤ m, 1 ≤ j ≤ p.

ˆ felul acesta calculul elementelor matricei C se efectueaz˘ prin trei cicluri In a imbricate (unul pentru parcurgerea liniilor matricei C, unul pentru parcurgerea coloanelor matricei C, iar unul pentru efectuarea sumei specificate mai sus).

30

CAPITOLUL 3. ALGORITMI int m, n, p; /* dimensiunile matricelor */ read m, n, p; double a[1..m][1..n], b[1..n][1..p], c[1..m][1..p]; int i, j, k; /* indici */ read a; /* citirea matricei a */ read b; /* citirea matricei b */ for i=1,m for j=1,p { c[i,j]=0; for k=1,n c[i][j]=c[i][j]+a[i][k]*b[k][j]; } write c;

/* matrice */

3.4.8

Probleme propuse

1. Fie D o dreapt˘ de ecuatie ax+by+c = 0 ¸i (C) un cerc de centru O(x0 , y0 ) a ¸ s ¸i raz˘ r. S˘ se stabileasc˘ pozitia dreptei fat˘ de cerc. s a a a ¸ ¸a Indicatie. Se calculeaz˘ distanta de la centrul cercului la dreapta D utilizˆnd ¸ a ¸ a formula: |ax0 + by0 + c| √ . d= a2 + b2 Dac˘ d ≥ r+ε atunci dreapta este exterioar˘ cercului, dac˘ d ≤ r−ε atunci dreapta a a a este secant˘, iar dac˘ r − ε < d < r + ε atunci este tangent˘ (la implementarea a a a egalitatea ˆ ıntre dou˘ numere reale ...). a 2. S˘ se genereze primele n elemente ale ¸irurilor ak ¸i bk date prin relatiile a s s ¸ de recurent˘: ¸a ak+1 = 5ak + 3 , ak + 3 bk = ak + 3 , ak + 1 k ≥ 0, a0 = 1.

3. S˘ se determine r˘d˘cina p˘trat˘ a unui num˘r real pozitiv a cu precizia a a a a a u ε = 0.001, folosind relatia de recurent˘: ¸ ¸a xn+1 = 1 2 xn + a xn , x1 = a.

Precizia se consider˘ atins˘ cˆnd |xn+1 − xn | < ε. a a a 4. Fie A o matrice p˘tratic˘ de dimensiune n. S˘ se transforme matricea A, a a a prin interrschimb˘ri de linii ¸i de coloane, astfel ˆ at elementele de pe diagonala a s ıncˆ principal˘ s˘ fie ordonate cresc˘tor. a a a

3.4. LIMBAJ ALGORITMIC

31

5. S˘ se determine cel mai mare divizor comun al unui ¸ir de numere ˆ a s ıntregi. 6. S˘ se calculeze coeficientii polinomului a ¸ P [X] = (aX + b)n , a, b ∈ Z, n ∈ N.

7. Fie A o matrice p˘tratic˘. S˘ se calculeze suma elementelor din fiecare zon˘ a a a a (diagonala principal˘, diagonala secundar˘, etc.) marcat˘ ˆ figura urm˘toare: a a a ın a

a.

b.

c.

Figura 3.1: Zone ˆ matrice p˘tratic˘ ın a a

8. Fie x1 , x2 , ..., xn ∈ Z r˘d˘cinile unui polinom cu coeficienti ˆ a a ¸ ıntregi: P [X] = an X n + an−1 X n−1 + ... + a1 X + a0 . S˘ se determine coeficientii polinomului. a ¸ 9. S˘ se determine toate r˘d˘cinile rationale ale polinomului P [X] care are a a a ¸ coeficienti ˆ ¸ ıntregi. 140. Fie [P1 , P2 , ..., Pn ] un poligon convex dat prin coordonatele carteziene ale vˆrfurilor sale (ˆ ordine trigonometric˘). S˘ se calculeze aria poligonului. a ın a a 11. Fie f : [a, b] → R o functie continu˘ cu proprietatea c˘ exist˘ un unic ξ ∈ ¸ a a a (a, b) care are proprietatea c˘ f (ξ) = 0. S˘ se aproximeze ξ cu precizia ε = 0.001 a a utilizˆnd metoda bisectiei. a ¸ 12. Fie P ¸i Q polinoame cu coeficienti ˆ s ¸ ıntregi. S˘ se determine toate r˘d˘cinile a a a rationale comune celor dou˘ polinoame. ¸ a 13. S˘ se determine toate numerele prime cu maxim 6 cifre care r˘mˆn prime a a a ¸i dup˘ ”r˘sturnarea” lor (r˘sturnatul num˘rului abcdef este f edcba). s a a a a

32

CAPITOLUL 3. ALGORITMI

3.5

Instructiuni corespondente limbajului algorit¸ mic

3.5.1

Declararea datelor

Datele simple se declar˘ sub forma: a <tip> <nume>; sau <tip> <nume>= literal; unde <tip> poate lua una dintre urm˘toarele valori: byte, short, int, long, float, a double, boolean, char. ˆ exemplul urm˘tor sunt prezentate cˆteva modalit˘¸i In a a at de declarare pentru diferite tipuri de variabile. class Literali { public static void main(String args[]) { long l1 = 5L; long l2 = 12l; int i1hexa = 0x1; int i2hexa = 0X1aF; int i3octal = 01; long i4octal = 012L; long i5LongHexa = 0xAL; float f1 = 5.40F; float f2 = 5.40f; float f3 = 5.40e2f; float f4 = 5.40e+12f; float f5 = 5.40; // da eroare, trebuie cast double d1 = 5.40; // implicit este double ! double d2 = 5.40d; double d3 = 5.40D; double d4 = 5.40e2; double d5 = 5.40e+12d; char c1 = ’r’; char c2 = ’\u4567’; } }

Ca ¸i celelalte a a ¸˘ s variabile. 32 sau 64 de biti. ele pot fi folosite de metodele statice. valoarea atribuit˘ implicit este null. Din aceast˘ cauz˘. Fiecare tip are o anumit˘ s a dimensiune. spre a s a deosebire de C/C++.79E+308 Valoare initiala 0 0 0 0 0 0 f alse null Cifre semnificative 6-7 14-15 Tabelul 3. Tabelul anterior prezint˘ cˆteva exemple ˆ acest ¸ a a a a ın sens. INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ 33 Java define¸te mai multe tipuri primitive de date.3. a a Tablourile unidimensionale se declar˘ sub forma: a <tip>[ ] <nume> =new <tip>[n].1: Tipurile primitive de date ˆ Java ın Variabilele pot fi initializate la declararea lor sau ˆ momentul utiliz˘rii lor ¸ ın a efective.4E+38 +-1. ¸ s a a s s a ın Tip byte short int long float double boolean char Dimensiune (octeti) ¸ 1 2 4 8 4 8 1 2 Valoare minima −27 −215 −231 −263 +-1. unde un ˆ ıntreg poate fi reprezentat pe 16. Variabilele statice sunt initializate la ˆ arcarea a a ¸ ınc˘ codului specific unei clase ¸i exist˘ chiar ¸i dac˘ nu exist˘ nici o instant˘ a clasei s a s a a ¸a respective.94E-324 Valoare maxima 27 − 1 215 − 1 231 − 1 263 − 1 +-3. . ˆ ¸ ın functie de arhitectura ma¸inii. 32 s ¸ a s a sau 64 de biti ¸i s˘ produc˘ acela¸i rezultat pe fiecare ma¸in˘ ˆ parte.5. indiferent de ma¸ina pe care ruleaz˘. o valoare de tip ˆ ¸ s ıntreg ˆ Java va ocupa ˆ ın ıntotdeauna 32 de biti. care este independent˘ de caracteristicile ma¸inii gazd˘. ¸ Se vede din tabel c˘ unele tipuri de variabile au posibilitatea s˘ reprezinte un a a spectru mai mare de numere decˆt altele. Conversiile ˆ ıntre diferitele tipuri sunt permise (acolo unde au semnificatie). Astfel. de pild˘ variabile de tip clas˘. dac˘ nu sunt explicit initializate. limbajul Java suport˘ ¸i tipuri de date create In a a s de utilizator. a ¸ a Modificatorul static este folosit pentru a specifica faptul c˘ variabila are a o singur˘ valoare. interfata sau tablou. comun˘ tuturor instantelor clasei ˆ care ea este declarat˘. a a ¸ ın a Modificarea valorii acestei variabile din interiorul unui obiect face ca modificarea s˘ fie vizibil˘ din celelalte obiecte. Dac˘ valoarea nu este specificat˘ explicit atunci variabila se initializeaz˘ a a ¸ a cu o valoare initial˘ implicit˘. sau <tip> <nume>[ ] =new <tip>[n]. Aceast˘ consecvent˘ este esential˘ ¸ s a a ¸a ¸ a deoarece o aceea¸i aplicatie va trebui s˘ ruleze pe ma¸ini cu arhitectur˘ pe 16.4E-45 +-4. a ˆ afara tipurilor de baz˘.

out).nval. sub forma: s StreamTokenizer st = new new new st. s 3.print(v). s ˆ cazul tablourilor bidimensionale. Scrierea valorii unei variabile v pe ecran se poate face sub forma: System.print(v). out. . sub forma: <nume>[i] unde i poate lua orice valoare ˆ ıntre 0 ¸i n − 1. Fiecare element se specific˘ prin doi a s a indici: <nume>[i][j] unde i reprezint˘ indicele liniei ¸i poate avea orice valoare ˆ a s ıntre 0 ¸i m − 1 iar j s reprezint˘ indicele coloanei ¸i poate avea orice valoare ˆ a s ıntre 0 ¸i n − 1. o declaratie de forma: In ¸ <tip>[ ] [ ] <nume> = new <tip>[m][n]. specific˘ o matrice cu m linii ¸i n coloane.5.nextToken(). iar dintr-un fi¸ier (de exemplu fis. int vi = StreamTokenizer( BufferedReader( FileReader("fis.in"))).parseInt(br.2 Operatii de intrare/ie¸ire ¸ s Preluarea unei valori de tip int de la tastatur˘ se poate face sub forma: a BufferedReader br = new BufferedReader( new InputStreamReader(System. sau <tip> <nume> [ ] [ ] = new <tip>[m][n]. iar ˆ ıntr-un fi¸ier (de exemplu fis.34 CAPITOLUL 3.in).out. ALGORITMI Elementele vectorului pot fi accesate cu ajutorul unui indice.readLine()). (int) st.close().in)). sub forma: s PrintWriter out = new PrintWriter( new BufferedWriter( new FileWriter("fis. out.out"))). int vi=Integer.

altfel se efectueaz˘ <instr 2>. <instr n>.5.3 Prelucr˘ri liniare a O secvent˘ de prelucr˘ri se descrie ˆ modul urm˘tor: ¸a a ın a <instr 1>.4 Prelucr˘ri alternative a O prelucrare alternativ˘ complet˘ (cu dou˘ ramuri) este descris˘ prin: a a a a if (<conditie>) ¸ <instr 1> else <instr 2>.5. atunci prelucrarea nu se a a efectueaz˘ niciodat˘. <instr 2>. <instr n>. O astfel de scriere indic˘ faptul c˘ ˆ momentul executiei instructiunile se a a ın ¸ ¸ efectueaz˘ ˆ ordinea ˆ care sunt specificate. .3.. Aceast˘ prelucrare trebuie ˆ ¸eleas˘ ¸ ¸ a a ınt a ˆ modul urm˘tor: dac˘ conditia este adev˘rat˘ atunci se efectueaz˘ prelucrarea ın a a ¸ a a a a <instr 1>. unde <conditie> este o expresie relational˘. altfel nu se efectueaz˘ nici o prelucrare ci se trece la ¸ a a urm˘toarea prelucrare a algoritmului. O prelucrare alternativ˘ cu o singur˘ ramur˘ se descrie prin: a a a if (<conditie>) <instr>. a a Prelucrarea repetitiv˘ cu test final se descrie prin: a . ¸ iar executia ei are urm˘torul efect: dac˘ conditia este satisfacut˘ atunci se efectueaz˘ ¸ a a ¸ a a instructiunea specificat˘.5. a 3.5 Prelucr˘ri repetitive a Prelucr˘rile repetitive pot fi de trei tipuri: a • cu test initial. ¸ ˆ momentul executiei. Dac˘ conditia nu este la ˆ a ¸ ınceput satisf˘cut˘. atˆt timp cˆt conditia este adevarat˘.. <instr 2>. sau <instr 1>.. Prelucrarea repetitiv˘ cu test initial se descrie prin: a ¸ while (<conditie>) <instr>. a ın ın 3. ¸ • cu test final ¸i s • cu contor. INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ 35 3. se va executa In ¸ a a ¸ a prelucrarea. ..5.

identificabil˘ printr-un nume. ˆ acest ¸ a a a a ¸ a a In caz prelucrarea se efectueaz˘ cel putin o dat˘.. ˆ general <instr1> reprezint˘ etapa de initializare a contorului. mai precis. <nume sp> reprezint˘ numele subprogramului. reprezint˘ numele parametrilor.. ALGORITMI do <instr> while (<conditie>). Ori de cˆte ori este necesar˘ efectuarea grupului de prelucr˘ri din cadrul a a a programului se specific˘ numele acestuia ¸i.. Prelucrarea repetitiv˘ cu contor se caracterizeaz˘ prin repetarea prelucr˘rii a a a de un num˘r prestabilit de ori ¸i este descris˘ prin: a s a for(<instr1> . return <nume rezultat>. <instr2>) <instr3>.5. eventual. Dac˘ subprogramul returneaz˘ efectiv un rezultat. Pentru a nu le descrie ˆ mod repetat s ın a ın ele pot constitui o unitate distinct˘. . . ˆ Java functiile ¸i procedurile se numesc a a In ¸ s metode. Modul de apel depinde de modul ˆ care subprogramul returneaz˘ rezultatele ın a sale. iar a a numep1. Ultimul enunt.. <conditie>. printr-un enunt de forma a a ¸ return <nume rezultat>. } unde <tipr> reprezint˘ tipul rezultatului returnat (void dac˘ subprogramul nu a a returneaz˘ nici un rezultat). ) { . <instr2> In a ¸ reprezint˘ etapa de incrementare a contorului. care este numit˘ a a a subprogram sau. /* prelucr˘ri specifice subprogramului */ a .6 Subprograme ˆ cadrul unui program poate s˘ apar˘ necesitatea de a specifica de mai multe In a a ori ¸i ˆ diferite locuri un grup de prelucr˘ri... datele curente asupra c˘rora a s a se vor efectua prelucrarile. prin care se a ¸ returneaz˘ rezultatul calculat ˆ cadrul subprogramului. numep2. ¸ Instructiunea se repet˘ pˆn˘ cˆnd conditia specificat˘ devine fals˘.. chiar dac˘ conditia nu este satisfaa ¸ a a ¸ cut˘ la ˆ a ınceput. Aceast˘ actiune se nume¸te apel al subprogramului. functie (dac˘ returneaz˘ un rezultat) sau procedur˘ ¸ a a a (dac˘ nu returneaz˘ nici un rezultat).. Un subprogram poate fi descris ˆ felul urm˘tor: ın a <tipr> <nume sp> (<tipp1> <numep1>. trebuie pus numai dac˘ a ın a <tipr> nu este void. a ¸ s iar datele specificate al˘turi de numele acestuia ¸i asupra c˘rora se efectueaz˘ a s a a prelucrarile se numesc parametri. <instr3> reprezint˘ instructiunea a a ¸ care se execut˘ ˆ mod repetat cˆt timp conditia <conditie> are valoarea true. . <tipp2> <numep2>.36 CAPITOLUL 3. a ın a ¸ 3.

out. Metoda static int maxFibo ( long nr ) a a returneaz˘ indicele celui mai mare element din ¸irul lui Fibonacci care este mai a s mic sau egal cu parametrul nr.k<=n. atunci el se va apela prin: a ın ¸ <nume sp>(nume p1.). System.io.. nrt=0.println(" "+Long.in)). INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ atunci subprogramul se va apela ˆ felul urm˘tor: ın a v=<nume sp>(nume p1. nume p2. variant˘ care corespunde subprogramelor de tip procedur˘. ˆ sum˘ de cˆt mai putini termeni Fibonacci.. 37 Aceste subprograme corespund subprogramelor de tip functie. class DescFibo { static int n=92.y. for(k=0. BufferedReader br = new BufferedReader( new InputStreamReader(System.readLine()). x=Long. f[2]=1.out. Dac˘ pe o linie a descrierii algoritmului apare simbolul // s a atunci tot ce urmeaz˘ dup˘ acest simbol. f[0]=0. k..k++) System. import java. Prelucr˘rile care nu sunt detaliate ˆ cadrul algoritmului sunt ¸ a ın descrise ˆ limbaj natural sau limbaj matematic. ¸ Dac˘ ˆ subprogram nu apare un astfel de enunt.7 Probleme rezolvate 1. ın a a ¸ Rezolvare: Programul urm˘tor calculeaz˘ ¸i afi¸eaz˘ primii 92 de termeni a a s s a din ¸irul Fibonacci (mai mult nu este posibil f˘r˘ numere mari!).5. este interpretat ca a a s fiind un comentariu (deci.MAX_VALUE+" = Long.MAX_VALUE"). System.parseLong(br. f[1]=1..println(k+" : "+f[k]). . a a Observatie.k++) f[k]=f[k-1]+f[k-2].print("x = "). ¸i descompune s aa s num˘rul x introdus de la tastatur˘.k<=n. .). Descompunere Fibonacci. for(k=3. a 3. . de cel mult a a a 18-19 cifre. public static void main (String[]args) throws IOException { long x. Comentariile suplimentare vor fi ın cuprinse ˆ ıntre /* ¸i */.out.3. static long[] f=new long[n+1]. nu reprezint˘ o prelucrare a programului). nume p2. pe aceea¸i linie cu el.*.5. int iy. S˘ se descompun˘ un num˘r natural.

a=1. x=x-y.println(nrt+" : "+x+" f["+iy+"] = "+y). nrt++. y=f[iy]. for(k=1. System. S˘ se afi¸eze primii 10 termeni ai ¸irului a s s Sn ¸i s˘ se precizeze ˆ dreptul fiec˘rui termen dac˘ este num˘r prim. s=-b/a. b. nnp=0.k++) if (f[k]>nr) break.k<=n. } } De exemplu.println("x = "+x). Fie Sn = xn +xn unde x1 ¸i x2 sunt r˘d˘cinile ecuatiei cu coeficienti ˆ 2 1 ax + bx + c = 0 (vom considera a = 1!). pentru x = 5678 pe ecran apare: 1 2 3 4 5 6 7 : : : : : : : 5678 f[19] = 418 1497 f[16] = 987 510 f[14] = 377 133 f[11] = 89 44 f[9] = 34 10 f[6] = 8 2 f[3] = 2 s a a ¸ ¸ ıntregi 2.b=1. k.out. s. long[] ss=new long[n+1].out.c=2. return k-1. ALGORITMI System. a a s ın Rezolvare: 2 class e02 { public static void main(String[] args) { int a. n=10. while(x>0) { iy=maxFibo(x). . } } static int maxFibo(long nr) { int k.38 CAPITOLUL 3. iar dac˘ nu s a ın a a a a este num˘r prim s˘ se afi¸eze descompunerea ˆ factori. c. p.

out.k<=n.} d=3. if(nr%d==0) return false. if((nr==2)||(nr==3)) return true.} d=d+2.abs(ss[k])).} while(nr%d==0){System.out.abs(ss[k]))) System.out.out. while((d*d<=nr)&&(nr!=1)) { while(nr%d==0){System. } if(nr!=1) System. while((nr%d!=0)&&(d*d<=nr)) d=d+2. return.print(d+"").k<=n.println(k+" : "+ss[k]+" PRIM "+(++nnp)). long d=3. nr=nr/d. else { System.out.println("nnp = "+nnp). INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ p=c/a.print(d+" ").k++) if(esteprim(Math. }// main static void descfact(long nr) { long d=2.3.out. if(nr%2==0) return false. ss[2]=s*s-2*p. descfact(Math. nr=nr/d.out. } }// class Pe ecran apar urm˘toarele rezultate: a 1 : -1 = 2 : -3 PRIM 1 39 . } static boolean esteprim(long nr) { if((nr==0)||(nr==1)) return false. else System.println(nr). for(k=3.k++) ss[k]=s*ss[k-1]-p*ss[k-2].print(k+" : "+ss[k]+" = ").println(). } System. ss[1]=s.5.println(). for(k=1. if((nr==0)||(nr==1)){System. else return true.out.

for(k=1.alfa). s˘ se precizeze s ın ¸ ın a dac˘ respectivul coeficient este num˘r prim. Se consider˘ functia f (x) = P (x)eαx unde P (x) este un polinom de grad n a ¸ cu coeficienti ˆ ¸ ıntregi.int alfa) . S˘ se afi¸eze toate derivatele pˆn˘ la ordinul m ale functiei f .40 3 : 5 PRIM 2 4 : 1 = 5 : -11 PRIM 3 6 : 9 = 3 3 7 : 13 PRIM 4 8 : -31 PRIM 5 9 : 5 PRIM 6 10 : 57 = 3 19 nnp = 6 Press any key to continue. afisv(p.0). a s a a ¸ ¸i.println("GATA!!!"). long[] p=new long[n+1]. iar dac˘ nu este num˘r prim s˘ se a a a a a afi¸eze descompunerea ˆ factori.k). p[3]=1.println(npmax+" "+pmax).out. ¸i care este ordinul derivatei ˆ care apare acest cel mai a s ın mare num˘r prim.out.out. p=deriv(p. m=10. Toat˘ rezolvarea problemei se reduce la determinarea s a coeficientilor polinomului Q ˆ functie de coeficientii polinomului P . System. a Rezolvare: Derivata functiei f are forma Q(x)eαx unde Q este un polinom de ¸ acela¸i grad cu polinomul P . ¸ ın ¸ ¸ class e03 { static long npmax=1. ˆ dreptul coeficientilor polinoamelor care apar ˆ aceste derivate. p[0]=1.pmax=0.k++) { System.. k. alfa=1. public static void main(String[] args) { int n=7. ALGORITMI 3. } static long[] deriv(long[] a. CAPITOLUL 3..print("derivata = "+k).k<=m. p[7]=1. afisv(p. } System. s˘ se afi¸eze care este cel mai mare s ın a s num˘r prim care apare. De asemenea.

i>=0. k.length-1. } System.println().print(d+" "). return b.println(i+" : "+x[i]+" PRIM "). INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ { int n=a.abs(x[i])) { npmax=Math. b[n]=a[n]*alfa.out.out. nr=nr/d.k++) b[k]=(k+1)*a[k+1]+a[k]*alfa.println().int ppp) { int n=x. return.5. for(k=0. System.3. long[] b=new long[n+1]. } 41 .i--) if(esteprim(Math. } } else { System.out.k<=n-1.out.abs(x[i])).out.out. if((nr==0)||(nr==1)) { System. } while(nr%d==0) { System.print(i+" : "+x[i]+" = "). int i. } static void afisv(long[] x. if(npmax<Math. } static void descfact(long nr) { long d=2.println(). pmax=ppp. for(i=n.abs(x[i]). descfact(Math.length-1.abs(x[i]))) { System.

plecˆnd de la fractii date a a ¸ ¸ a ¸ (ca r˘d˘cini). while((nr%d!=0)&&(d*d<=nr)) d=d+2. Rezolvare: Se caut˘ r˘d˘cini rationale formate din fractii ˆ care num˘r˘torul a a a ¸ ¸ ın aa este divizor al termenului liber iar numitorul este divizor al termenului dominant. for(alfa=1. else return true.println(nr). if(nr%2==0) return false. 1}. q={2.alfa<=moda0. nr=nr/d.2. int n=a. int[] a=genPol(p. } }// class 4. R˘d˘cini rationale.3. if(nr%d==0) return false. } if(nr!=1) System.modan=Math.q). int moda0=Math.3. if((nr==2)||(nr==3)) return true. ALGORITMI static boolean esteprim(long nr) { if((nr==0)||(nr==1)) return false. S˘ se determine toate r˘d˘cinile rationale ale unei a a ¸ a a a ¸ ecuatii cu coeficienti ˆ ¸ ¸ ıntregi.2.-1}. Programul care urmeaz˘ genereaz˘ coeficientii ecuatiei.println().length-1. long d=3.abs(a[0]).1. } CAPITOLUL 3. 3.beta.42 d=3.out. } d=d+2. else System.3. public static void main(String[] args) { int[] p={1.out. while((d*d<=nr)&&(nr!=1)) { while(nr%d==0) { System.alfa++) .out. ¸i apoi determin˘ r˘d˘cinile rationale a a s a a a ¸ class RadaciniRationale // generez p_i/q_i { static int k=0.abs(a[n]).print(d+" ").-2.alfa.

beta)!=1) continue. INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ { 43 if(moda0%alfa!=0) continue.j. for(k=0.length-1. int i.out. afisv(p).println("x["+(++k)+"] = -"+alfa+"/"+beta+" "). }// genPol() static int[] pxq(int[] p.-alfa. // q initializat "aiurea" . if(cmmdc(alfa. p=pxq(p. if (f(a.//p=b[0] X -a[0] q={13. if (f(a. int[] pq=new int[gradpq+1].a_i { int n=a.5.k<=gradpq. for(i=0.j<=gradq.length.out.length-1. for(int k=1.j++) pq[i+j]+=p[i]*q[j].k++) { q[0]=-a[k].i<=gradp. }// for beta }// for alfa }// main static int[] genPol(int[] a.beta++) { if(modan%beta!=0) continue.beta)==0) System.k.k++) pq[k]=0. int beta) { . int[] p={-a[0].i++) for(j=0. return pq.pentru dimensiune ! afisv(p).k<n. } static int f(int[]a. q[1]=b[k]. } return p.int[] q) { int gradp=p. for(beta=1.13}.3. int gradpq=gradp+gradq.int alfa.println("x["+(++k)+"] = "+alfa+"/"+beta+" ").q).b[0]}.beta)==0) System.beta<=modan. int[] b) // X-a_i/b_i==>b_i X . gradq=q.alfa.

// ca sa inceapa while !!! while (r > 0){c=d/i. i=r.println().k++) s+=a[k]*putere(alfa.r. a s ımp˘ ¸ class e05 .length-1. }// afisv() }// class 5. Suma ¸ a a a s trebuie calculat˘ simulˆnd operatiile de adunare. return s.} r=123.length-1.k<=n. cu ¸ s ımp˘ ¸ numere mari. i=b. } static void afisv(int[] a) { for(int i=a.k++) p*=a.k<=n.i.n-k). int b) { int d. S˘ se afi¸eze frecventa cifrelor care apar ˆ a s ¸ ın n f (n) = k=0 1 n C 2k n+k netinˆnd cont de faptul c˘ f (n) are o expresie mult mai simpl˘. r=d%i.print(a[i]+" "). d=i. ALGORITMI int n=a.44 CAPITOLUL 3. return p.c.k. for(k=0.out. Rezolvare: Functia se pune sub forma: ¸ 1 f (n) = n 2 n n 2n−k Cn+k k=0 Se calculeaz˘ suma. } static int cmmdc(int a.} else {d=b. ˆ a a ¸ ınmultire ¸i ˆ artire la 2.i--) System. ¸i anume 2n . int n) { int p=1. ¸i apoi se fac n ˆ artiri succesive la 2. if (a>b) {d=a.i>=0.s=0. System.out. i=a.} return d.k)*putere(beta. } static int putere(int a. for(int k=1.

t=a[k]%2.} } return b.k--) {a[k]+=10*t.length. int[] b=new int[nb+1].println("GATA").k>=0. int[] s.putere(2. b[k]=a[k]/2. 45 . } static void fcifre(int[] x) { int i.i++) f[x[i]]++. fcifre(s). for(k=1. int[] p.length-1.n<=12. for(k=na-1.n-k)). s=suma(s. if(a[na]==1) nb=na-1. k. }//main() static int[] impartLa2(int[] a) { int na.k.k<=n. int[] f=new int[10]. System. } System. } afisv(s).out.k<=n.n). afisv(s). t=a[k]%2.i<x. if(na==nb) for(k=na.nb.print(n+" : "). else nb=na. for(k=0.p).5.out.} else { t=a[na]. for(i=0.3.k++) s=impartLa2(s).k++) { p=inm(comb(n+k.n++) { s=nrv(0).k>=0. INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ { public static void main (String[]args) { int n.k--){a[k]+=10*t. for(n=10. na=a.t=0. b[k]=a[k]/2.

} } static int[] inm(int[]x. j.i<=9.j++) yy[j]=y[j].46 CAPITOLUL 3.i<ncx.j<m.println().i<n. t. int[] y) { int i. for(j=0.out.i++) xx[i]=x[i]. int[] yy=new int[ncz]. int[] z=new int[ncz].int[]y) { int t.i<=ncz-2.i++) System. t=a[j][i+j]/10. z[i]=z[i]%10.i++){z[i]=xx[i]+yy[i]+t. } t=0.length. for(i=0.i++) zz[i]=z[i]. else ncz=ncy+1. for(i=0.out.out. System.i<ncz.} if(z[ncz-1]!= 0) return z.length. } static int[] suma(int[] x. ncy=y. t=z[i]/10. a[j][i+j]=a[j][i+j]%10. int[] xx=new int[ncz]. int[][]a=new int[m][n+m].println(). ncx=x. return zz.println(i+" : "+f[i]). i. ALGORITMI System.j++) { t=0. int[]z=new int[m+n]. ncz.length. if(ncx>ncy) ncz=ncx+1. for(i=0.j++) { .j<ncy. else { int[]zz=new int[ncz-1]. t=0. j. for(j=0. } a[j][i+j]=t.length. n=x. for(j=0.j<m+n. for(i=0.i++) { a[j][i+j]=y[j]*x[i]+t. for(i=0. m=y.

out. int n) { int[] rez. int k.k++) rez=inm(rez. System. for(k=1. nr=nrrez. rez=nrv(1). while(nr!=0){x[nc]=nr%10. z[j]=z[j]%10.i++) z[j]=z[j]+a[i][j]. else { int[]zz=new int[m+n-1].println().} return x. } 47 .3.i<m. return rez. z[j]=z[j]+t. nr=nr/10. System.length). while(nr!=0) {nc++.out.i<=m+n-2. nc++.print(x[i]).i--) System.length-1. for(i=x. for(i=0.out. nc=0.nrv(a)). } } static void afisv(int[]x) { int i. INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ z[j]=0.i>=0. } static int[] nrv(int nr) { int nrrez=nr.i++) zz[i]=z[i].} int[]x=new int [nc]. nr=nr/10. for(i=0. } if(z[m+n-1]!= 0) return z.print(" *** "+x.5. t=z[j]/10. } static int[] putere (int a. return zz.k<=n. nc=0.

for(j=2. S˘ se afi¸eze S(n. Se vor implementa operatiile cu numere mari. y[j]=y[j]/d. } } rez=nrv(1). return rez.i<=k. 1) = S(n.} return d.j++) y[j]=j. if(y[j]==1) break.c.j<=k.nrv(x[i])). m) (inclusiv suma cifrelor ¸i num˘rul a s s a cifrelor pentru fiecare num˘) ¸tiind c˘ a s a S(n + 1.x[i]). ¸ Rezolvare: Matricea de calcul este subdiagonal˘.48 CAPITOLUL 3.} while (i!=0){c=d/i. 2).i=a. m − 1) + mS(n. m) ¸i s S(n. for(j=1. if (a>b) {d=a. . Se completeaz˘ cu 1 prima a a coloan˘ ¸i diagonala principal˘. S(n.i++) { d=cmmdc(y[j]. for(i=1. for(i=1. ∀n ≥ m. int[]x=new int[k+1]. x[i]=x[i]/d. int[]y=new int[k+1]. i=r. } }// class 6. n) = 1. d=i. iar apoi se determin˘ celelalte elemente ale matricei as a a folosind relatia dat˘ (aranjat˘ putin altfel!). } static int cmmdc (int a.i++) rez=inm(rez. ALGORITMI static int[] comb (int n.r.i.int b) { int d. j. S(n.i<=k.i++) x[i]=n-k+i. int i... Matricea de calcul va avea de fapt trei ¸ a a ¸ .i=b.j<=k.i<=k..j++) { for(i=1. int k) { int[] rez.} else{d=b. r=d%i. d. m) = S(n. 1).

for(j=2.3..m). } } static static static static int[] suma(int[] x.. for(i=1. j.j<=min(i. } }// class Pe ecran apar urm˘toarele valori (numerele devin foarte mari!): a 1 : 1 1 1 2 : 15 64 562949953421311 .print("\n"+i+" : "+s[n][i].5. class e06 { public static void main(String[] args) { int n=50.out. } for(i=1.j++) s[i][j]=suma(s[i-1][j-1]. afissumac(s[n][i]).} int[] inm(int[]x.j trebuie s˘ contin˘ s a a ¸ a vectorul cifrelor valorii sale).} static void afissumac(int[]x) { int i.} int[] nr2v(int nr){.i--) s+=x[i]. if(i<=m) s[i][i]=nr2v(1). INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ 49 dimensiuni (numerele devin foarte mari.i++) { System. s[i][1]=nr2v(1).i<=n...i++) { if(i<=m) s[i][i]=nr2v(1). int[]y){. for(i=x. } static int min(int a. int[][][] s=new int[n+1][m+1][1]..s[i-1][j])).inm(nr2v(j).. int b) { return (a<b)?a:b.length+" ").length-1. m=40.} void afisv(int[]x){.out.. afisv(s[n][i]). a¸a c˘ elementul Si. System.int[] y){. i.i<=m..print(s+" ").i>=0.s=0.

.. S˘ se afi¸eze B1 .. ALGORITMI 3 : 24 102 119649664052358811373730 4 : 29 138 52818655359845224561907882505 5 : 33 150 740095864368253016271188139587625 6 : 37 170 1121872763094011987454778237712816687 7 : 39 172 355716059292752464797065038013137686280 8 : 41 163 35041731132610098771332691525663865902850 9 : 43 189 1385022509795956184601907089700730509680195 10 : 44 205 26154716515862881292012777396577993781727011 11 : 45 177 267235754090021618651175277046931371050194780 12 : 46 205 1619330944936279779154381745816428036441286410 13 : 46 232 6238901276275784811492861794826737563889288230 14 : 47 205 16132809270066494376125322988035691981158490930 15 : 47 162 29226457001965139089793853213126510270024300000 16 : 47 216 38400825365495544823847807988536071815780050940 17 : 47 198 37645241791600906804871080818625037726247519045 18 : 47 225 28189332813493454141899976735501798322277536165 19 : 47 165 16443993651925074352512402220900950019217097000 20 : 46 237 7597921606860986900454469394099277146998755300 21 : 46 198 2820255028563506149657952954637813048172723380 22 : 45 189 851221883077356634241622276646259170751626380 23 : 45 198 211092494149947371195608696099645107168146400 24 : 44 192 43397743800247894833556570977432285162431400 25 : 43 168 7453802153273200083379626234837625465912500 26 : 43 186 1076689601597672801650712654209772574328212 27 : 42 189 131546627365808405813814858256465369456080 28 : 41 155 13660054661277961013613328658015172843800 29 : 40 165 1210546686654900169010588840430963387720 30 : 38 185 91860943867630642501164254978867961752 31 : 37 155 5985123385551625085090007793831362560 32 : 36 164 335506079163614744581488648870187520 33 : 35 153 16204251384884158932677856617905110 34 : 33 144 674833416425711522482381379544960 35 : 32 126 24235536318546124501501767693750 36 : 30 135 750135688292101886770568010795 37 : 29 141 19983209983507514547524896035 38 : 27 132 457149347489175573737344245 39 : 25 114 8951779743496412314947000 40 : 24 93 149377949042637543000150 7.50 CAPITOLUL 3. Bn ¸tiind c˘ a s s a n Bn+1 = k=0 k Cn Bk . B2 . B0 = 1. ..

j++) for(i=1.. b[i]=suma(b[i].b[k])...i++) { b[i]=nr2v(0). ¸ Rezolvare: Vectorul de calcul va avea de fapt dou˘ dimensiuni (numerele devin a foarte mari.5.out.d. int[] x=new int[k+1]. System. for(i=1.} int[] inm(int[]x.i.. int[][] b=new int[n+1][1].3.prod). // n=25 ultimul care incape pe long int k.j<=k.println(".out..} void afisv(int[]x){. for(j=1. for(i=1. for(k=0.. b[0]=nr2v(1). int[]y){.} int[] nr2v(int nr){.i<=k.int k) { int i.int[] y){..print(i+" : ").. int[] rez.. s a a ¸ a class e07 { public static void main(String[] args) { int n=71. } System.i++) x[i]=n-k+i... a¸a c˘ elementul Bi trebuie s˘ contin˘ vectorul cifrelor valorii sale).MAX_VALUE). Gata .k<=i-1.out. } System. } static static static static int[] suma(int[] x.").i++) .k++) { prod=inm(comb(i-1. for(j=2. afisv(b[i])..i<=n. int[] y=new int[k+1]..j<=k.j.j++) y[j]=j.i<=k. int[] prod={1}.k). INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ 51 Se vor implementa operatiile cu numere mari.println(" "+Long.} static int[] comb(int n.

i<=k. a a ¸ 3.} } 3.52 { d=cmmdc(y[j]. iar s s a ın a a a dac˘ nu este num˘r prim s˘ se afi¸eze descompunerea ˆ factori. S˘ se afi¸eze primii 10 termeni a s ai ¸irului Sn ¸i s˘ se precizeze ˆ dreptul fiec˘rui termen dac˘ este num˘r prim. } static int cmmdc(int a.nr2v(x[i])). Suma ¸ a a a s trebuie calculat˘ simulˆnd operatiile cu numere mari. 1 − 1 pm . for(i=1.i++) rez=inm(rez. S˘ se calculeze a f (n) = n 1 − 1 p1 1− 1 p2 . ALGORITMI } rez=nr2v(1). y[j]=y[j]/d.x[i]).. a a ¸ 4. x2 ¸i x3 sunt r˘d˘cinile ecuatiei cu coeficienti 3 2 1 ˆ ıntregi ax3 +bx2 +cx+d = 0 (vom considera a = 1!)..int b) {. S˘ se afi¸eze frecventa cifrelor care apar ˆ a s ¸ ın n−1 f (n) = nn−1 + k=1 k Cn k k−1 (n − k)n−k netinˆnd cont de faptul c˘ f (n) are o expresie mult mai simpl˘. a a a s ın 2. ¸i anume nn .. x[i]=x[i]/d. S˘ se afi¸eze frecventa cifrelor care apar ˆ a s ¸ ın n−1 f (n) = k=0 k Cn−1 nn−1−k (k + 1)! netinˆnd cont de faptul c˘ f (n) are o expresie mult mai simpl˘. return rez. CAPITOLUL 3. if(y[j]==1) break. ¸i anume nn . Fie Sn = xn +xn +xn unde x1 . Suma ¸ a a a s trebuie calculat˘ simulˆnd operatiile cu numere mari.5..8 Probleme propuse s a a ¸ ¸ 1.

6. 2n 1 Cn n + 1 2n implementˆnd operatiile cu numere mari. S˘ se calculeze a m 1 1 (−1)n + − . Cm+n−1 λn implementˆnd operatiile cu numere mari. m 1 2 5. + (−1)n 2n n! . n... cmmdc(k. λn ) = Cm λ1 2 Cm+1 λ2 n .... 50) (inclusiv suma cifrelor ¸i num˘rul cifrelor) ¸tiind a s s a s c˘ a P (n + k.pim reprezint˘ descompunerea ˆ factori primi a lui n.. 1) + P (n. λn ) = 1 k (−1)m−k Cm Ck k=1 λ1 2 Ck+1 λ2 n . + P (n.... .. a ¸ Cn = 12. S˘ se calculeze a f (n) = 1 1 2 (2n)! − Cn 2(2n − 1)! + Cn 22 (2n − 2)! − . a s 7. netinˆnd cont de faptul c˘ f (n) are o ¸ ¸ ¸ a a expresie mult mai simpl˘.. Ck+n−1 λn . INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ a ın unde n = pi1 pi2 . a ¸ 10. n.. n) = 1} .. S˘ se calculeze a . . f (m.3... + 1! 2! n! . λ1 . ¸i anume n. λ2 . S˘ se afi¸eze P (100. S˘ se calculeze a 1 g(m. k) 11. k) = P (n. S˘ se calculeze a f (n) = n! 1 − 8.5.. S˘ se calculeze a f (n) = d|n 53 φ(n) unde φ este functia de la exercitiul anterior. 2) + . 9... S˘ se calculeze a φ(n) = card {k ∈ N/1 ≤ k ≤ n. λ1 . λ2 .

E1 = E2 = 1. astfel ˆ at pr = e. unde p a a ıncˆ este o permutare dat˘ ¸i e este permutarea identic˘. Se vor implementa operatiile cu numere mari. 1) = P (n. n) = 1. Se vor implementa operatiile cu numere mari.54 ¸i s CAPITOLUL 3. S˘ se determine cel mai mic num˘r natural r. unde Fk este termen Fibonacci.. + En−1 E2 . S˘ se afi¸eze E100 ¸tiind c˘ a s s a En = E2 En−1 + E3 En−2 + . S˘ se calculeze a S(n. S˘ se afi¸eze C100 ¸tiind c˘ a s s a n Cn = k=1 Ck−1 Cn−k . ∀n ≥ k ≥ 1. m) = 1 m! m−1 k (−1)k Cm (m − k)n k=0 17. S˘ se determine puterea a zecea a unui polinom dat. ¸ 18. ¸ 13. as a 14. ¸ 16. ALGORITMI P (n. a . Se vor implementa operatiile cu numere mari. ¸ 19. unde Fk este termen Fibonacci. Se vor implementa operatiile cu numere mari. S˘ se afi¸eze C100 ¸tiind c˘ a s s a n Cn = k=1 k Cn 2k Fk . Se vor implementa operatiile cu numere mari.. ¸ 15. S˘ se afi¸eze C100 ¸tiind c˘ a s s a n Cn = k=1 k Cn Fk . C0 = 1.

¸a Se pune problema de alegere a unei unit˘¸i de m˘sur˘ pentru a exprima at a a eficienta teoretic˘ a unui algoritm. ci numai num˘rul lor. ˆ ambele cazuri ˆ a. Principiul invariantei: dou˘ implement˘ri diferite ale aceluia¸i algoritm ¸ a a s nu difer˘ ˆ eficienta cu mai mult de o constant˘ multiplicativ˘. O important˘ deosebit˘ ˆ rezolvarea acestei ¸ a ¸a a ın probleme o are principiul invariantei. care la rˆndul lor sunt ¸ a compuse din mai multe operatii elementare.1 Scopul analizei complexit˘¸ii at ˆ general exist˘ mai multi algoritmi care rezolv˘ aceea¸i problem˘. Datorit˘ principiului invariantei nu ¸ a ¸ ne intereseaz˘ timpul de executie a unei operatii elementare. a ¸ ¸ a dar ne intereseaz˘ care ¸i ce sunt operatiile elementare. Dorim In a a s a s˘ exprim˘m eficienta algoritmilor sub forma unui criteriu care s˘ ne permit˘ s˘ a a ¸ a a a alegem din mai multi algoritmi pe cel optim. avem o In ıns˘ dependent˘ de dimensiunea cazului studiat. a s ¸ Definitia 1 O operatie elementar˘ este o operatie al c˘rui timp de executie poate ¸ ¸ a ¸ a ¸ fi m˘rginit superior de o constant˘ care depinde numai de particularitatea implea a ment˘rii (calculator. Acesta ne arat˘ c˘ nu este necesar s˘ folosim ¸ a a a o astfel de unitate. ¸ ın Un algoritm este compus din mai multe instructiuni. de limbajul de programare si ˆ ındemˆnarea programatorului (cu conditia ca a ¸ acesta s˘ nu modifice algoritmul). a ın ¸˘ a a Implementarea unui algoritm presupune elementele legate de calculatorul folosit.Capitolul 4 Analiza complexit˘¸ii at algoritmilor 4. limbaj de programare etc). Datorit˘ principiului invariantei vom exprima a a ¸ eficienta unui algoritm ˆ limitele unei constante multiplicative. Exist˘ mai multe moduri ˆ care ¸ a ın putem exprima eficienta: prin timpul necesar pentru executia algoritmului sau ¸ ¸ prin alte resurse necesare (de exemplu memoria). a 55 .

Dac˘ ¸irul este introdus ˆ ordine invers˘. ¸ a ın a ¸ Elaborarea unor algoritmi eficienti presupune cuno¸tinte din diverse domenii ¸ s ¸ (informatic˘. care ın a ¸ a este caracteristic˘ algoritmului. s ¸ ¸ Este foarte important ce anume definim ca operatie elementar˘. comparatiile ¸i atribuirile sunt operatii elementare. a ın ¸ Dac˘ dimensiunea problemei este mare. a ¸ a De multe ori. pentru c˘ depinde de lungimea celor doi ¸ a operanzi. a s Exist˘ mai multi algoritmi de rezolvare a unei probleme date. ˆ a ımbun˘t˘¸irea ordinului algoritmului a at este esential˘. Un algoritm mai bun poate fi obtinut a ¸ dac˘ ¸inem seama de rezultatul descoperit de Lucas ˆ 1876: at ın cmmdc(fm . atunci cˆnd este cazul). a ¸ se impune o analiz˘ a acestora. avem cazul cel mai defavorabil as ın a ¸i timpul va fi cel mai mare dintre timpii de sortare a ¸irului de n numere. fn = fn−1 + fn−2 .n) Deci putem rezolva problema calculˆnd un singur termen al ¸irului lui Fibonacci. este total neindicat˘. ˆ timp ce pentru timpi mici este sufcient˘ performanta hardware. fn ) = fcmmdc(m. ¸ a ın a a sc˘derile. operatiile booleene. nu ¸i timpul exact de executie al operatiilor respective. ANALIZA COMPLEXITATII ALGORITMILOR Deoarece ne intereseaz˘ timpul de executie ˆ limita unei constante multia ın plicative. a s a ¸ . ˆ a ınmultirile. operatiile modulo (restul ˆ aartirii ˆ ¸ ımp˘ ¸ ¸ ımp˘ ¸ ıntregi). Este adunarea ¸ a o operatie elementara? Teoretic nu este. matematic˘ ¸i cuno¸tiinte din domeniul c˘ruia ˆ apartine problema a as s ¸ a ıi ¸ practic˘ a c˘rui model este studiat. timpul necesar va cel mai mic dintre timpii necesari pentru sortarea oricarui alt ¸ir format din n numere. urmat˘ de calculul celui as a a mai mare divizor al lor. timpul de executie al unui algoritm poate varia pentru cazuri de m˘rime identic˘. Spunem c˘ avem de-a face cu cazul cel mai s a favorabil. De exemplu la sortare. Criteriile ˆ functie s a at ın ¸ de care vom stabili eficienta unui algoritm sunt complexitatea spatiu (memorie ¸ ¸ utilizat˘) ¸i complexitatea timp (num˘rul de operatiilor elementare). neglijˆndu-le pe celelalte. este un exemplu de recursivitate ˆ ¸ ın cascad˘ ¸i calcularea efectiv˘ a celor doi termeni fm fn . ¸ ¸ s ¸ Uneori eficienta difer˘ dac˘ ¸inem cont numai de unele operatii elementare ¸i ¸ a at ¸ s le ignor˘m pe celelalte (de exemplu la sortare: comparatia ¸i interschimbarea). ˆ artirile.56 ˘¸ CAPITOLUL 4. Vom considera ˆ continuare c˘ adun˘rile. Practic. Prin urmare. dac˘ introducem un ¸ir de n numere a a a s gata sortat. s Sirul lui Fibonacci. pentru operanzi de lungime rezonabil˘ putem s˘ consider˘m c˘ a a a a adunarea este o operatie elementar˘. s s Exist˘ algoritmi ˆ care timpul de executie nu depinde de cazul considerat. a a a Exemplul 1 Elaborati un algoritm care returneaz˘ cel mai mare divizor comun ¸ a (cmmdc) a doi termeni de rang oarecare din ¸irul lui Fibonacci. ca operatie barometru. De a ¸ s aceea ˆ analiza unor algoritmi vom considera o anumit˘ operatie elementar˘. ˆ scopul determin˘rii eficientei algoritmilor de a ın a ¸ rezolvare a problemei ¸i pe cˆt posibil a optimalit˘¸ii lor. vom considera doar num˘rul operatiilor elementare executate ˆ a ¸ ıntr-un algoritm.

fiecare celul˘ poate stoca cel mult o dat˘. Scopul analizei teoretice a algoritmilor este de fapt determinarea unor functii ¸ care s˘ limiteze superior. a s ¸ a c˘rui dimensiune depinde de datele de intrare. fiind astfel necesar˘ doar evaluarea num˘rului de s ¸ a a operatii elementare. Astfel. Timpul necesar executiei unei operatii elementare poate fi diferit de la o ¸ ¸ operatie la alta. dar este fixat.1. a a F˘r˘ a restrˆnge generalitatea. a a 4. prioritar devenind criteriul timp.˘¸ 4. a c˘ror dimensiune depinde de instanta a ¸ problemei de rezolvat ¸i din spatiul de memorie necesar apelurilor de proceduri ¸i s ¸ s functii. ¸ . timpul necesar executiei programului depinde numai de num˘rul de ¸ a operatii elementare efectuate de algoritm. care presupune: memoria const˘ ˆ a ıntr-un ¸ir infinit de s celule. pentru memorarea codului.1. Un program necesit˘ un spatiu de memorie constant. ¸ Primul pas ˆ analiza complexit˘¸ii timp a unui algoritm este determinarea ın at operatiilor elementare efectuate de algoritm ¸i a costurilor acestora.1. ˆ sensul c˘ o singur˘ instructiune este executat˘ la un moment ın a a ¸ a dat. nu ¸i a timpului total de executie a acestora. ¸ Pentru a analiza teoretic algoritmul. ¸ Progresele tehnologice fac ca importanta criteriului spatiu de memorie utilizat ¸ ¸ s˘ scad˘.2 Complexitatea timp Prin complexitate timp ˆ ¸elegem timpul necesar executiei programului. a ın Functiile depind de caracteristicile relevante ale datelor de intrare. a constantelor. instructiunile sunt executate secvential ¸i toate ¸ ¸ s instructiunile de baz˘ se execut˘ ˆ ¸ a a ıntr-o unitate de timp.1 Complexitatea spatiu ¸ Prin complexitate spatiu ˆ ¸elegem dimensiunea spatiului de memorie utilizat ¸ ınt ¸ de program. a variabilelor ¸i a structurilor s de date de dimensiune constant˘ alocate static ¸i un spatiu de memorie variabil. constˆnd din spatiul necesar a a ¸ pentru structurile de date alocate dinamic. a ¸ Pentru a analiza timpul de executie se folose¸te deseori modelul Random ¸ s Access Machine (RAM). fiecare celul˘ de memorie poate a a a fi accesat˘ ˆ a ıntr-o unitate de timp. ¸ s ¸ Analiza teoretic˘ ignor˘ factorii care depind de calculator sau de limbajul a a de programare ales ¸i se axeaz˘ doar pe determinarea ordinului de m˘rime a s a a num˘rului de operati elementare. deci putem spune c˘ operatiile elementare au timpul ¸ a ¸ m˘ginit superior de o constant˘. ¸ s Consider˘m operatie elementar˘ orice operatie al c˘rei timp de executie este a ¸ a ¸ a ¸ independent de datele de intrare ale problemei. independent de datele a ¸ de intrare. SCOPUL ANALIZEI COMPLEXITATII 57 4. respectiv inferior comportarea ˆ timp a algoritmului. vom presupune c˘ toate operatiile elementare aa a a ¸ au acela¸i timp de executie. ınt ¸ ˆ Inainte de a evalua timpul necesar executiei programului ar trebui s˘ avem ¸ a informatii detaliate despre sistemul de calcul folosit. vom presupune c˘ se lucreaz˘ pe un a a calculator ”clasic”.

a a ın Fie un algoritm dat ¸i o functie t : N → R+ . acest algoritm necesit˘ un timp ˆ ordinul lui ın a ın f pentru orice functie f : N → R+ pentru care t ∈ O(f ). g)) Pentru calculul multimilor O(f ) ¸i O(g) este util˘ proprietatea urm˘toare: ¸ s a a Proprietatea 4 Fie f. pentru valori suficient de mari ale argumentului. multimea de functii ¸ ¸ ¸ O(f ) = {t : N → R+ |∃c > 0. ANALIZA COMPLEXITATII ALGORITMILOR 4. g : N → R+ . astfel ˆ at o anumit˘ imples ¸ ıncˆ a mentare a algoritmului s˘ necesite cel mult t(n) unit˘¸i de timp pentru a rezolva a at un caz de marime n. ˆ particular t ∈ O(t). atunci n2 + 3n + 2 = 1 =⇒ O(n2 + 3n + 2) = O(n2 ) n→∞ n2 lim n2 + 3n + 2 = 0 =⇒ O(n2 + 3n + 2) ⊂ O(n3 ) n→∞ n3 lim . ∃n0 ∈ N a.1 Definire ¸i propriet˘¸i s at Definitia 2 Numim ordinul lui f .2. ın a Fie de exemplu.2.2 Notatia asimptotic˘ ¸ a 4. Principiul invariantei ne asigur˘ c˘ orice implementare a algoritmului necesit˘ ¸ a a a un timp ˆ ordinul lui t. Mai mult. t(n) = n2 + 3n + 2.58 ˘¸ CAPITOLUL 4. a a a a ¸ ıncˆ Pentru calculul ordinului unei functii sunt utile urm˘toarele propriet˘¸i: ¸ a at Proprietatea 1 O(f ) = O(g) ⇐⇒ f ∈ O(g) ¸i g ∈ O(f ) s Proprietatea 2 O(f ) ⊂ O(g) ⇐⇒ f ∈ O(g) ¸i g ∈ O(f ) s / Proprietatea 3 O(f + g) = O(max(f. ∀n > n0 } ı. Atunci n→∞ lim f (n) ∈ R+ ⇒ O(f ) = O(g) g(n) f (n) = 0 ⇒ O(f ) ⊂ O(g). (4.1) Rezult˘ c˘ O(f ) este multimea tuturor functiilor m˘rginite superior de un a a ¸ ¸ a multiplu real pozitiv al lui f .ˆ t(n) ≤ cf (n). g(n) n→∞ lim Reciproca nu este ˆ general valabil˘. In Vom c˘uta s˘ g˘sim cea mai simpl˘ functie astfel ˆ at t ∈ O(f ). Dac˘ t(n) ∈ O(f ) vom spune c˘ t este de ordinul lui f sau ˆ ordinul lui f .

. Dac˘ TA (n) = ak nk + ak−1 nk−1 + .. s a Dar nu este o relatie de ordine! Exist˘ ¸i functii astfel ˆ at f ≺ g (f ∈ O(g)) ¸ as ¸ ıncˆ / ¸i g ≺ f (g ∈ O(f )). De exemplu f (n) = n. a ¸ ¸ a a Fie f : N → R∗ o functie arbitrar˘. g : N → R∗ not˘m f ≺ g dac˘ O(f ) ⊆ O(g). atunci T A(n) ∈ O(nk ). g(n) = n1+sin(n) .... a pentru c˘ an2 + bn + c ≤ (a + 1)n2 . + a1 n + a0 | ≤ a |ak |nk + |ak−1 |nk−1 + . + a1 n + a0 . dac˘ O(f ) = O(g). ın a ın a Notatia O(f ) este pentru a delimita superior timpul necesar unui algoritm.∀n ≥ 4. astfel ˆ at a · n + b ≤ (a + 1) · n. atunci TA (n) ∈ O(n) pentru c˘ a a exist˘ c = a + 1 > 0 ¸i n0 = b ∈ N. dac˘ T A(n) = an2 + bn + c. dac˘ TA (n) = a · n + b. + |a1 | + |a0 |)nk . ∀n ≥ n0 . ∀n ≥ 2. s Aceast˘ ierarhie corespunde ierarhiei algoritmilor dup˘ criteriul performantei. ¸ Pentru f. atunci T A(n) ∈ O(n2 ). a Aceasta rezult˘ din: TA (n) = |TA (n)| = |ak nk + ak−1 nk−1 + . pentru c˘ TA (n) ≤ a a n 7 · 2 . a > 0. a s ıncˆ b) Dac˘ TA (n) = 10n2 + 4n + 2.. dorim sa realiz˘m un algoritm cu un ordin situat cˆt mai a a a a ˆ stˆnga ˆ aceast˘ ierarhie. atunci O(p) = O(nm ). s a s a . De ¸ ¸ a exemplu: ln(n) ≡ log(n) ≡ log2 (n). ıncˆ De exemplu: a) Dac˘ TA (n) = 3n+2. a c) Dac˘ TA (n) = 6 · 2n + n2 . s / Putem defini ¸i o relatie de echivalent˘: f ≡ g. ∀n ≥ 5. m ≥ 2.. a ın Notatia asimptotic˘ define¸te o relatie de ordine partial˘ ˆ ¸ a s ¸ ¸ a ıntre functii. + |a1 | + |a0 | ¸i n = 1 rezult˘ TA (n) ∈ O(nk ). ˆ s ¸ ¸a a In multimea O(f ) putem ˆ ¸ ınlocui orice functie cu o functie echivalent˘ cu ea.˘ 4. pentru c˘ 3n+2 ≤ 4n. implic˘ f ≺ h. c) + 1. adic˘: a ¸ at a ¸ a a) reflexivitate: f ≺ f b) antisimetrie: dac˘ f ≺ g ¸i g ≺ f atunci f = g a s c) tranzitivitate: f ≺ g ¸i g ≺ h. a a ¸ Pentru o problem˘ dat˘. Mai general. dac˘ ¸i numai dac˘ exist˘ c > 0 ¸i n0 ∈ N. atunci TA (n) ∈ O(n2 ). NOTATIA ASIMPTOTICA ¸ ln(n) √ = lim n→∞ n→∞ n lim 1 n 1 √ 59 2 n √ 2 = lim √ = 0 =⇒ O(ln(n)) ⊂ O( n) n→∞ n √ dar O( n) ⊂ O(ln(n) Dac˘ p este un polinom de gradul m ˆ variabila n. Spunem c˘ algoritmul este de ordinul + lui f (n) (¸i not˘m TA (n) ∈ O(f (n))). ∀n ≥ 1 ¸i alegˆnd c = |ak | + |ak−1 | + . atunci TA (n) ∈ O(n). a > 0. ∀n ≥ max(b. Notˆnd cu O(1) multimea functiilor m˘rginite superior de o constant˘ ¸i a ¸ ¸ a a s considerˆnd m ∈ N . atunci T A(n) ∈ O(2n ). + |a1 |n + |a0 | ≤ (|ak | + |ak−1 | + .. a a Aceast˘ relatie are propriet˘¸ile corespunz˘toare unei relatii de ordine. Not˘m TA (n) timpul necesar executiei algoritmului A.2. s a as a a s astfel ˆ at TA (n) ≤ c · f (n). a a Mai general. pentru c˘ 10n2 + a a 4n + 2 ≤ 11n2 .... ∀n ≥ b. ⊂ O(nm ) pentru m ≥ 4. obtinem ierarhia: a ¸ √ O(1) ⊂ O(log(n)) ⊂ O( n) ⊂ O(n) ⊂ O(n · log(n)) ⊂ O(nm ) ⊂ O(2n ) ⊂ O(n!) ¸i evident O(n2 ) ⊂ O(n3 ) ⊂ ..

ANALIZA COMPLEXITATII ALGORITMILOR 4.2. Definitie : Spunem c˘ TA (n) ∈ Θ(f (n)) dac˘ ¸i numai dac˘ ∃c1 . ¸ a Definitie: Spunem c˘ TA (n) ∈ Ω(f (n)) dac˘ ¸i numai dac˘ ∃c > 0 ¸i n0 ∈ N ¸ a as a s astfel ˆ at TA (n) ≥ c · f (n). De exemplu. a a ∀n ≥ 1. Un algoritm cu TA (n) ∈ O(nk ) s a a se nume¸te polinomial. pentru c˘ 10n2 +4n+2 ≥ a a n2. acela¸i program a s va rula 13 zile pe acest calculator.1: Functii de complexitate ¸ Dac˘ TA (n) ∈ O(2n ). ∀n ≥ n0 . Pentru n = 50. Dac˘ TA (n) ∈ O(n2 ) algoritmul se s a nume¸te p˘tratic. ¸ O(log(n)) (logaritmic) 0 1 2 3 4 5 O(n) (liniar) 1 2 4 8 16 32 O(n. ak > 0 atunci TA (n) ∈ Ω(nk ). ¸ a a a ¸ Un algoritm cu TA (n) ∈ O(1) necesit˘ un timp de executie constant. Un a ¸ algoritm cu TA (n) ∈ O(n) se nume¸te liniar.1013 ani pentru n = 1000. a De exemplu. ∀n ≥ 1. iar dac˘ TA (n) ∈ O(2n ) algoritmul se nume¸te exponential. ıncˆ .60 ˘¸ CAPITOLUL 4. pe un calculator care face 109 de operatii a ¸ pe secund˘. vor fi necesari peste 310 ani. Notatia matematic˘ este Ω. Utilitatea algoritmilor polinomiali de grad mare este de asemenea limitat˘. pentru O(n10 ).. aproximativ 3 ani pentru n = 100 ¸i circa s 3. cubic. dac˘ TA (n) = ak nk + a ¸ a ak−1 nk−1 + . Exist˘ functii f care constituie atˆt o limit˘ superioar˘ cˆt ¸i o limit˘ infea ¸ a a a a s a rioar˘ a timpului de executie a algoritmului. sunt necesare aproximativ 18 minute.2 Clase de complexitate Notatia O ofer˘ o limit˘ superioar˘ a timpului de executie a unui algoritm.log(n)) (log-liniar) 0 2 8 24 64 160 O(n2 ) (p˘tratic) a 1 4 16 64 256 1024 O(n3 ) cubic 1 8 64 512 4096 32768 O(2n ) (exponential) ¸ 2 4 16 256 65536 4294967296 Tabelul 4. atunci TA (n) ∈ Ω(2n ). s a s ¸ Tabelul urm˘tor ilustreaz˘ comportarea a cinci din cele mai importante a a functii de complexitate.. pentru c˘ 3n + 2 ≥ 3n. c) dac˘ TA (n) = 6 · 2n + n2 . c2 > 0 ¸i ¸ a as a s n0 ∈ N astfel ˆ at c1 · f (n) ≤ TA (n) ≤ c2 · f (n). pentru c˘ 6 · 2n + n2 ≥ 2n . a a b) dac˘ TA (n) = 10n2 +4n+2. atunci TA (n) ∈ Ω(n). iar dac˘ TA (n) ∈ O(n3 ). atunci TA (n) ∈ Ω(n). pentru n = 40. Uneori este util s˘ determin˘m ¸i o limit˘ inferioar˘ pentru timpul de executie a a s a a ¸ a unui algoritm. ıncˆ De exemplu: a) dac˘ TA (n) = 3n + 2. ∀n ≥ n0 . pe un calculator care execut˘ 109 operatii pe secund˘ a ¸ a sunt necesare 10 secunde pentru n = 10. iar pentru n = 100 aproximativ 4. ∀n ≥ 1. pentru n = 60.1013 ani. + a1 n + a0 .

De asemenea.2.. dar foarte proast˘ ˆ cazul a ın a a ın cel mai defavorabil.spatiul datelor de intrare ¸ • p(d) . Dac˘ not˘m: a a • D . d∈D . putem caracteriza exact datele de intrare. Cel mai cunoscut exemplu este algoritmul de sortare rapid˘ (quicksort) care a are complexitatea ˆ cazul cel mai defavorabil de O(n2 ). Se poate ar˘ta u¸or c˘ Θ(f (n)) = O(f (n)) ∩ Ω(f (n)).num˘rul de operatii elementare efectuate de algoritm pentru d ∈ D a ¸ atunci complexitatea medie este p(d) · TA (d). Determinarea complexit˘¸ii timp a algoritmului ca o functie de caracteristiat ¸ cile datelor de intrare este o sarcin˘ u¸oar˘ doar pentru algoritmi relativ simpli. num˘rul de operatii elementare efectuate de algoritm poate ¸ s a ¸ varia considerabil pentru diferite seturi de date de intrare. ın ın Complexitatea ˆ cazul cel mai defavorabil este num˘rul maxim de operatii ın a ¸ elementare efectuate de algoritm.. a s a dar ˆ general problema este dificil˘ ¸i din aceast˘ cauz˘ analiz˘m complexitatea ın as a a a algoritmilor ˆ medie sau ˆ cazul cel mai defavorabil. Dar chiar dac˘ este cunoscut cazul cel mai defavorabil.probabilitatea aparitiei datei d ∈ D la intrarea algoritmului ¸ • TA (d) . Pentru cazuri simple. de exemplu un algoritm de sortare care actioneaza asupra unui tablou cu n componente ˆ ¸ ıntregi aleatoare sau un algoritm geometric pe o multime de N puncte ˆ plan de coordonate aleatoare cuprinse ˆ ¸ ın ın intervalul [0. Totu¸i. Numero¸i algoritmi ın a ¸ s foarte utili au o comportare convenabil˘ ˆ practic˘.3 Cazul mediu ¸i cazul cel mai defavorabil s Am ar˘tat c˘ timpul de executie al unui algoritm este direct proportional cu a a ¸ ¸ num˘rul de operatii elementare ¸i am stabilit o notatie asimptotic˘ pentru timpul a ¸ s ¸ a de executie. datele utilizate efectiv a ˆ practic˘ pot conduce la timpi de executie mult mai mici.2. + a1 n + a0 . 1]. NOTATIA ASIMPTOTICA ¸ 61 ˆ acest caz f (n) constituie atˆt o limit˘ inferioar˘ cˆt ¸i o limit˘ superioar˘ In a a a a s a a pentru timpul de executie a algoritmului.˘ 4. Din acest motiv Θ se poate numi ordin ¸ exact. ak > 0 atunci TA (n) ∈ Θ(nk ). dar pentru datele ˆ alnite ın ıntˆ ˆ practic˘ functioneaz˘ ˆ O(n · log n). dac˘ a s a a TA (n) = ak nk + ak−1 nk−1 + . ın a ¸ a ın Determinarea complexit˘¸ii ˆ medie necesit˘ cunoa¸terea repartitiei probaat ın a s ¸ bilistice a datelor de intrare ¸i din acest motiv analiza complexit˘¸ii ˆ medie este s at ın mai dificil de realizat. 4.

.. atˆt ˆ medie cˆt ¸i ˆ cazul cel mai a ın a s ın defavorabil.. 4.. } . ¸ ¸ a Deci complexitatea algoritmului este O(n). Vom estima timpul de executie al algoritmului ˆ functie de n.62 ˘¸ CAPITOLUL 4.. ˆ cazul unei structuri repetitive pentru a determina ordinul de complexitate In ˆ cazul cel mai defavorabil se consider˘ num˘rul maxim de iteratii. for i=2.. gk (n)}).4 Analiza asimptotic˘ a structurilor fundamentale a Consider˘m problema determin˘rii ordinului de complexitate ˆ cazul cel mai a a ın defavorabil pentru structurile algoritmice: secvential˘. a ¸ a a a . for i = 2 to n do if a[i] > max then max = a[i].. Dac˘ acesta ın a a ¸ a este n iar ˆ corpul ciclului prelucr˘rile sunt de cost constant atunci se obtine ın a ¸ ordinul O(n). A2 .2 Sortarea prin selectia maximului ¸ Sort˘m cresc˘tor vectorul a. ANALIZA COMPLEXITATII ALGORITMILOR 4. a a for j=n... . pozmax=1. a2 . an }.. Ak ¸i fiecare dintre acestea are ordinul de complexitate O(gi (n)). 4.3. a max = a[1]. .. an .3.n-1.1 Calcularea maximului Fiind date n elemente a1 . 1 ≤ i ≤ n..2 { max=a[1].. s˘ se calculeze max{a1 ...3. care are n componente. Dac˘ conditia unei structuri alternative are cost constant iar prelucr˘rile a ¸ a celor dou˘ variante au ordinele de complexitate O(g1 (n)) respectiv O(g2 (n)) atunci a costul structurii alternative va fi O(max{g1 (n).3 Exemple 4. s Atunci structura va avea ordinul de complexitate O(max{g1 (n). num˘rul de ¸ ın ¸ a date de intrare.2. pozmax=i. Fiecare iteratie a ciclului for o vom considera operatie elementar˘.j { if a[i]>max { a[i]=max. g2 (n)}).. .. a2 ... alternativ˘ ¸i repetitiv˘.. ¸ a as a Presupunem c˘ structura secvential˘ este constituit˘ din prelucr˘rile A1 .

. a ın ¸ La fiecare iteratie a ciclului for exterior este calculat max{a1 . ¸ a 4. a2 . S˘ observ˘m c˘ timpul de executie este a a a ¸ independent de ordinea initial˘ a elementelor vectorului.3 Sortarea prin insertie ¸ Este o metod˘ de asemenea simpl˘. pe care o utiliz˘m adesea cˆnd ordon˘m a a a a a c˘rtile la jocuri de c˘rti. + (n − 1) = n(n − 1)/2. + (n − 1) = ¸ ın n(n − 1)/2 operatii elementare. cˆnd vectorul este initial ordonat descresc˘tor. ˆ cazul cel mai s a a ¸ a ın s In defavorabil.4. ..3. pentru a calcula max{a1 . La fiecare iteratie a ciclului for elementele a1 .. dimensiunea vectorului... } Analiz˘m algoritmul ˆ functie de n. .. a2 . } a[poz]=val. num˘rul mediu de operatii a ¸ elementare este: i k=1 1 1 · (k − 1) = · i i i k=1 (k − 1) = 1 i i(i + 1) −i 2 = i+1 i−1 −1= 2 2 .. Atunci probabilitatea ca valoarea ai s˘ fie plasat˘ pe pozitia k ˆ ¸irul ¸ a a ¸ ın s a1 . Pentru i fixat. deci ciclul while se execut˘ de i − 1 ori. 2. . fiecare element a[i] va fi a ¸ a plasat pe prima pozitie. EXEMPLE a[pozmax]=a[j].. aj } sunt necesare j − 1 operatii elementare.3.. Considerˆnd drept ¸ a a operatie elementar˘ comparatia a[poz −1] > val urmat˘ de deplasarea elementului ¸ a ¸ a de pe pozitia poz − 1... a[j]=max... Consider˘m c˘ elementele a a ın a a vectorului sunt distincte ¸i c˘ orice permutare a lor are aceea¸i probabilitate de s a s aparitie. while a[poz-1]>val { a[poz]=a[poz-1]. deci complexitatea algoritmului este de O(n2 )... } } 63 Estim˘m complexitatea algoritmului ˆ functie de n.n { val=a[i]. poz=i. . ¸ ¸ Conform exemplului anterior. a2 .. elementele de la j + 1 la n fiind deja plasate pe pozitiile lor definitive. a2 . ai . a¸ a¸ for i=2.. aj } ¸i plasat ¸ s pe pozitia j.. ai−1 sunt deja ordonate ¸ ¸i trebuie s˘ inser˘m valorea a[i] pe pozitia corect˘ ˆ ¸irul ordonat. Deci ¸ ın complexitatea algoritmului este de O(n2 ). dimensiunea vectorului ce urmeaz˘ a fi a ın ¸ a sortat. poz=poz-1... i} este 1/i. . ˆ total 1 + 2 + ....3.. vom avea ˆ cazul cel mai defavorabil 1 + 2 + . k ∈ {1. ¸ S˘ analiz˘m comportarea algoritmului ˆ medie.

quicksort(st. return i. dr). int dr) { int i.R. i=st. j=dr. ANALIZA COMPLEXITATII ALGORITMILOR Pentru a sorta cele n elemente sunt necesare n i=2 i−1 1 = 2 2 n(n + 1) − 1 − (n − 1) 2 = n 2 (n + 1) −1 2 = n(n − 1) 4 operatii elementare. val=a[st]. Deci complexitatea algoritmului ˆ medie este tot O(n2 ).3.4 Sortarea rapid˘ (quicksort) a Acest algoritm a fost elaborat de C. a int divide(int st. ¸ ın 4. val.64 ˘¸ CAPITOLUL 4.n). quicksort(m+1. while((i<j) && (a[i] <= val)) i=i+1. a[i]=a[j]. while(i<j) { while((i<j) && (a[j] >= val)) j=j-1. ¸ a a . dr). } Observatie : Vectorul a este considerat variabil˘ global˘. Hoare ˆ 1960 ¸i este unul dintre cei ın s mai utilizati algoritmi de sortare. ¸ void quicksort(int st. if st<dr { m=divide(st.A. m-1). a[j]=a[i]. int dr) { int m. ¸ a Functia divide are rolul de aplasa primul element (a[st]) pe pozitia sa corect˘ ¸ ¸ a ˆ ¸irul ordonat. } a[i]=val. j. iar ˆ dreapta ın s In a a ın numai elemente mai mari decˆt el. } } Initial apel˘m quicksort(1. ˆ stˆnga sa se vor g˘si numai elemente mai mici.

a a La fiecare apel al procedurii quicksort este apelat˘ functia divide(1. (2. n)) care efectueaz˘ i − 1. sort˘m elementele din a ¸ a stˆnga. + T1 + T0 . ˆ total num˘rul de operatii elementare este (n − 1) + (n − 2) + .. ¸ Problema se reduce la a rezolva relatia de recurent˘ de mai sus. 0...4. 1 n n k=1 Tn = (Tk−1 + Tn−k ) + (n − 1). Tn = n − 1 + 2 n n Tk−1 k=1 ˆ Inmultim ambii membri ai acestei relatii cu n. .. Vom consider˘m c˘ orice a a ın a a permutare a elementelor vectorului are aceea¸i probabilitate de aparitie ¸i not˘m s ¸ s a cu Tn num˘rul de operatii elementare efectuate pentru a sorta n elemente.. 2) (dac˘ vectorul a era initial ordonat descresc˘tor) sau (1. Obtinem: ¸ ¸ ¸ n nTn = n(n − 1) + 2 Tk−1 k=1 Sc˘zˆnd din aceast˘ relatie. Mai ˆ ai ¸ ¸a ıntˆ observ˘m c˘ a a T0 + T1 + ... (1. ceea ce necesit˘ Tk−1 operatii elementare. a ¸ Probabilitatea ca un element al vectorului s˘ fie plasat pe pozitia k ˆ vectorul a ¸ ın ordonat. n). ın S˘ analiz˘m comportarea algoritmului ˆ medie. este de 1/n. n). (n − 1. Deci. cu parametrii (1. n−1). (respectiv n − i − 1) operatii a ¸ elementare. se fac n − 1 In a ¸ apeluri succesive ale procedurii quicksort.. EXEMPLE 65 ˆ cazul cel mai defavorabil. ceea ce necesit˘ n−1 operatii elementare. n). adic˘ a a a ¸ ¸ ¸ a a n−1 (n − 1)Tn−1 = (n − 1)(n − 2) + 2 obtinem ¸ Tk−1 k=1 nTn − (n − 1)Tn−1 = n(n − 1) − (n − 1)(n − 2) + 2Tn−1 de unde rezult˘ a nTn = 2(n − 1) + (n + 1)Tn−1 . cˆnd vectorul a era initial ordonat. determin˘m pozitia k ˆ vectorul ordonat a a a ¸ ın primului element. dac˘ n = 0 sau n = 1 a dac˘ n > 1 a (pentru a ordona cresc˘tor n elemente. apoi cele din dreapta. (1. + Tn−1 = T n − 1 + . necesitˆnd a a ¸ a Tn−k operatii elementare).3. n) a ¸ a (dac˘ vectorul a era ordonat cresc˘tor). + 1 = In a ¸ n(n − 1)/2.. . Complexitatea algoritmului ˆ cazul cel mai defavorabil este de O(n2 ).... relatia obtinut˘ pentru n − 1.i) a ¸ (respectiv divide(i.

} if (in=n-1) and (out = 0) celebritate=p.. Cu alte cuvinte. ˆ medie. for j=1. 0....’)..3.. ’ este o celebritate. a altfel. dac˘ persoana i cunoaste persoana j. dar nu a a a cunoa¸te pe nimeni. } if celebritate=0 writeln(’Nu exista celebritati !’) else writeln(p. ¸ Putem reformula problema ˆ limbaj de grafuri astfel: fiind dat un digraf cu ın n vˆrfuri. out=0.. = +2 n+1 n n(n + 1) n − 1 n(n + 1) (n − 1)n 3 Deci Tn T2 = +2 n+1 3 n n k=3 k−1 k(k + 1) k=3 1 1 1 − + k+1 k k+1 = T2 2 + +2 3 n+1 n k=3 1 ≈2 k n k=1 1 ≈ 2 ln n k Deci.. for p=1.2. out=out+a[p][j].n { in=0.j = 1. Se pune problema de a identifica o celebritate. . aceasta s a a a s va fi celebritatea c˘utat˘. dac˘ exist˘..66 ˘¸ CAPITOLUL 4. pentru fiecare vˆrf din digraf calcul˘m gradul interior a a ¸i gradul exterior. a a celebritate=0.5 Problema celebrit˘¸ii at Numim celebritate o persoan˘ care este cunoscut˘ de toat˘ lumea. Dac˘ g˘sim o persoan˘ pentru care out = 0 ¸i in = n−1. O prim˘ solutie ar fi s˘ calcul˘m pentru fiecare persoan˘ p din grup num˘rul a ¸ a a a a de persoane pe care p le cunoa¸te (out) ¸i num˘rul de persoane care cunosc pers s a soana p (in). s a a ˆ ıntr-un grup de n persoane pentru care relatiile dintre persoane sunt cunoscute.n { in=in+a[j][p]. ın 4.. ANALIZA COMPLEXITATII ALGORITMILOR ˆ artind ambii membri cu n(n + 1) obtinem Imp˘ ¸ ¸ Tn Tn−1 2(n − 1) Tn−2 2(n − 1) 2(n − 2) T2 = + = + + = . complexitatea algoritmului este de O(n log n).2. a ¸ a a a s Reprezent˘m graful asociat problemei prin matricea de adiacent˘ an×n a ¸a ai. verificati dac˘ exist˘ un vˆrf cu gradul exterior 0 ¸i gradul interior n − 1.

ˆ acest caz algoritmul a devenit liniar. sau s ın s a a a[x. for i=2. ’ este o celebritate . R˘mˆne s˘ calcul˘m num˘rul de persoane cunoscute ¸i num˘rul de a a a a a s a persoane care ˆ cunosc pe acest candidat.4. ˆ final vom avea o singur˘ persoan˘ candidat a a ın a a la celebritate.4 Probleme 4.n if a[candidat][i]=1 candidat=i. f ∈ O(n) =⇒ f 2 ∈ O(n2 ) f ) ∀f : N → R∗ . In 4.4. ıl a candidat=1.’) else write(’Nu exista celebritati. ¸ a a n3 b) Afirmatia este fals˘ pentru c˘: limn→∞ n2 = ∞ ¸ a a . for i=1.4. f ∈ O(n) =⇒ 2f ∈ O(2n ) Rezolvare: n2 a) Afirmatia este adevarat˘ pentru c˘: limn→∞ n3 = 0 =⇒ n2 ∈ O(n3 ).n { in=in+a[i][candidat]. y] = 0 ¸i ˆ acest caz y nu are nici o ¸ans˘ s˘ fie celebritate. s ın Deci la un test elimin˘m o persoan˘ care nu are ¸anse s˘ fie celebritate. out=0. out=out+a[candidat][i]. PROBLEME 67 Se poate observa cu u¸urint˘ c˘ algoritmul este de O(n2 ). Putem ˆ s ¸a a ımbun˘t˘¸i a at algoritmul f˘cˆnd observatia c˘ atunci cˆnd test˘m relatiile dintre persoanele x ¸i a a ¸ a a a ¸ s y apar urm˘toarele posibilit˘¸ii: a at a[x.’). singura celebritate posibil˘. in=0. a a s a F˘cˆnd succesiv n − 1 teste. } if (out=0) and (in=n-1) write(candidat. y] = 1 ¸i ˆ acest caz x nu poate fi celebritate.1 Probleme rezolvate Problema 1 Care afirmatii sunt adevarate: ¸ a) n2 ∈ O(n3 ) b) n3 ∈ O(n2 ) c) 2n+1 ∈ O(2n ) d) (n + 1)! ∈ O(n!) e) ∀f : N → R∗ .

∀n > n0 . ıncˆ a a ıncˆ ∀n > n0 . a a Relatia se poate demonstra ¸i folosind aproximarea lui Stirling ¸ s n! ≈ √ 2πn n e n (1 + Θ(1/n)) . rezult˘ log n! < n log n. a Trebuie s˘ g˘sim ¸i o margine inferioar˘.5772 este constanta lui Euler. deci log n! ∈ Ω(n log n).. ¸i aplic˘m ¸ ¸ s a √ relula lui L’Hˆspital pentru log n/ n.(2 · (n − 1))(1 · n) ≥ nn rezult˘ 2 log n! ≥ n log n.68 ˘¸ CAPITOLUL 4. Pentru 0 ≤ i ≤ n − 1 este adev˘rat˘ a a s a a a relatia ¸ (n − i)(i + 1) ≥ n Deoarece (n!)2 = (n · 1)((n − 1) · 2)((n − 2) · 3). La punctul iv) din n! < nn . pe care sunt derivabile.5n log n. d) Afirmatia este fals˘ pentru c˘: limn→∞ (n+1)! = limn→∞ n+1 = ∞ ¸ a a n! 1 e) Afirmatia este adevarat˘ pentru c˘: f ∈ O(n) =⇒ ∃c > 0 ¸i ∃n0 ∈ N ¸ a a s astfel ˆ at f (n) < c · n. deci f 2 ∈ O(n2 ). adic˘ log n! ≥ 0. pentru oricare k ∈ N 1 ∈ Θ(n log n) i iii) i=1 iv) log n! ∈ Θ(n log n) ∞ i=1 Indicatii: La punctul iii) se ¸ine cont de relatia ¸ t ¸ 1 ≈ γ + ln n i unde γ ≈ 0. a ¸ a Indicatie: Prelungim domeniile functiilor pe R+ . ∀n > n0 . deci 2f ∈ O(2n ). ∀n > n0 . b > 1 n ik ∈ Θ(nk+1 ). deci log n! ∈ O(n log n). o Problema 3 Demonstrati urm˘toarele afirmatii: ¸ a ¸ i) ii) i=1 n loga ∈ Θ(logb n). ANALIZA COMPLEXITATII ALGORITMILOR n+1 c) Afirmatia este adevarat˘ pentru c˘: limn→∞ 22n = 2 =⇒ O(2n+1 ) = ¸ a a n O(2 ). √ √ / Problema 2 Ar˘tati c˘ log n ∈ O( n) dar n ∈ O(log n). pentru oricare a. Rezult˘ c˘ ∃c1 = 2c astfel ˆ a 2f (n) < 2c·n = 2c · 2n = ıncˆ a a ıncˆ c1 · 2n . Rezult˘ c˘ ∃c1 = c2 astfel ˆ a f 2 (n) < c1 · n2 . e) Afirmatia este adevarat˘ pentru c˘: f ∈ O(n) =⇒ ∃c > 0 ¸i ∃n0 ∈ N astfel ¸ a a s ˆ at f (n) < c · n..

PROBLEME 69 4. Fie f.4. k ≥ 1 e) loga n ∈ Θ(logb n). Analizati complexitatea algoritmului de interclasare a doi vectori ordonati. Analizati algoritmul ˆ medie ¸i ˆ cazul cel mai defavorabil. s 3.n-1 if a[i]>a[i+1] { aux=a[i].4. g : N → R+ Demonstrati c˘: ¸ a i) lim n→∞ (4. b = 1. i=i+1. while (i <= n) and (j <= m) { k=k+1. g(n) ii) lim n→∞ f (n) = 0 ⇒ O(f ) ⊂ O(g) g(n) Observatie: Implicatiile inverse nu sunt ˆ general adev˘rate. } else { c[k]=b[j]. ¸ ¸ a cu n componente. } ok=false. Care este timpul de executie a algoritmului quicksort pentru un vector ¸ cu n componente egale? 6. a[i]=a[i+1]. 2. a = 1. g)) unde suma ¸i maximul se iau punctual. Demonstrati prin inductie c˘ pentru a determina maximul a n numere sunt ¸ ¸ a necesare n − 1 comparatii.4. a 4. j=1. for i=1. k=0. j=j+1. g : N → R∗ demonstrati c˘ ¸ a O(f + g) = O(max(f. if a[i] < b[j] { c[k]=a[i]. Pentru oricare doua functii f.1) f (n) ∈ R+ ⇒ O(f ) = O(g). b > 0.2 Probleme propuse 1. a[i+1]= aux. } while !ok. deoarece se ¸ ¸ ın a poate ˆ ıntampla ca limitele s˘ nu existe. respectiv b cu m componente : i=1. ¸ ın s ın 7. ¸ 5. S˘ consider˘m urm˘torul algoritm de sortare a unui vector a cu n compoa a a nente: do { ok=true.4. Ar˘tati c˘: a ¸ a a) n3 + 106 n∈ Θ(n3 ) n n b) n2 + 6 · 2n ∈ Θ()(n2 ) c) 2n2 + n log n ∈ Θ(n2 ) d) nk + n + nk log n ∈ Θ(nk log n). } . a.

} 8.m { k=k+1. x2 . + an xm ia valoare maxim˘. S˘ se determine {x1 . vom numi multiplicitate a lui x ˆ X num˘rul de aparitii ale lui x ˆ X... . un vector cu n componente distincte. Fiind dat x. nenule (m < n)... x2 . dac˘ un astfel a s a de element exist˘. Fie {a1 . an } ¸i {b1 .. Se d˘ a un vector cu n componente. b2 ..n { k=k+1. xm ) = a1 x1 + a2 x2 + . bm }. xm }. . a ¸a 10. ANALIZA COMPLEXITATII ALGORITMILOR } for t=i... verificati dac˘ o valoare ¸ a dat˘ x se g˘se¸te sau nu ˆ vector. c[k]=a[t]. a 12. Descrieti un s a a ¸ algoritm liniar care s˘ determine elementul majoritar dintr-un ¸ir... . c[k]=b[t]..70 ˘¸ CAPITOLUL 4. s ın 9. a2 ... . Scrieti un algoritm liniar care s˘ a ¸ a determine cea mai lung˘ secvent˘ de elemente consecutive de valori egale. .. ¸ a prin doi algoritmi de complexitate diferit˘. Fiind dat a. Un element se ın a ¸ ın nume¸te majoritar dac˘ multiplicitatea sa este mai mare decˆt n/2. dou˘ multimi de numere ˆ s a ¸ ıntregi.. xn ) o secvent˘ de numere ˆ ¸a ıntregi. an } a ¸ ¸ pentru care functia f (x1 . } for t=j... a2 . Fie X = (x1 .. ... Verificati ˆ timp liniar dac˘ un text dat T ′ este o permutare ¸ ın a circular˘ a lui T . a 11. Fie T un text. a . o submultime a multimii {a1 . x2 . Evaluati complexitatea algoritmului ˆ cazul a a s ın ¸ ın cel mai defavorabil ¸i ˆ medie.

C. In ın Pascal. fib(n-1) + fib(n-2). a ˆ Java este posibil. a a s s 5. De asemenea. ¸ a ın ¸ dac˘ argumentul n este mai mic decˆt 1 returneaz˘ valoarea 1 iar ˆ caz contrar a a a ın returneaz˘ f ib(n − 1) + f ib(n − 2). limite de ¸iruri. Dealtfel. s˘ definim astfel de functii recursive. toate ¸irurile definite a ¸ s prin recurent˘ se scriu ˆ aceast˘ manier˘ ˆ Java. fib este o functie care utilizeaz˘ propriul nume ˆ definitia proprie. a a s 71 . a transcriere literal˘ a fors a mulei este urm˘toarea: a static int if (n <= return else return } fib(int n) { 1) 1. etc. etc).1 Functii recursive ¸ 5. ca de altfel ˆ multe alte limbaje de programare (Fortran.Capitolul 5 Recursivitate Definitiile prin recurent˘ sunt destul de curente ˆ matematic˘: progresia ¸ ¸a ın a aritmetic˘. cum se poate observa din ¸a ın a a ın urm˘toarele dou˘ exemple numerice: factorialul ¸i triunghiul lui Pascal.1 Functii numerice ¸ Pentru calculul termenilor ¸irului lui Fibonacci. progresia geometric˘.1. ¸irul lui Fibonacci.

p) + comb(n-1.0) comb(1. int p) { if ((p == 0) || (p == n)) return 1.0) Ne putem ˆ ıntreba cum efectueaz˘ Java calculul functiilor recursive.2) comb(3. n * fact (n1). p-1). } comb(4.0) comb(1.2) comb(3. CAPITOLUL 5. RECURSIVITATE fibo(4) fact(4) fibo(3) fibo(2) fact(3) fibo(2) fibo(1) fibo(1) fibo(0) fact(2) fibo(1) fibo(0) fact(1) static int comb(int n. iar un apel de functie a ın ¸ const˘ ˆ evaluarea argumentului. else return comb(n-1.1) comb(1. apoi lansarea ˆ executie a functiei cu valoarea a ın ın ¸ ¸ .1) comb(2.1) comb(2.1) comb(1.2) comb(2.72 static int if (n != return else return } fact(int n) { 1) 1. Reamintim a a a ın c˘ argumentele sunt transmise prin valoare ˆ acest caz. Putem a ¸ s˘ r˘spundem prin urm˘rirea calculelor ˆ cazul calculului lui f ibo(4).1) comb(2.

u = u0 + v0. } . Rezult˘ Rn = 2 · f ibo(n) ¸i de aici obtinem c˘ Rn = 2 · f ibo(n) − 1. f ibo(n) f ibo(n − 1) = 1 1 1 0 u v × = f ibo(n − 1) f ibo(n − 2) 1 1 1 0 × = . int i. a ¸ a ′ ′ ′ ′ a s ¸ a ¸i R1 = R0 = 2. obtinem c˘ Rn = Rn−1 + Rn−2 pentru n > 1.. ¸i Rn = 1 + Rn−1 + Rn−2 s ′ ′ ′ pentru n > 1. v = v0. s Num˘rul de apeluri recursive este foarte mare! Exist˘ o metod˘ iterativ˘ simpl˘ a a a a a care permite calculul lui f ibo(n) mult mai repede. u = 1. ++i) { u0 = u. Punˆnd Rn = Rn + 1. i <= n. int u0. v0. for (i = 2. v0 = v. v = 1. v.5. Evident R0 = R1 = 1.. } return u. S˘ not˘m prin Rn num˘rul apelurilor functiei a a a ¸ f ibo pentru calculul lui f ibo(n). Deci f ibo(4) → → f ibo(3) + f ibo(2) (f ibo(2) + f ibo(1)) + f ibo(2) ((f ibo(1) + f ibo(1)) + f ibo(1)) + f ibo(2) ((1 + f ibo(1)) + f ibo(1)) + f ibo(2) ((1 + 1) + f ibo(1)) + f ibo(2) (2 + f ibo(1)) + f ibo(2) (2 + 1) + f ibo(2) 3 + f ibo(2) 3 + (f ibo(1) + f ibo(1)) 3 + (1 + f ibo(1)) 3 + (1 + 1) 3+2 5 73 → → → → → → → → → → → Exist˘ deci un num˘r semnificativ de apeluri succesive ale functiei f ib (9 a a ¸ apeluri pentru calculul lui f ibo(4)).1. FUNCTII RECURSIVE ¸ argumentului. = u0 v0 1 1 1 0 n × 1 0 static int fibo(int n) { int u.

. Prototipul este functia lui Ackerman. RECURSIVITATE Se poate calcula ¸i mai repede folosind ultima form˘ ¸i calculˆnd puterea s a s a matricei . ack(2. ack(m. Pentru a rezuma. Nu numai c˘ scrierea recursiv˘ se poate dovedi eficace. dar ea este ın a a totdeauna natural˘ ¸i deci cea mai estetic˘. Un alt exemplu este ”functia 91” a lui MacCarty [11]: a s a ¸ static int f(int n) { if (n > 100) return n-10. este de asemenea simplu s˘ d˘m definitia recursiv˘ ˆ a ¸ a a ¸ a ın Java. else return ack(m-1. } Se poate verifica c˘ ack(0. else if (n == 0) return ack (m-1. ˆ ınt ¸ In general este sufucient s˘ ˆ ¸elegem sintetic functia. 5. 1). ack(1. static int ack(int m. a ¸a a a 5. n) ≈ 2n . a ack(3. n) ≈ 2n. ack(5. Este o metod˘ de programare foarte puternic˘.1. Functia lui Fibonacci este un a ınt ¸ ¸ caz particular ˆ care calculul recursiv este foarte lung.74 CAPITOLUL 5.. ˆ loc s˘ definim s a ¸ In a matematic aceast˘ functie. Exist˘ functii recursive care au ¸ s ¸ a a ¸ o cre¸tere mult mai rapid˘.1.3 Recursii imbricate Functia lui Ackerman contine dou˘ apeluri recursive imbricate ceea ce deter¸ ¸ a min˘ o cre¸tere rapid˘. } . adic˘ num˘rul atomilor din a a univers [11].2 Functia lui Ackerman ¸ Sirul lui Fibonacci are o cre¸tere exponential˘. else return f(f(n+11)). int n) { if (m == 0) return n+1. 4) ≈ 265536 > 1080 . n-1)). n) = n + 1. o regul˘ bun˘ este s˘ nu ˆ a a a ıncerc˘m s˘ intr˘m ˆ meandrele a a a ın detaliilor apelurilor recursive pentru a ˆ ¸elege sensul unei functii recursive. n) = n + 2. 1) ≈ ack(4. Dar nu aceasta este situatia a a s ¸ ˆ general. Cam la fel se ˆ ampl˘ ın ıntˆ a (dac˘ nu chiar mai r˘u!) ¸i cu triunghiul lui Pascal. Ea nu face decˆt s˘ respecte definitia as a a a ¸ matematic˘ prin recurent˘.

¸ a numerotate 1.. 0) = g(0. } } . 0)). se mut˘ primele n−1 discuri (cele mai mici) de pe tija i pe tija k = 6−i−j. Aceast˘ functie anecdotic˘. Exemplul clasic este cel al turnurilor din Hanoi. i. = f (100) = f (f (111)) = f (101) = 91. g(m-1. hanoi (n-1. 6-(i+j)). 75 Se poate ar˘ta c˘ aceast˘ functie va returna 91 dac˘ n ≤ 100 ¸i n − 10 dac˘ a a a ¸ a s a n > 100. j ≤ 3). ¸ s a as calculul nu se va termina niciodat˘! a 5. mutˆnd numai cˆte un singur disc ¸i a s a a s neplasˆnd niciodat˘ un disc mai mare peste unul mai mic. Un rationament recura a ¸ siv permite scrierea solutiei ˆ cˆteva rˆnduri. a static void hanoi(int n. a n a ¸ a a s Un alt exemplu este functia lui Morris [11] care are urm˘toarea form˘: ¸ a a static int if (m == return else return } g(int m. 2 ¸i 3 de la stˆnga la dreapta. System. g(1. a 2. calculul lui f (96) d˘ a ¸ a f (96) = f (f (107)) = f (97) = . int n) { 0) 1.2 Proceduri recursive Procedurile..5. 6-(i+j). PROCEDURI RECURSIVE Pentru aceast˘ functie. problema este trivial˘. se mut˘ cele n − 1 discuri de pe tija k pe tija j. 0). n)). int i. se mut˘ cel mai mare disc de pe tija i pe tija j. g(m. Dac˘ n ≤ 1. Se dore¸te mutarea discurilor pe tija 3. exist˘ o solutie foarte simpl˘ pentru mutarea celor n a ¸ a discuri de pe tija i pe tija j: 1. Atunci. a 3.println (i + " -> " + j). ¸ ın a a a a Presupunem problema rezolvat˘ pentru mutarea a n − 1 discuri de pe tija i pe a tija j (1 ≤ i.2. Se declan¸eaz˘ la nesfˆr¸it apelul g(1. j). este a ¸ a s a interesant˘ pentru c˘u este evident c˘ o astfel de definitie d˘ d˘ acela¸i rezultat. Ce valoare are g(1. care folose¸te recursivitatea imbricat˘. Deci. pot fi recursive ¸i pot suporta apeluri recurs ¸ s sive.out. int j) { if (n > 0) { hanoi (n-1. 0)? Efectul acestui apel de functie se poate observa din ¸ definitia ei: g(1. la fel ca ¸i functiile. sunt n discuri de dimensiuni diferite s a plasate pe tija 1 formˆnd un con cu discul cel mai mare la baz˘ ¸i cel mai mic ˆ a as ın vˆrf. Pe 3 tije din fata noastr˘.

un program recursiv de cˆteva a a a a linii poate rezolva o problem˘ apriori complicat˘. s ¸ ¸a pasul 1 A B a) C A B b) pasul 2 C pasul 3 A B d) C A B c) C . a a a adic˘ mutarea de pe oricare tij˘ i pe oricare tij˘j.76 CAPITOLUL 5. Aceasta este forta recursivit˘¸ii a a ¸ at ¸i a rationamentului prin recurent˘. RECURSIVITATE Aceste cˆteva linii de program arat˘ foarte bine cum generalizˆnd problema.

as Analiza unui algoritm recursiv implic˘ rezolvarea unui sistem de recurente. x1 . a0 .xn+k ai unui ¸ir ¸ ın s de numere se nume¸te relatie de recurenta de ordinul k. ai ∈ R.1.. deoarece a a a solicit˘ ¸i ele resursele calculatorului (timp si memorie). xn+1 .1) atunci ea se nume¸te relatie de recurenta de ordinul k cu coeficienti constanti. pentru n ≥ 0. + ak xn+k = 0. xk−1 .1 Relatii de recurent˘ ¸ ¸a O ecuatie ˆ care necunoscutele sunt termenii xn . adic˘ de valorile termenilor ¸a s ¸ ¸ a x0 .. Ca s˘ putem rezolva ecuatia (relatia de a a s a ¸ ¸ recurent˘) mai avem nevoie ¸i de conditii initiale..Capitolul 6 Analiza algoritmilor recursivi Am v˘zut ˆ capitolul precedent cˆt de puternic˘ ¸i util˘ este recursivitatea a ın a as a ˆ elaborarea unui algoritm. a as a Pe de alt˘ parte. s ¸ ¸˘ ¸ ¸ Coeficientii sunt constanti ˆ sensul c˘ nu depind de valorile ¸irului xn . C0 = 1 este de ordinul 1... k ≥ 1. Cel mai important cˆ¸tig al exprim˘rii recursive este ın as a faptul c˘ ea este natural˘ ¸i compact˘. apelurile recursive trebuie folosite cu discern˘mˆnt... ¸ ¸ ın a s 77 . Dac˘ un ¸ir xn de numere satisface o formul˘ de forma a s a a0 xn + a1 xn+1 + . ın ¸ 6. ak = 0 (6. De exemplu relatia de recurent˘ ¸ ¸a (n + 2)Cn+1 = (4n + 2)Cn . . Aceast˘ ecuatie poate s ¸ ¸˘ a ¸ fi satisf˘cut˘ de o infinitate de ¸iruri. . a ¸ Vom vedea ˆ continuare cum pot fi rezolvate astfel de recurente.

2 Solutia general˘ ¸ a Solutia general˘ a relatiei de recurent˘ omogen˘ de ordinul k cu coeficienti ¸ a ¸ ¸a a ¸ constanti este de forma ¸ k xn = i=1 (i ci x(i) n (6.1 Ecuatia caracteristic˘ ¸ a G˘sirea expresiei lui xn care s˘ satisfac˘ relatia de recurent˘ se nume¸te a a a ¸ ¸a s rezolvarea relatiei de recurenta. r2 ..... 2.2) 6. ... ..1. . Ea este o a ¸ ¸a s s relatie de recurent˘ de ordinul 2 cu coeficienti constanti. a adic˘ relatia de recurent˘ care define¸te ¸irul numerelor lui Fibonacci. k) sunt distincte. F0 = 0.78 CAPITOLUL 6....1.. F˘cˆnd substitutia ¸ ¸˘ a a ¸ xn = rn obtinem urm˘toarea ecuatie. ¸ ¸a ¸ ¸ 6. atunci ri sunt a a a ¸ solutii ale relatiei de recurent˘... c2 ..1. np−1 rn sunt solutii liniar independente ale relatiei de recurent˘ ¸i ¸ ¸ ¸a s xn = c1 + c2 n + . ¸ ¸ ¸ • Ecuatia caracteristic˘ admite r˘d˘cini reale multiple ¸ a a a Fie r o r˘d˘cin˘ multipl˘ de ordinul p a ecuatiei caracteristice. atunci relatia de recurent˘ a a a ¸ ¸a are solutia general˘ ¸ a n n n xn = c1 r1 + c2 r2 + . n2 rn . nrn ..1. .1.4) unde coeficientii c1 . introducˆnd expresiile ri ˆ relatia de recurent˘. + ck rk (6.. + ak ri + a2 ri a0 ri + a1 ri unde xn )|i ∈ {1. Atunci a a a a ¸ rn . + cp−1 np−1 rn (6.5) ..3) (se mai numesc ¸i sistem fundamental de solutii). ... F1 = 1. Pentru determinarea acestor s ¸ solutii distingem urm˘toarele cazuri: ¸ a • Ecuatia caracteristic˘ admite r˘d˘cini reale ¸i distincte ¸ a a a s n Dac˘ r1 . ANALIZA ALGORITMILOR RECURSIVI O astfel de formul˘ este de exemplu Fn+2 = Fn+1 + Fn ...1. 2.. rk sunt r˘d˘cini reale ale ecuatiei caracteristice. ¸ ¸ ¸a n ˆ ın ¸ ¸a ¸ Intr-adev˘r.. k} sunt solutii liniar independente ale relatiei de recurent˘ ¸ ¸ ¸a Dac˘ r˘d˘cinile ri (i = 1. + ak rk = 0 (6. ck se pot determina din conditiile initiale. numit˘ ecuatie caracteristic˘: ¸ a ¸ a ¸ a a0 + a1 r + a2 r2 + . + ak ri = 0 + .. obtinem: a a n+k n+2 n+1 2 k n n = ri a0 + a1 ri + a2 ri + ..

c1 . n n • Ecuatia caracteristic˘ admite r˘d˘cini complexe multiple Dac˘ ¸ a a a a ecuatia caracteristic˘ admite perechea de r˘d˘cini complexe ¸ a a a r = aeib ... deci ¸i conjugata r = ae−ib = a(cos b − i sin b) este r˘d˘cin˘ ¸ s ¯ a a a pentru ecuatia caracteristic˘.. x(2) = an sin bn. + cp1 −1 np1 −1 + . Se determin˘ r˘d˘cinile ecuatiei caracteristice a a a ¸ 2. Se ˆ ınsumeaz˘ ¸i se obtine solutia general˘ ˆ functie de n constante as ¸ ¸ a ın ¸ arbitrare.1.... rs ¸i r˘d˘cinile mula ¸ a a a s a a tiple rs1 . Dac˘ ecuatia caracteristic˘ are r˘d˘cinile simple r1 . + cs rs + (1) (1) (1) c1 + c2 n + ... . s ¸ ¸ a .. + cpt −1 npt −1 + unde c1 . Acest lucru se mai poate demonstra u¸or dac˘ ¸ ¸ ¸a s a ¸inem cont de faptul c˘ o r˘d˘cin˘ multipl˘ de ordinul p a unui polinom P (x) este t a a a a a r˘d˘cin˘ ¸i a polinoamelor derivate P ′ (x). Dac˘ sunt precizate conditiile initiale atunci se determin˘ constantele a ¸ ¸ a ¸i se obtine o solutie unic˘. cp1 −1 ... p2 . ¸ a a a ¸ a 3.... r2 .6. 4.... .... x(2) = nan cos bn.. . xn = nk−1 an sin bn. Atunci solutiile corespunz˘toare acestora ˆ sistemul ¸ a ¸ a ın fundamental de solutii pentru recurenta liniar˘ ¸i omogen˘ sunt ¸ ¸ as a x(1) = an cos bn.. .. . care se pot determina din conditiile initiale. P ′′ (x)... . (t) (t) (1) c1 + c2 n + . cpt −1 sunt constante.. xn = nan sin bn. . c1 . rs+t de multiplicitate p1 . ¸ ¸ • Ecuatia caracteristic˘ admite r˘d˘cini complexe simple ¸ a a a Fie r = aeib = a(cos b + i sin b) o r˘d˘cin˘ complex˘. cs ... x(k) = nk−1 an cos bn.... . r = ae−ib b = 0 ¯ de ordin de multiplicitate k... Se scrie contributia fiec˘rei r˘d˘cini la solutia general˘. . P (p−1) (x).. RELATII DE RECURENTA ¸ ¸˘ 79 este o solutie a relatiei de recurent˘. Ecuatia caracteristic˘ a a a a ¸ a are coeficienti reali.. .. n n n (k+2) (2k) x(k+1) = an sin bn.. atunci solutia general˘ a relatiei de recurent˘ este ¸ a ¸ ¸a xn n n n = c1 r1 + c2 r2 + . rs+2 . atunci solutiile corespunz˘toare acestora ˆ sistemul ¸ a ın fundamental de solutii sunt ¸ x(1) = an cos bn. n (1) (1) (t) (t) Pentru a obtine solutia general˘ a recurentei omogene de ordinul n cu coeficienti ¸ ¸ a ¸ ¸ constanti se procedeaz˘ astfel: ¸ a 1. pt (s+p1 +p2 +... a a as Solutia general˘ este suma dintre solutia general˘ corespunz˘toare r˘d˘cinilor ¸ a ¸ a a a a simple ale ecuatiei caracteristice ¸i solutia general˘ corespunz˘toare r˘d˘cinilor ¸ s ¸ a a a a multiple..+pt = k).

2 Ecuatii recurente neomogene ¸ 6. se procedeaz˘ ca ˆ cazul omogen. Rescriem recurenta astfel ¸ tn − 2tn−1 = 1 . Intuitiv.1 O form˘ simpl˘ a a Consider˘m acum recurente de urm˘toarea form˘ mai general˘ a ¸ a a a a0 tn + a1 tn−1 + . + ak tn−k = bn p(n) unde b este o constant˘. n = 1 iar t0 = 0. iar p(n) este un polinom ˆ n de grad d. a a a De exemplu. este suficient s˘ lu˘m urm˘toarea ecuatie caracteristic˘: ¸ a a a a ¸ a (a0 xk + a1 xk−1 + + ak )(x − b)d+1 = 0 Odat˘ ce s-a obtinut aceast˘ ecuatie. b = 3 ¸i p(n) = 1. ˆ timp ce factorul (x − 3) a ap˘rut ca rezultat al a ¸ ¸ ın a calculelor efectuate pentru a sc˘pa de partea dreapt˘. ˆ In s Inmultim recurenta cu 3. ¸i obtinem ¸ ¸ s ¸ 3tn − 6tn−1 = 3n+1 ˆ Inlocuind pe n cu n + 1 ˆ recurenta initial˘. pentru a rezolva ecuatia a a a ¸ initial˘. Ecuatia caracteristic˘ este: ¸ ¸a a ¸ a x2 − 5x + 6 = 0 adic˘ (x − 2)(x − 3) = 0. ANALIZA ALGORITMILOR RECURSIVI 6. o astfel de recurent˘ poate fi: ¸a tn − 2tn−1 = 3n ˆ acest caz. se poate ar˘ta c˘.. a ¸ a ¸ a ın Vom rezolva acum recurenta corespunzatoare problemei turnurilor din Hanoi: ¸ tn = 2tn−1 + 1.. Ideea general˘ a ın a este s˘ reducem un astfel de caz la o form˘ omogen˘. observ˘m c˘ factorul (x − 2) corespunde p˘rtii a a a a¸ stˆngi a recurentei initiale. a a Generalizˆnd acest procedeu. avem ın ¸ ¸ a tn+1 − 2tn = 3n+1 Sc˘dem aceste dou˘ ecuatii a a ¸ tn+1 − 5tn + 6tn−1 = 0 Am obtinut o recurent˘ omogen˘.2.80 CAPITOLUL 6.

. c1 este deci −1. Putem obtine chiar ceva mai mult.. Stim c˘ t0 = 0.. rezult˘ tn ∈ O(2n ).2. Ecuatia ¸ caracteristic˘ este atunci (x − 2)(x − 1) = 0.. deducem c˘ c2 > 0. g˘sim ın ¸ ¸ a a 1 = tn − 2tn−1 = c1 + c2 2n − 2(c1 + c2 2n−1 ) = −c1 Indiferent de conditia initial˘. nu este necesar s˘ calcul˘m efectiv a a a a constantele ˆ solutia general˘. cu b = 1 si p(n) = 1. d1 = 1 ¸i b2 = 2. T0 = 0.. = 0 . Dac˘ ne intereseaz˘ doar ordinul lui tn . Avem atunci ın a tn ∈ Ω(2n ) ¸i deci. obtinem ¸ ¸ ¸ tn = 2n − 1. ¸ ¸ a 6..2 O form˘ mai general˘ a a O ecuatie recurent˘ neomogen˘ de form˘ mai general˘ este: ¸ a a a a k j=0 aj T n − j = bn · pd1 (n) + bn · pd2 (n) + . iar ecuatia caracteristic˘ complet˘ este: ¸ a a (r − 2)(r − 1)2 (r − 2) = 0  j=0 aj · rk−j  · (r − b1 ) d1 +1 · (r − b2 ) d2 +1 · . Solutia general˘ a a ¸ s ¸ a recurentei este: ¸ tn = c1 1n + c2 2n Avem nevoie de dou˘ conditii initiale. ıi s d2 = 0. cu solutiile 1 ¸i 2. + cd Ecuatia caracteristic˘ complet˘ este: ¸ a a   k Exemplul 3: Tn = 2T (n − 1) + n + 2n . Acestui caz ˆ corespund b1 = 1. p2 (n) = 1. ECUATII RECURENTE NEOMOGENE ¸ 81 care este de forma general˘ prezentat˘ la ˆ a a ınceput. Dac˘ ¸tim c˘ tn = c1 1n + c2 2n . deoarece avem ˆ mod evident tn ≥ n. tn ∈ Θ(2n ). Substituind s ¸ solutia general˘ ˆ ¸ a ınapoi ˆ recurenta initial˘. p1 (n) = n. ın ¸ a as a a Din faptul c˘ num˘rul de mut˘ri a unor discuri nu poate fi negativ sau a a a constant. n ≥ 1. Din conditiile initiale. 2 1 ˆ care ın pd (n) = nd + c1 nd−1 + . pentru a g˘si cea de-a a ¸ ¸ ¸ a a doua conditie calcul˘m ¸ a t1 = 2t0 + 1 = 1.6.2.

A¸a numita teorem˘ ¸ a s a Master d˘ o metod˘ general˘ pentru rezolvarea unor astfel de recurente cˆnd f (n) a a a ¸ a este un simplu polinom. aceasta este o ¸ ıns˘ tehnic˘ de rezolvare a celor mai multe relatii de recurent˘ provenite din metoda a ¸ ¸a Divide et Impera. R˘d˘cina arborelui contine valoarea f (n).2. Pentru a rezolva astfel de ecuatii recurente vom reprezenta arborele generat ¸ de ecuatia recursiv˘. dac˘ f (n) = O nlogb (a−ε) cu ε > 0 atunci T (n) = Θ nlogb a a 2. 6. ANALIZA ALGORITMILOR RECURSIVI T (n) = c1 · 1n + c2 · n · 2n + c3 · 2n + c4 · n · 2n  T (0) = 0   T (1) = 2T (0) + 1 + 21 = 3 T (2) = 2T (1) + 2 + 22 = 12    T (3) = 2T (2) + 3 + 23 = 35 Deci  c3 c1 +   c + c + 2c + 2c 1 2 3 4 c1 + 2c2 + 4c3 + 8c4    c1 + 3c2 + 8c3 + 24c4  =0 c1   c =3 2 ⇒ c3 = 12    = 35 c4 = −2 = −1 =2 =1 T (n) = −2 − n + 2n+1 + n · 2n = O(n · 2n ). ¸i a ¸ a ¸ s multe recurente utile nu sunt de forma (6.82 cu solutia: ¸ CAPITOLUL 6.2. ¸i ea are noduri ¸ a a a ¸ s descendente care sunt noduri r˘d˘cin˘ pentru arborele provenit din T (n/b). Din fericire ˆ a. teorema Master nu functioneaz˘ pentru toate functiile f (n).2.6). dac˘ f (n) = Θ nlogb a atunci T (n) = Θ nlogb a lg n a 3.6) unde a ¸i b sunt constante iar f (n) este o functie (aplicarea metodei Divide et s ¸ Impera conduce de obicei la o astfel de ecuatie recurent˘). ≤ c·f (n) cu c < 1 atunci T (n) = Θ (f (n)). dac˘ f (n) = Ω nlogb (a+ε) ¸i a·f a s n b Din p˘cate. Solutia dat˘ de teorema master este: ¸ a 1. a a a .3 Teorema master De multe ori apare relatia de recurent˘ de forma ¸ ¸a T (n) = aT (n/b) + f (n) (6.

s a ¸ a ¸a Presupunem c˘ T (1) = f (1). a Cu aceast˘ reprezentare este foarte clar c˘ T (n) este suma valorilor din a a nodurile arborelui. ¸ a Dac˘ f (n) este un factor constant mai mic decˆt f (b/n). s ¸ s Teorema 1 (Teorema Master) Relatia de recurenta T (n) = aT (n/b)+f (n) are ¸ ¸˘ urm˘toarea solutie: a ¸ • dac˘ af (n/b) = αf (n) unde α < 1 atunci T (n) = Θ(f (n)). a • dac˘ af (n/b) = f (n) atunci T (n) = Θ(f (n) logb n). + ak f (n/bk ) unde k este adˆncimea arborelui de recursivitate. a a Ultimul termen diferit de zero ˆ sum˘ este de forma ak = alogb n = nlogb a (ultima ın a egalitate fiind ˆ alnit˘ ˆ liceu!).6. ¸ a . Din n/bk = 1 rezult˘ k = logb n. obtinem a a ¸ T (n) = f (n) + af (n/b) + a2 f (n/b2 ) + a3 f (n/b3 ) + . ¸ a a a Suma ˆ acest caz este o constant˘ ˆ ın a ınmultit˘ cu primul termen care este f (n). a Demonstratie: Dac˘ f (n) este un factor constant mai mare decˆt f (b/n).2. a • dac˘ af (n/b) = βf (n) unde β > 1 atunci T (n) = Θ(nlogb a ). atunci ¸ a a prin inductie se poate ar˘ta c˘ suma este a unei progresii geometrice descresc˘toare.. ECUATII RECURENTE NEOMOGENE ¸ 83 Pe nivelul i se afl˘ nodurile care contin valoarea ai f (n/bi ). Suma ˆ acest a a a ın caz este o constant˘ ˆ a ınmultit˘ cu ultimul termen care este nlogb a . Recursivitatea se a ¸ opre¸te cˆnd se obtine un caz de baz˘ pentru recurent˘. atunci prin inductie a a ¸ se poate ar˘ta c˘ suma este a unei progresii geometrice cresc˘toare.. ıntˆ a ın Acum putem u¸or enunta ¸i demonstra teorema Master. Presupunˆnd c˘ fiecare nivel este plin.

deci T (n) = Θ(nlog2 3 ). a ¸ unde f (n) este o functie simpl˘ ¸i S() are o recurent˘ mai u¸oar˘. deci a a T (n) = S(n − 2) = O((n − 2) log(n − 2) = O(n log n). ¸ Aici af (n/b) = 3n/4 iar f (n) = n.4 Transformarea recurentelor ¸ La Mergesort am avut o relaie de recurent˘ de forma T (n) = 2T (n/2) + n ¸i ¸ ¸a s am obtinut solutia T (n) = O(n log2 n) folosind teorema Master (metoda arborelui ¸ ¸ de recursivitate). Cˆnd n este impar. 6. nu vom atinge niciodat˘ cazul de baz˘ T (1) = 0. a 2. rezult˘ α = 3/2. a Exemple. Teorema Master ne spune acum c˘ S(n) = O(n log n). Selectia aleatoare: T (n) = T (3n/4) + n. a ¸a a a recurenta ne cere s˘ sort˘m un num˘r elemente care nu este ˆ ¸ a a a ıntreg! Mai r˘u chiar. putem rezolva recurente pentru s a ¸ care nu se poate aplica teorema Master. rezult˘ α = 1 deci T (n) = Θ(n log2 n). ¸ as ¸a s a Urm˘toarele inegalit˘¸i sunt evidente: a at T (n) ≤ 2T (⌈n/2⌉) + n ≤ 2T (n/2 + 1) + n. aleas˘ astfel ˆ at s˘ fie satisf˘cut˘ recurenta din teorema Master a a ıncˆ a a a ¸ S(n) ≤ S(n/2) + O(n). a dac˘ n nu este o putere a lui 2. vom compara ¸ a dou˘ versiuni ale recurentei pentru functia S(n + α): a ¸ ¸ S(n) ≤ 2S(n/2) + O(n) T (n) ≤ 2T (n/2 + 1) + n ⇒ T (n + α) ≤ 2T (n/2 + α) + O(n) ⇒ T (n + α) ≤ 2T ((n + α)/2 + 1) + n + α Pentru ca aceste dou˘ recurente s˘ fie egale. a 3. a ¸ a care implic˘ α = 2. a a a Pentru a obtine o recurent˘ care s˘ fie valid˘ pentru orice valori ˆ ¸ ¸a a a ıntregi ale lui n. trebuie ca n/2+α = (n+α)/2+1. Aici af (n/b) = 3n/2 iar f (n) = n.84 CAPITOLUL 6. trebuie s˘ determin˘m cu atentie marginile inferioar˘ ¸i superioar˘: a a ¸ as a T (n) = T (⌊n/2⌋) + T (⌈n/2⌉) + n. dar a a a pentru alte valori ale lui n aceast˘ recurent˘ nu este corect˘. unde α este o constant˘ a ¸ a necunoscut˘. atunci prin inductie se poate ar˘ta c˘ fiecare din cei a ¸ a a k + 1 termeni din sum˘ sunt egali cu f (n). Pentru a obtine valoarea corect˘ a lui α. iar f (n) = n. Aceast˘ modalitate este corect˘ dac˘ n este o putere a lui 2. ANALIZA ALGORITMILOR RECURSIVI Dac˘ af (b/n) = f (n). Mergesort: T (n) = 2T (n/2) + n. Metoda transform˘rii domeniului rescrie functia T (n) sub forma S(f (n)). Acum definim o nou˘ functie S(n) = T (n + α). Aici af (n/b) = n. a Folosind accea¸i tehnic˘ a arborelui recursiv. rezult˘ α = 3/4. . Algoritmul de multiplicare al lui Karatsuba: T (n) = 3T (n/2) + n. 1. deci T (n) = Θ(n).2.

ˆ exemplele care urmeaz˘. T (n) = Θ(n log n) este un rezultat ˆ ıntemeiat de¸i am ignorat marginile s inferioar˘ ¸i superioar˘ de la ˆ as a ınceput! Transformarea domeniului este util˘ pentru ˆ aturarea marginilor inferioar˘ a ınl˘ a ¸i superioar˘. ecuatiile recurente pot fi aduse la aceast˘ form˘ a ¸ a a printr-o schimbare de variabil˘. deci b = 2. a Un prim exemplu este recurenta T (n) = 4T (n/2) + n. pentru c˘ cele dou˘ subprobleme s a a au dimensiuni diferite. a Deci. ¸i a termenilor de ordin mic din argumentele oric˘rei recurente care s a s a ¸ se potrive¸te un pic cu teorema master sau metoda arborelui de recursivitate. ECUATII RECURENTE NEOMOGENE ¸ 85 Un argument similar d˘ o ajustare a marginii inferioare T (n) = Ω(n log n). p(k) = k. ¸ ¸ ¸ a Presupunem pentru ˆ ınceput c˘ n este o putere a lui 2. ¸i √ and metoda arborelui de recursivitate nu obtinem s utilizˆ ¸ decˆt ni¸te margini slabe n << T (n) << n.6. n > 1 Facem schimbarea de variabil˘ t(k) = T (2k ) ¸i obtinem: a s ¸ t(k) − 2 · t(k − 1) = k · 2k .2. putem rezolva recurente mult mai a ¸ complicate. d = 1 Ecuatia caracteristic˘ complet˘ este: ¸ a a (r − 2)3 = 0 cu solutia ¸ t(k) = c1 · 2k + c2 · k · 2k + c3 · k 2 · 2k Deci T (n) = c1 · n + c2 · n · log n + c3 · n · log2 n ∈ O(n · log2 n|n = 2k ) Uneori. vom nota cu T (n) termenul general al In a recurentei si cu tk termenul noii recurente obtinute printr-o schimbare de variabil˘. O schimbare de variabil˘ aplicabil˘ pentru ecuatii a a a ¸ de recurent˘ de tip multiplicativ este: ¸a n = 2k ⇔ k = log n De exemplu. a ın ¸ a a a pentru care costul operatiei de c˘utare ˆ a ındepline¸te relatia de recurent˘ s ¸ ¸a T (n) = T (n/2) + T (n/4) + 1. s Exist˘ ˆ geometria computational˘ o structur˘ de date numit˘ arbore pliat. a s Dac˘ nu au forma standard. Aceasta nu se potrive¸te cu teorema master. printr-o schimbare de variabil˘. fie T (n) = 2 · T (n/2) + n · log n. n > 1 .

s ¸ a Atunci. Obtinem succesiv a ¸ T (2k ) = 3T (2k−1 ) + c2k tk = 3tk−1 + c2k cu ecuatia caracteristic˘ ¸ a tk = c1 3k + c2 2k T (n) = c1 3lg n + c2 n (x − 3)(x − 2) = 0 (x − 4)2 = 0 . ajungem la recurenta a tk = 4tk−1 + 4k cu ecuatia caracteristic˘ ¸ a ¸i solutia general˘ tk = c1 42 + c2 k42 .86 CAPITOLUL 6. n > 1 Procedˆnd la fel. n > 1 c fiind o constant˘. ˆ s Inlocuim la loc pe k cu log2 n T (n) = c1 n2 + c2 n Rezult˘ a T (n) ∈ O(n2 |n este o putere a lui 2) Un al doilea exemplu ˆ reprezint˘ ecuatia ıl a ¸ T (n) = 4T (n/2) + n2 . ANALIZA ALGORITMILOR RECURSIVI ˆ care ˆ ın ınlocuim pe n cu 2k . notam tk = T (2k ) = T (n) ¸i obtinem s ¸ tk = 4tk−1 + 2k Ecuatia caracteristic˘ a acestei recurente liniare este ¸ a ¸ (x − 4)(x − 2) = 0 ¸i deci. tk = c1 4k + c2 2k . s˘ consider˘m ¸i exemplul In a s a a s T (n) ∈ O(n2 log n|n este o putere a lui 2) T (n) = 3T (n/2) + cn. T (n) = c1 n2 + c2 n2 lg n ¸i obtinem s ¸ ˆ sfˆr¸it.

pentru a = bk . n/n0 s pentru a < bk .   Θ(nlogb a ). ¸i c sunt numere reale pozitive. S˘ se rezolve ecuatia: a ¸ Fn+2 = Fn+1 + Fn .3. a este o putere a lui b. PROBLEME REZOLVATE ¸i. T (n) ∈ O(nlg 3 |n este o putere a lui 2) 87 Putem enunta acum o proprietate care este util˘ ca retet˘ pentru analiza ¸ a ¸ a algoritmilor cu recursivit˘¸i de forma celor din exemplele precedente. F0 = 0. at Fie T : N −→ R+ o functie eventual nedescresc˘toare ¸ a T (n) = aT (n/b) + cnk .3 Probleme rezolvate 1. deoarece s alg b = blg a obtinem ¸ T (n) = c1 nlg 3 + c2 n deci.6. pentru a > bk . r2 = . 6. Atunci avem  Θ(nk ). Ecuataia caracteristic˘ corespunz˘toare ¸ a a r2 − r − 1 = 0 are solutiile ¸ √ √ 1− 5 1+ 5 . n > n0 unde: n0 ≥ 1. b ≥ 2 si k ≥ 0 sunt ˆ ıntregi. Determin˘m constantele c1 ¸i c2 din conditiile initiale F0 = 0 ¸i F1 = 1. F1 = 1. a s ¸ ¸ s Rezolvˆnd sistemul a c1 + c2 = 0 c1 √ 1+ 5 2 + c2 √ 1− 5 2 =1 . r1 = 2 2 √ 1+ 5 2 n Solutia general˘ este ¸ a Fn = c1 + c2 √ 1− 5 2 n .  T (n) ∈ Θ(nk log n).

S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+3 = 6xn+2 − 12xn+1 + 8xn . c2 = ¸ ¸ a 5 Solutia general˘ este: ¸ a xn = n 1 + 2 5 1 2n − (−3)n . 5 1 2 x0 = 0. c2 = ¸ ¸ a Solutia general˘ este: ¸ a xn = 3 2 x0 = 0. ANALIZA ALGORITMILOR RECURSIVI 1 1 obtinem c1 = √5 ¸i c1 = − √5 . s 5 3. x1 = 1. ¸i c3 = − 1 . x1 = 2. . Ecuatia caracteristic˘ corespunz˘toare este: ¸ a a r3 − r2 − 8r + 12 = 0 ¸i are solutiile: r1 = r2 = 2 ¸i r3 = −3. Ecuatia caracteristic˘ corespunz˘toare este: ¸ a a r3 − 6r2 + 12r − 8 = 0 ¸i are solutiile: r1 = r2 = r3 = 2. solutia relatiei de recurent˘ care define¸te numerele lui Fibonacci este: ¸ ¸ ¸a s √ n √ n 1 1+ 5 1− 5 1 Fn = √ −√ 2 2 5 5 2. x1 = 2. x0 = 0. S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+3 = xn+2 + 8xn+1 − 12xn . x2 = 4. ¸i c3 = − 1 .88 CAPITOLUL 6. s ¸ s Solutia general˘ este de forma: ¸ a xn = (c1 + nc2 )2n + c3 (−3)n . s 2 3 1 n − n2 2n = (3n − n2 )2n−1 . 2 2 4. Din conditiile initiale rezult˘ constantele: c1 = 1 . Din conditiile initiale rezult˘ constantele: c1 = 0. S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+2 = 2xn+1 − 2xn . x2 = 3. s ¸ Solutia general˘ este de forma: ¸ a xn = (c1 + c2 n + c3 n2 )2n . ¸ s Deci.

. + 3 sin 2 4 4 6. T (0) = 0. r2 = 2 cos − i sin . r2 = 1 + i ¸i r3 = 1 − i. 4 Solutia general˘ este de forma: ¸ a xn = √ 2 n c1 cos nπ nπ .3. Ecuatia caracteristic˘ corespunz˘toare este: ¸ a a r3 − 4r2 + 6r − 4 = 0 ¸i are solutiile: r1 = 2. S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+3 = 4xn+2 − 6xn+1 + 4xn . + c2 sin 4 4 Din conditiile initiale rezult˘ constantele: c1 = 0 si c2 = 1. ¸ ¸ a 2 Solutia general˘ este: ¸ a √ n 2 nπ nπ n−1 xn = −2 + cos . 4 3 1 Din conditiile initiale rezult˘ constantele: c1 = − 2 . c2 = 1 si c3 = 2 . s ¸ s Solutia general˘ este de forma: ¸ a xn = c1 2n + c2 √ 2 n x0 = 0. x1 = 1. T (1) = 1. PROBLEME REZOLVATE Ecuatia caracteristic˘ corespunz˘toare este: ¸ a a r2 − 2r + 2 = 0 89 ¸i are solutiile: r1 = 1 + i ¸i r2 = 1 − i care se pot scrie sub form˘ trigonometric˘ s ¸ s a a astfel: √ √ π π π π r1 = 2 cos + i sin .6. cos √ nπ 2 + c3 4 n sin nπ . ¸ ¸ a Solutia general˘ este: ¸ a xn = √ 2 n sin nπ . 4 5. x2 = 1. 4 4 4 4 Solutiile fundamentale sunt: ¸ x(1) = n √ 2 n cos √ nπ (2) 2 . S˘ se rezolve relatia de recurent˘: a ¸ ¸a T (n) − 3T (n − 1) + 4T (n − 2) = 0. xn = 4 n sin nπ . n ≥ 2.

S˘ se rezolve relatia de recurent˘: a ¸ ¸a T (n) = 2T (n/2) + n lg n. avem af (n/b) = 2n lg n − 2n. Pentru n suficient de mare. avem 2f (n) > af (n/b) > 1. n ≥ 3.9f (n). Suma este m˘rginit˘ ¸i inferior ¸i superior de c˘tre serii geometrice cresc˘toare. r2 = r3 = 2 deci T (n) = c1 1n + c2 2n + c3 n2n Determinarea constantelor  c1 + c2  c + 2c2 + 2c3  1  c1 + 4c2 + 8c3 Deci T (n) = −2 + 2n+1 − n n 2 = 2n+1 − n2n−1 − 2. a as s a a deci solutia este T (n) = Θ(nlog2 4 ) = Θ(n2 ). ˆ acest caz.90 CAPITOLUL 6. T (2) = 2. ANALIZA ALGORITMILOR RECURSIVI Ecuatia caracteristic˘ r2 − 3r + 4 = 0 are solutiile r1 = −1. Ecuatia caracteristic˘: ¸ a r3 − 5r2 + 8r − 4 = 0 ⇒ r1 = 1. deci ¸ a ¸ T (n) = c1 (−1)n + c2 4n Constantele se determin˘ din conditiile initiale: a ¸ ¸ c1 + c2 = 0 −c1 + 4c2 = 1 ⇒ 1 c1 = − 5 c2 = 1 5 Solutia este: ¸ T (n) = 1 n [4 − (−1)n ] . S˘ se rezolve relatia de recurent˘: a ¸ ¸a T (n) = 5T (n − 1) − 8T (n − 2) + 4T (n − 3). S˘ se rezolve relatia de recurent˘: a ¸ ¸a T (n) = 4T (n/2) + n lg n. 9. care nu este tocmai dublul lui In f (n) = n lg n. cu T (0) = 0. 2  c1 = −2 =0  = 1 ⇒ c2 = 2   1 =2 c3 = − 2 8. r2 = 4. 5 7. . Acest truc nu merge ˆ cazurile doi ¸i ¸ ın s trei ale teoremei Master. T (1) = 1.

(Quicksort aleator). avem c˘ a a T (n) = Θ(n log n). Putem s˘ obtinem o margine superioar˘ ignorˆnd cazurile a ¸ a a de baz˘ ˆ totalitate ¸i crescˆnd arborele spre infinit. avem un arbore recursiv ”trunchiat”. deasupra oric˘rei frunze) au suma a a n. Nodurile din orice nivel complet (adic˘. iar diferenta nu este un factor constant. 12. pentru a obtine o margine inferioar˘ pentru T (n). vom subevalua ¸ a T (n) contorizˆnd numai nodurile din arbore pˆn˘ la nivelul frunzei care este cea a a a mai putin adˆnc˘. ¸i putem obtine o margine a ın s a s ¸ inferioar˘ contorizˆnd numai nodurile din nivelurile complete. S˘ se rezolve relatia de recurent˘: ¸ a a ¸ ¸a T (n) = T (n/5) + T (7n/10) + n. Deoarece aceste margini difer˘ numai printr-un factor constant. (Selectie determinist˘). j 10.. Din nou.6. 11. Aceste observatii ne dau marginile inferioar˘ ¸i superioar˘: ¸ a a ¸ as a n log4 n ≤ T (n) ≤ n log4/3 n... aceasta ˆ In ınseamn˘ a c˘ adˆncimea arborelui este cel mult lg n − 1. PROBLEME REZOLVATE 91 Nu putem aplica teorema Master pentru c˘ af (n/b) = n/(lg n − 1) nu este a egal˘ cu f (n) = n/ lg n. vom supraevalua T (n) ignorˆnd cazurile ¸ a a de baz˘ ¸i extinzˆnd arborele ˆ jos c˘tre nivelul celei mai adˆnci frunze. Avem cel mult lg lg n niveluri dar acum avem nodurile de pe nivelul i care au suma 2i n. deci T (n) = Θ(n). Dac˘ ne uit˘m numai la a a nivelurile complete ale arborelui. Suma a a s a ın tuturor nodurilor de pe nivelul i este n/(lg n − i). S˘ se rezolve relatia de recurent˘: a ¸ ¸a √ √ T (n) = 2 n · T ( n) + n. S˘ se rezolve relatia de recurent˘: a ¸ ¸a T (n) = T (3n/4) + T (n/4) + n. la fel ca a a . s Pentru a obtine o margine superioar˘. Avem o serie geometric˘ cresc˘toare a sumelor nivelurilor. ˆ particular. ˆ ambele situatii. a a lg n−1 T (n) = i=0 n = lg n − i lg n j=1 n = nHlg n = Θ(n lg lg n).3. a a In ¸ seriile geometrice sunt majorate de termenul cel mai mare. deci este ca ˆ primul a a ın caz al teoremei Master. observ˘m c˘ suma pe nivel formeaz˘ o serie a a a geometric˘ descresc˘toare T (n) = n + 9n/10 + 81n/100 + . deci este la fel ca ˆ ultimul caz al teoremei Master ¸i orice frunz˘ are nivelul ın s a ˆ ıntre log4 n ¸i log4/3 n. ˆ acest caz nodurile de pe acela¸i nivel al arborelui de recursivitate au diferite In s valori. a ¸ Trebuie s˘ calcul˘m suma pe fiecare nivel ¸i suma total˘ ˆ alt mod. as a ın a a Similar.

92 CAPITOLUL 6. Se obtine: a ¸ T (n) = Θ(2lg lg n n) = Θ(n log n). Suma nodurilor de pe nivelul i este 4i n. Se obtine ¸ T (n) = Θ(4lg lg n n) = Θ(n log2 n). deci nu trebuie decˆt s˘ avem grij˘ de ın a a a aceste niveluri. 13. S˘ se rezolve relatia de recurent˘: a ¸ ¸a √ √ T (n) = 4 n · T ( n) + n. ANALIZA ALGORITMILOR RECURSIVI ˆ cazul doi din teorema Master. a a la fel ca ˆ cazul doi din teorema master. deci T (n) este majorat˘ de suma nivelurilor cele ın a mai adˆnci. . Avem o serie geometric˘ cresc˘toare.

Se poate mai repede? Da! ˆ artim ¸irul ˆ ¸ Imp˘ ¸ s ın dou˘ ¸i determin˘m vmin ¸i vmax ˆ cele dou˘ zone.Capitolul 7 Algoritmi elementari 7. Atunci a ¸i b trebuie s˘ satisfac˘ sistemul de ecuatii s a a ¸ 2a + b = 1 an + b = 2(an/2 + b) + 2 93 . La fel pentru vmax. s Cum rezolv˘m aceast˘ relatie de recurent˘? B˘nuim c˘ solutia este de forma a a ¸ ¸a a a ¸ T (n) = an + b. n vmin = minim(vmin. Compar˘m vmin1 cu vmin2 as a s ın a a ¸i stabilim vminm.1 Operatii cu numere ¸ 7.1. Dar cˆte sunt ˆ minus? Presupunem c˘ n este o putere a lui 2 ¸i T (n) este a a ın a s num˘rul de comparatii. Proced˘m astfel: a vmin = x[1]..1 Minim ¸i maxim s S˘ presupunem c˘ dorim s˘ determin˘m valorile minim˘ ¸i maxim˘ dintru-un a a a a as a vector x[1. for i=2. Apar cˆte dou˘ comparatii ˆ plus de fiecare s a a ¸ ın dat˘. vmax = x[1]. x[i]) vmax = maxim(vmax.n. Atunci a ¸ T (n) = 2T (n/2) + 2 ¸i T (2) = 1. Prelucrarea se repet˘ pentru cele dou˘ s a a zone (deci se folose¸te recursivitatea). x[i]) Evident se fac 2n − 2 comparatii.

while(d*d<=n) { nd=0.x[2]) vmax = maxim(x[1].x[i+1]) if cmin < vmin vmin = cmin if vmax > cmax vmax = cmax Fiecare iteratie necesit˘ trei comparatii. algoritmul are forma: s a vmin = minim(x[1].94 CAPITOLUL 7. Se poate demonstra c˘ num˘rul de comparatii a a a ¸ este 3 ⌈n/2⌉ − 2 pentru a afla minimum ¸i maximum.} . atunci: d(n) = (1 + α1 )(1 + α2 ). · pαk 2 1 k (7.. d=2.1. while(n%d==0){nd++.x[2]) for(i=3.n=n/d.x[i+1]) cmax = maxim(x[i]. deci (pentru n putere a lui 2)..nd=0. iar initializarea variabilelor necesit˘ ¸ a ¸ ¸ a o comparatie.1) se nume¸te descompunere canonic˘. ¸ s adic˘ 75% din algoritmul anterior.1. ¸ 7.i=i+2) cmin = minim(x[i]. ALGORITMI ELEMENTARI care are solutia b = −2 ¸i a = 3/2.2 Divizori Fie n un num˘r natural. Presupunem c˘ a a ¸ a a ¸irul are un num˘r par de termeni. Ciclul se repet˘ de (n − 2)/2 ori. Atunci. Descompunerea ˆ facori primi a ın n = pα1 · pα2 · .i<n.} p=p*(1+nd).p=1. Dac˘ not˘m prin d(n) num˘rul divizorilor lui s a a a a n ∈ N. while(n%d==0){nd++.nd.2) Pentru calculul lui d(n) putem folosi urm˘torul algoritm: a static int ndiv(int n) { int d.n=n/d. d=3.(1 + αk ) (7. T (n) = 3n/2 − 2. deci avem un total de 3n/2 − 2 ¸ a comparatii pentru n par.1... s O idee similar˘ poate fi aplicat˘ pentru varianta secvential˘.

2. } . a = b. if(nr==2) return true. static int cmmdc(int a. while((d*d<=nr)&&(nr%d!=0)) d=d+2. return p. b = c. } while((c=a%b) != 0) { a = b. } 7. if(nr%2==0) return false.1.7. } if(n!=1) p=p*2. Descompunerea ˆ factori a unui num˘r natural n poate necesita ˆ ın a√ ıncercarea tuturor numerelor naturale din intervalul [2. De exemplu dac˘ a = 1134 = 2 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 7 ¸i b = 308 = a s 2 ∗ 2 ∗ 7 ∗ 11 atunci cmmdc(a. Un algoritm eficient pentru calculul cmmdc(a.2. ALGORITMUL LUI EUCLID p=p*(1+nd). else return false. if (a < b) { c = a.2 Algoritmul lui Euclid 7.1 Algoritmul clasic Un algoritm pentru calculul celui mai mare divizor comun (cmmdc) a dou˘ a numere naturale poate fi descompunerea lor ˆ factori ¸i calculul produsului tuturor ın s divizorilor comuni.} return b.3 Numere prime Pentru testarea primalit˘¸i unui num˘r putem folosi urm˘torul algoritm: at a a static boolean estePrim(int nr) { int d. if(nr<=1) return false. int b} { int c. d=d+2. d=3. if(d*d>nr) return true. n]. b) este algoritmul lui Euclid. b = c. b) = 2 ∗ 7 = 14. } 95 7.

a2 = 210. c) = cmmdc(c. CAPITOLUL 7. d0 = 3. b1 = 210. b). b) = xk ∗ ak + yk ∗ bk = xk ∗ bk−1 + yk ∗ (ak−1 − dk−1 ∗ bk−1 ) = yk ∗ ak−1 + (xk − yk ∗ dk−1 ) ∗ bk−1 . a Demonstr˘m ¸i inegalitatea contrar˘ cmmdc(a − x ∗ b. deci a = c1 ∗ d ¸i b = c2 ∗ d. b). aa a Presupunem c˘ d divide a ¸ b. a Substituind aceste expresii pentru ak ¸i bk obtinem s ¸ cmmdc(a. b). Dar pentru cele a dou˘ numere a ¸i b din final. Atunci d divide a − x ∗ b a s s pentru c˘ a − x ∗ b = (c1 − x ∗ c2 ) ∗ d. yk−1 = xk − yk ∗ dk−1 . Aceste numere pot fi calculate parcurgˆnd ˆ ıncˆ a ınapoi algoritmul clasic al lui Euclid. pentru c˘ b divide a. Prin inductie rezult˘ c˘ cel mai mare divizor comun al ultimelor dou˘ numere ¸ a a a este egal cu cel mai mare divizor comun al primelor dou˘ numere. a a s s ak = bk−1 ¸i bk = ak−1 mod bk−1 = ak−1 − dk−1 ∗ bk−1 . b). Fie xk ¸i yk s s a ¸ s numerele care indeplinesc relatia xk ∗ ak + yk ∗ bk = cmmdc(ak . cmmdc(a. b). ALGORITMI ELEMENTARI b = 308 se obtine: ¸ b0 = 308. a s a 7. b).2. ¸ Prin inductie presupunem c˘ xk ¸ yk exist˘. putem lua xk = 0 ¸ yk = 1. a2 = 210. b) = b. b) <= cmmdc(a. unde s dk−1 = ak−1 /bk−1 (ˆ artire ˆ ımp˘ ¸ ıntreag˘). bk ) = cmmdc(a. b) = cmmdc(a mod b. b) >= cmmdc(a. Pentru 1134 ¸i 308. b2 = 98. Astfel. b) = gcd(a. Fie ak ¸i bk valorile lui a ¸i b dup˘ k iteratii ale buclei din algoritm.96 Pentru a = 1134 ¸i s a0 = 1134. . exist˘ x ¸i y (unul negativ) astfel a a s ˆ at x ∗ a + y ∗ b = cmmdc(a. a1 = 308. obtinem: s ¸ a0 = 1134. deci a − x ∗ b = c3 ∗ d ¸i b = c2 ∗ d. pentru c˘ la sfˆr¸it. Demonstratie: Pentru ˆ ¸ ınceput ar˘t˘m c˘ cmmdc(a − x ∗ b. b1 = 210. Atunci d a s s divide a pentru c˘ a = (a − x ∗ b) + x ∗ b = (c3 + x ∗ c2 ) ∗ d. d3 = 7. a3 = 98. a3 = 98.2 Algoritmul lui Euclid extins Pentru orice dou˘ numere intregi pozitive. b). b0 = 308. b3 = 14. b3 = 14. obtinem t a ¸ ¸ xk−1 = yk . a1 = 308. De aici rezult˘ c˘ a a a cmmdc(b. Lema 1 cmmdc(a − x ∗ b. b2 = 98. d2 = 2. ¸inˆnd cont de relatia xk−1 ∗ak−1 +yk−1 ∗bk−1 = cmmdc(a. b) = cmmdc(a. cˆnd bk divide a s a a as a ak . putem calcula xk−1 ¸i yk−1 . d1 = 1. a s a Presupunem c˘ d divide a − x ∗ b ¸i b. s Presupunˆnd c˘ xk ¸i yk sunt cunoscute.

length-1.j. + a1 X + a0 ¸i b(X) = bn X n + . x1 = −2. a a s 7. else for(i=minmn+1. } } .i<=k.3. Desigur relatia 3 ∗ 1134 − 11 ∗ 308 = 14 este corect˘. Deci as s a(X) = am X m + . if(m<n) {k=n. if(i==k) return s. y2 = 0−1∗2 = −2. y1 = −2 − 3 ∗ 3 = −11.i++) s[i]=b[i]. m=a.7.minmn. while((s[i]==0)&&(i>=1)) i--.n. OPERATII CU POLINOAME ¸ 97 ¸i de asemenea.1 Adunarea a dou˘ polinoame a Este asem˘n˘toare cu adunarea numerelor mari. return ss. ¸ a ¸ a S˘ observ˘m c˘ (3 + k ∗ 308) ∗ 1134 − (11 + k ∗ 1134) ∗ 308 = 14.. valorile pentru xk ¸i yk : s s x3 = 0. minmn=m. Solutia nu este unic˘.j++) ss[j]=s[j].j<=i.i++) s[i]=a[i]+b[i]. x2 = 1. for(i=0. y1 = 1+2∗1 = 3. for(j=0.3 Operatii cu polinoame ¸ Toate operatiile cu polinoame obi¸nuite se fac utilizˆnd ¸iruri de numere ¸ s a s care reprezint˘ coeficientii polinomului.i++) s[i]=a[i]. y3 = 1. int[] s... x0 = 3. else { int[] ss=new int[i+1].. + b1 X + b0 .} else {k=m. Not˘m cu a si b vectorii coeficientilor a ¸ a ¸ polinoamelor cu care se opereaz˘ ¸i cu m ¸i n gradele lor.length-1. pentru orice k.i<=minmn.k.} s=new int[k+1]. n=b.3. minmn=n. int[] b) { int m.i. s 7. a a static int[] sumap(int[] a.i<=k. if(minmn<m) for(i=minmn+1. a a a ceea ce arat˘ c˘ valorile calculate pentru x = x0 ¸i y = y0 nu sunt unice. i=k.

3...i.val. return val. 0 ≤ i ≤ m ¸i 0 ≤ j ≤ n. n=b.((an · x + an−1 ) · x + an−2 ) · x + . s static int[] prodp(int[] a. p=new int[m+n+1].. return p.j++) p[i+j]+=a[i]*b[j]..3.j<=n.3. + b1 X + b0 derivata de ordinul 1 a polinomului a(X) = am X m + am−1 X m−1 + . m=a.i--) val=val*x+a[i]. } 7. .length-1. gradul polinomului produs p = a·b este m+n iar coeficientul pk este suma tuturor produselor de forma ai · bj unde i + j = k.i>=0.i. int[] b) { int m.i<=m.. val=a[m]. m=a. } 7.4 Fie Calculul derivatelor unui polinom b(X) = bn X n + bn−1 X n−1 + .length-1....length-1.98 CAPITOLUL 7. int[] p. int x) { int m.j.i++) for(j=0. + a1 ) · x + a0 static int valp(int[] a. for(i=0. for(i=m-1.n.2 ˆ Inmultirea a dou˘ polinoame ¸ a Evident. ALGORITMI ELEMENTARI 7. + a1 X + a0 .3 Calculul valorii unui polinom Valoarea unui polinom se calculeaz˘ eficient cu schema lui Horner: a a(x) = (.

for(i=0. return b.i<=n.. Dac˘ vrem s˘ calcul˘m derivata de ordinul k ≥ 0 a polinomului a..i++) b[i]=a[i]. b=new int[n+1]. b=new int[m+1]. OPERATII CU POLINOAME ¸ Dar a′ (X) = m · am · X m−1 + (m − 1) · am−1 · X m−2 + .7. n=m-1. for(i=0. atunci a a a static int[] derivpk(int[] a. } 99 Pentru calculul valorii v = a′ (x) a derivatei polinomului a ˆ x este suficient ın apelul v=valp(derivp(a).i<=n.int k) { int i. static int[] derivp(int[] a) { int m.i.. int[] b. for(i=1.k). + 2 · a2 · X + a1 . ..x).i++) b[i]=(i+1)*a[i+1].n.length-1.length-1.i++) b=derivp(b). return b.i<=k.3. int[] b. Rezult˘ c˘ a a n=m−1 ¸i s bi = (i + 1) · ai+1 pentru 0 ≤ i ≤ n.x). } Pentru calculul valorii v = a(k) (x) a derivatei de ordinul k a polinomului a ˆ x este suficient apelul ın v=valp(derivpk(a. m=a. m=a.

for(i=0.i++) if(!apartine(b. break. int x) { int i. x ∈ B} / Not˘m card A = m. for(i=0.1 Apartenenta la multime ¸ ¸ Testul de apartenent˘ a unui element x la o multime A.i++) cc[i]=c[i].i<m. if(j==m) return c. ¸ ¸ 7.4.i++) if(a[i]==x) {ap=true. boolean ap=false. for(i=0. } 7. j=0. return cc.i<j. } } . ALGORITMI ELEMENTARI 7. int[] b) { int i. int[] c=new int[m]. a static int[] diferenta(int[] a.n=a.a[i]) c[j++]=a[i].} return ap.100 CAPITOLUL 7.4 Operatii cu multimi ¸ ¸ O multime A se poate memora ˆ ¸ ıntr-un vector a. else { int[] cc=new int[j].i<n.2 Diferenta a dou˘ multimi ¸ a ¸ Diferenta a dou˘ multimi este dat˘ de multimea ¸ a ¸ a ¸ C = A − B = {x|x ∈ A. este prezentat ˆ ¸a ın algoritmul urm˘tor: a static boolean apartine(int[] a. Folosind vectorii putem descrie operatiile cu multimi. ale c˘rui elemente sunt a distincte.length.4. m=a.length.

i++) c[i]=a[i].i<m.4.a[i]) c[j++]=a[i]. j.i<n. return cc. ın s static int[] reuniune(int[] a. else { int[] cc=new int[j].i++) if(!apartine(a.i<j.3 Reuniunea ¸i intersectia a dou˘ multimi s ¸ a ¸ Reuniunea a dou˘ multimi este multimea: a ¸ C = A ∪ B = A ∪ (B − A). Introducem ˆ C toate elementele lui A ¸i apoi elementele lui B − A. for(i=0. j=m. int[] b) { int i.4 Produsul cartezian a dou˘ multimi a ¸ .i<j.7.i++) cc[i]=c[i]. int[] c=new int[m]. for(i=0. int[] c=new int[m+n]. for(i=0.length. if(j==m) return c.b[i]) c[j++]=b[i]. } } Intersectia a dou˘ multimi este multimea: ¸ a ¸ C = A ∩ B = {x|x ∈ A ¸i x ∈ B} s static int[] reuniune(int[] a. if(j==m+n) return c.length. int[] b) { int i.length. for(i=0.4. for(i=0. return cc. m=a.4. j=0. m=a. n=b. } } 7.i<m. j. OPERATII CU MULTIMI ¸ ¸ 101 7.i++) if(apartine(b. else { int[] cc=new int[j].i++) cc[i]=c[i].

4. } De exemplu. 3}. este identic˘ cu ¸ a generarea submultimilor multimii de indici {1.j<n. 2..length.. ¸inem cont c˘ trecerea de la un ordin la ın t a urm˘torul se face cˆnd se obtine suma egal˘ cu 2. ¸ ¸ O submultime se poate memora sub forma unui vector cu n componente.5 Generarea submultimilor unei multimi ¸ ¸ Generarea submultimilor unei multimi A = {a1 . 2.. j. s Generarea tuturor submultimilor ˆ ¸ ınseamn˘ generarea tuturor combinatiilor a ¸ de 0 ¸i 1 care pot fi retinute de vectorul caracteristic V . an }. 4} ¸i B = {1. matricea C este s linia 0 linia 1 0 1 1 1 1 2 2 1 3 3 2 1 4 2 2 5 2 3 6 3 1 7 3 2 8 3 3 9 4 1 10 4 2 11 4 3 7. m=a. adic˘ 1 + 1 = (10)2 .length. int[] b) { int i.i++) for(j=0. k=0... . int[][] c=new int[2][m*n]. a a ¸ a a pozitia ¸ 1 2 3 4 De exemplu. 3. pentru A = {1. } return c. vom folosi un vector v valoarea · · · · .102 CAPITOLUL 7. c[1][k]=b[j]. 2. a2 . n=b. k. adic˘ a tuturor numerelor s ¸ a ˆ baza 2 care se pot reprezenta folosind n cifre. ¸ unde fiecare component˘ poate avea valori 0 sau 1. static int[][] prodc(int[] a. . Fiecare coloan˘ a matricei contine cˆte un element al produsului a ¸ a cartezian. k++. O astfel de reprezentare a ¸ ¸ s ın se nume¸te reprezentare prin vector caracteristic.j++) { c[0][k]=a[i]. ın Pentru a genera adunarea ˆ binar. pentru n = 4.. ALGORITMI ELEMENTARI Produs cartezian a doua multimi este multimea: A × B = {(x. Componenta i are valoarea 1 a dac˘ elementul ai apartine submultimii ¸i 0 ˆ caz contrar. y)|x ∈ A ¸i y ∈ B} s Putem stoca produsul cartezian sub forma unei matrice C cu dou˘ linii ¸i a s m × n coloane.i<m. n}. for(i=0.

nc=1. ¸i. int[][] c. ¸i trecem la ordinul urm˘tor s a 0 ¸i a¸a mai departe s s · pˆn˘ cˆnd a a a 1 Aceste rezultate se pot retine ˆ ¸ ıntr-o matrice cu n linii ¸i 2n coloane. for(i=1. j++.7.i++) v[i]=0.i++) c[j][i-1]=v[i]. coloana 5 ¸ s reprezint˘ submultimea {a2 . while(v[i]>1) { v[i]=v[i]-2.i<=n.i++) nc*=2. a4 } iar coloana 7 reprezint˘ submultimea {a2 . ¸i trecem la ordinul urm˘tor s a 0 care nu este permis. ¸i trecem la ordinul urm˘tor s a 0 ¸i adun˘m 1 s a 1 ¸i adun˘m 1 s a 2 care nu este permis. while(j<nc) { v[n]=v[n]+1. i=n. } .i<=n.i<=n. j. i--. } for(i=1. a3 . de exemplu. j=0. s a1 a2 a3 a4 0 0 0 0 0 1 0 0 0 1 2 0 0 1 0 3 0 0 1 1 4 0 1 0 0 5 0 1 0 1 6 0 1 1 0 7 0 1 1 1 8 1 0 0 0 9 1 0 0 1 A 1 0 1 0 B 1 0 1 1 C 1 1 0 0 D 1 1 0 1 E 1 1 1 0 F 1 1 1 1 0 1 2 3 Ultima coloan˘ contine num˘rul liniei din matrice. a4 }. a a static int[][] submultimi(int n) { int i.4. v[i-1]=v[i-1]+1. c=new int[n][nc]. Coloana 0 reprezint˘ a ¸ a a multimea vid˘. } return c. OPERATII CU MULTIMI ¸ ¸ initial ¸ 0 0 0 0 0 0 0 0 1 1 · 0 0 0 1 1 1 2 0 · 1 0 ¸i adun˘m 1 s a 1 ¸i adun˘m 1 s a 103 obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem · ¸ obtinem 1 ¸ 2 care nu este permis. for(i=1. int[] v=new int[n+1]. coloana F reprezint˘ ˆ ¸ a a ıntreaga multime.

104 CAPITOLUL 7. t=0. ¸ 32 sau 64 biti. } if(z[nz-1]!=0) return z. else nz=ny+1.5 Operatii cu numere ˆ ¸ ıntregi mari Operatiile aritmetice sunt definite numai pentru numere reprezentate pe 16.5.i++) { s=t. int ny=y. operatiile trebuie implementate de ¸ a ¸ utilizator. return zz. for (i=0.s. if(i<=ny-1) s=s+y[i]. int[] z=new int[nz].1 Adunarea ¸i sc˘derea s a Adunarea ¸i sc˘derea sunt directe: aplicˆnd metodele din ¸coala elementar˘.i<=nz-1. s a a s a static int[] suma(int[] x.length.i. } } . Dac˘ numerele sunt mai mari. if(nx>ny) nz=nx+1. int t. else { int[] zz=new int[nz-1]. int nz. int[] y) { int nx=x. ALGORITMI ELEMENTARI 7.length.i<=nz-2. if(i<=nx-1) s=s+x[i]. 7. t=s/10. z[i]=s%10. for (i=0.i++) zz[i]=z[i].

7. int ny=y.length.length. for(j=0.i++) s=s+a[i][j].s. } a[j][i+j]=t.j<=nz-1.j.j++) { s=0. int i. int[] [] a=new int[ny][nx+ny].5. return zz.5. } if(z[nz-1]!=0) return z. for(j=0. } t=0. OPERATII CU NUMERE ˆ ¸ INTREGI MARI 105 7.int[]y) { int nx=x. int nz=nx+ny. for(i=0.j++) zz[j]=z[j]. t=s/10.j<=nz-2. for(i=0. for(j=0.j<=ny-1. int t. t=s/10.2 Inmultirea ¸i ˆ artirea ¸ s ımp˘ Metoda ˆ a¸at˘ ˆ ¸oal˘ este corect˘. s=s+t.j++) { t=0. int[] z=new int[nz].i<=ny-1. ınv˘t a ın s a a static int[] produs(int[]x. a[j][i+j]=s%10.i<=nx-1. } } . else { int[] zz=new int [nz-1]. z[j]=s%10.i++) { s=t+y[j]*x[i].

pentru ˆ a ınceput. n .bi =1 2i . a Acum. Presupunem c˘ n are expresia binar˘ a a a a (bk . Cum facem acest lucru? Este evident a a a c˘ urm˘toarea secvent˘ functioneaz˘: a a ¸a ¸ a for (p = 1. n / 2). ALGORITMI ELEMENTARI 7. if (n & 1) /* n este impar */ return x * exponent_2(x. } .. n = n / 2) { if (n & 1) /* n este impar */ z *= c. s˘ consider˘m cazul general. z. Totu¸i. int n) { if (n == 0) return 1. i = 1. } int exponent_2(int x. acest algoritm are complexitatea O(n).. k xn = i=0. bk−1 . n != 0. . z = 1. putem s˘ facem acest lucru mai repede! s a Presupunˆnd. c *= c. urm˘torul algoritm este corect: a a for (p = x. i int exponent_1(int x. i < n.3 Puterea Presupunem c˘ vrem s˘ calcul˘m xn . int n) { int c.. i < n. i++) p *= x. Aici num˘rul de treceri prin ciclu este egal cu k = log2 n. i *= 2) p *= p.5. return exponent_2(x. b0 ).bi =1 x2 . Atunci putem scrie k n= i=0.106 CAPITOLUL 7. i = 0.1). Presupunˆnd c˘ toate ˆ a a ınmultirile sunt efectuate ˆ ¸ ıntr-o unitate de timp. n / 2) * exponent_2(x. b1 . } return z. c˘ n = 2k . for (c = x. Deci.

s a ¸ Determinantul det(A) se define¸te recursiv astfel: s n−1 det(A) = i=0 (−1)i+j ∗ ai. k++) C[i][j] += A[i][k] * B[k][j].j este submatricea obtinut˘ prin eliminarea ¸ a liniei i ¸i a coloanei j. k < n.j ∗ det(Ai. OPERATII CU MATRICE ¸ int exponent_3(int x.7. int** B. i < n. } 107 7. i++) for (j = 0. n .6. int** C) { for (i = 0.6 Operatii cu matrice ¸ 7. for (k = 0. int n) { int y. } } 7.6. s . y = exponent_3(x. return y * y.2 Inversa unei matrice O posibilitate este cea din ¸coal˘. j++) { C[i][j] = 0. unde ai. if (n & 1) /* n este impar */ return x * exponent_3(x.6. n / 2).j ). if (n == 0) return 1.1). Aceasta presupune calculul unor determinanti.j este element al matricei iar Ai. j < n.1 ˆ Inmultirea ¸ O functie scris˘ ˆ C/C++: ¸ a ın void matrix_product(int** A.

i++) { for (int j = 0.1]. } } Folosind determinanti. for (int i = 0. j++) for (int k = 1. k++) b[j][k . int det = 0. . sign *= -1. k++) b[j . Presupunem c˘ A este inversabil˘ ¸i fie B = (bi. Atunci A−1 = B T .108 CAPITOLUL 7. unde B T este transpusa matricei B. j < i.j ) matricea definit˘ prin a as a bi. j < n.1][k .1] = a[j][k].j )/ det(A). k < n.1.1] = a[j][k]. for (int j = i + 1. int[][] b = new int[n . det += sign * a[i][0] * determinant(n . int sign = 1. int[][] a) { if (n == 1) return a[0][0].j = (−1)i+j ∗ det(Ai.1][n . inversa matricei se poate calcula folosind regula lui ¸ Cramer. j++) for (int k = 1. i < n. b). k < n. ALGORITMI ELEMENTARI int determinant(int n.

. n) este ın ¸ egal cu: n |A|− i=1 |Ai |+ 1≤i<j≤n |Ai ∩Aj |− 1≤i<j<k≤n |Ai ∩Aj ∩Ak |+.+(−1)n |A1 ∩A2 ∩...1..∩An | Se pot demonstra prin inductie matematic˘ urm˘toarele formule: ¸ a a n n n | i=1 n Ai | = i=1 n |Ai |− 1≤i<j≤n |Ai ∩Aj |+ 1≤i<j<k≤n |Ai ∩Aj ∩Aj |−.. An submultimi ale sale. Atunci num˘rul ¸ as ¸ a elementelor lui A care nu apar ˆ nici una din submultimile Ai (i = 1.1 Principiul includerii ¸i al excluderii ¸i aplicatii s s ¸ 8. Not˘m prin |A| cardinalul multimii A. 2.Capitolul 8 Algoritmi combinatoriali 8........+(−1)n+1 | i=1 n Ai | | i=1 Ai | = i=1 |Ai |− 1≤i<j≤n |Ai ∪Aj |+ 1≤i<j<k≤n |Ai ∪Aj ∪Aj |−. Se s a ¸ a ¸ deduce u¸or c˘: s a |A ∪ B| = |A| + |B| − |A ∩ B|..1 Principiul includerii ¸i al excluderii s Fie A ¸i B dou˘ multimi finite. A2 . Fie A o multime finit˘ ¸i A1 .+(−1)n+1 | i=1 Ai | 109 . ...

.m<=10. ∩ An = ∅ ¸i pentru c˘ nu poate exista o functie care s a ¸ s˘ nu ia nici o valoare. x2 . yi ∈ f (X)} (multimea functiilor pentru care yi nu ın s / ¸ ¸ este imaginea nici unui element din X). ∩ An | etc. ¸ s Fie Sm. ALGORITMI COMBINATORIALI 8. s.n num˘rul functiilor surjective f : X −→ Y . Atunci n Sm.110 CAPITOLUL 8.. .. a ¸ Fie A = {f |f : X −→ Y } (multimea tuturor functiilor definite pe X cu valori ¸ ¸ ˆ Y ) ¸i Ai = {f |f : X −→ Y... deci Sm.<ik ≤n j=1 | k Aij | = Cn (n − k)m Rezult˘: a 1 2 n−1 Sm. |Ai | = (n − 1)m . + (−1)n |A1 ∩ A2 ∩ . s a Din Y putem elimina k elemente ˆ Cn moduri. + (−1)n−1 Cn Observatii: ¸ 1. ultimul termen lipse¸te.. obtinem s ¸ n Sm.m++) { s=0.n = |A| − i=1 |Ai | + 1≤i<j≤n |Ai ∩ Aj | − .. yn }.. Deoarece A1 ∩ A2 ∩ ..n = n! ¸i se obtine o formul˘ interesant˘: s ¸ a a n−1 n! = k=0 k (−1)k Cn (n − k)n class Surjectii { public static void main (String[]args) { int m. a s 2.1. y2 .. Se poate observa u¸or c˘ |A| = nm .. ..2 Num˘rul functiilor surjective a ¸ Se dau multimile X = {x1 ..n = |A| − | i=1 Ai | Folosind principiul includerii ¸i al excluderii.n = nm − Cn (n − 1)m + Cn (n − 2)m + . for(m=2... deci ın k k 1≤i1 <i2 <. |Ai ∩ Aj | = (n − 2)m . k. n=5. Dac˘ n = m atunci num˘rul functiilor surjective este egal cu cel al functiilor a a ¸ ¸ injective. . xm } ¸i Y = {y1 ..

x[i]). } rez=1.} while (i!=0) { c=d/i. } static int comb (int n.k)*putere(n-k.k)*putere(-1. j. for(k=1. PRINCIPIUL INCLUDERII SI AL EXCLUDERII SI APLICATII ¸ ¸ ¸ for(k=0.8. for(i=1.i. } return d.i++) { d=cmmdc(y[j]. if(y[j]==1) break. } static int putere (int a.out.j<=k. int[] y=new int[k+1]. System. for(j=1.1.i<=k. int n) { int rez=1. i=r. if (a>b) {d=a.j++) y[j]=j. return rez. return rez.m). for(i=1. d.k++) s=s+comb(n. for(j=2. y[j]=y[j]/d.println(m+" : "+s). r=d%i.i=b.i++) x[i]=n-k+i.k<=n-1. } } 111 .} else{d=b.i<=k.j<=k. } System.r.int b) { int d.k<=n.println("GATA"). int k) { int rez.c. x[i]=x[i]/d.out.i=a.i++) rez=rez*x[i]. d=i.j++) for(i=1. k. } static int cmmdc (int a.k++) rez=rez*a. int[] x=new int[k+1].i<=k. i.

aleas˘ aleator.. ∪ An | = Dar i=1 |Ai | − 1≤i<j≤n |Ai ∩ Aj | + ....... a a a a Se cere s˘ se determine num˘rul D(n) al permut˘rilor f˘r˘ puncte fixe. ik pot fi alese ˆ Cn ¸ ın k moduri. num˘rul permut˘rilor care admit cel putin un punct fix este egal s a a ¸ cu: n n |A1 ∪ A2 ∪ . ..... ∩ Aik | = (n − k)! ın ¸ deoarece o permutare din multimea Ai1 ∩ Ai2 ∩ .. i2 . a a Se poate demonstra u¸or c˘: s a n→∞ lim D(n + 1) D(n + 1) = (n + 1)D(n) + (−1)n+1 = n (D(n) + D(n − 1)) .3678.. ALGORITMI COMBINATORIALI 8. .. ∪ An | = 1 2 n = n! − Cn (n − 1)! + Cn (n − 2)! − +.. class PermutariFixe { public static void main(String [] args) { . Folosind principiul includerii ın ¸i al excluderii.. + (−1)n−1 Cn . n! deci.. este de e−1 ≈ 0. deci 1 2 n |A1 ∪ A2 ∪ . a s˘ nu aib˘ puncte fixe....3 Num˘rul permut˘rilor f˘r˘ puncte fixe a a a a Fie X = {1. ik . ∪ An | = Cn (n − 1)! − Cn (n − 2)! + ..1. n}. a ¸ spunem c˘ num˘rul i este un punct fix al permut˘rii p. probabilitatea ca o permutare a n elemente. dac˘ p(i) = i (1 ≤ i ≤ n). + (−1)n Cn 1 1 (−1)n 1 ... 2. i2 . ∩ Aik are puncte fixe ˆ pozitiile ¸ i1 . . S˘ not˘m cu Ai multimea celor (n−1)! permut˘ri care admit un punct ¸ a a ¸ a fix ˆ i (dar nu obligatoriu numai acest punct fix!). Dac˘ p este o permutare a elementelor multimii X. + 1! 2! 3! n! De aici rezult˘ c˘ a a D(n) = e−1 . Cele k pozitii i1 ... Atunci D(n) = n! − |A1 ∪ A2 ∪ .112 CAPITOLUL 8. ale a a a aa multimii X. + (−1)n−1 | i=1 Ai |. pentru n mare. = n! 1 − + − + .. celelalte pozitii continˆnd o permutare a celor n − k elemente r˘mase ¸ ¸ a a (care pot avea sau nu puncte fixe!).. |Ai1 ∩ Ai2 ∩ .

¸ a Cu ajutorul functiilor. care trebuie ˆ artite ˆ n multimi..out. a ˆ forma cea mai simpl˘ acest principiu se enunt˘ astfel: In a ¸a Dac˘ n obiecte trebuie ˆ artite ˆ mai putin de n multimi. atunci. an de numere ˆ a s ıntregi. r n =n r Dar multimea B are r elemente.k--) { xn=-k*xv. a2 .k. atunci a a a a |f −1 (b)| < n .2 Principiul cutiei lui Dirichlet ¸i aplicatii s ¸ Acest principiu a fost formulat prima dat˘ de Dirichle (1805-1859). + aj este un multiplu de n.xn. Dac˘ aceasta nu ar fi adev˘rat˘.2.println("f("+n+") = "+s). // n=22 maxim pe long ! if((n&1)==1) xv=-1L.8. s r Demonstr˘m ultima inegalitate. exist˘ b ∈ B cu proprietatea c˘ |f −1 (b)| ≥ 2. s=xv. ∀b ∈ B..k>=3. . aj a ¸a cu proprietatea c˘ ai + ai+1 + . else xv=1L. } } 113 8. a .. ˆ cazul oric˘rei ˆ artiri. ¸ 8.xv. ai+1 . xv=xn. .. } System.. principiul cutiei se poate formula astfel: ¸ Fie A ¸i B dou˘ multimi finite cu |A| > |B| ¸i functia f : A −→ B. principiul lui Dirichlet se poate enunta astfel: ¸ Fiind date m obiecte. Dac˘ not˘m a a a a |A| = n ¸i |B| = r atunci |f −1 (b)| ≥ n .s=0L. atunci a ımp˘ ¸ ın ¸ ¸ exist˘ cel putin o multime ˆ care vor fi cel putin dou˘ obiecte.1 Problema subsecventei ¸ Se d˘ un ¸ir finit a1 . s+=xn... ¸ ¸ ¸ Pentru k = 1 se obtine formularea anterioar˘. ¸i un num˘r ımp˘ ¸ ın ¸ s a natural k astfel ˆ at m > kn. deci ¸ n= b∈B |f −1 (b)| < r · ceea ce este o contradictie. va ıncˆ ın a ımp˘ ¸ exista cel putin o multime cu cel putin k + 1 obiecte. for(k=n..2. s a ¸ s ¸ Atunci. a ¸ ¸ ın ¸ a Mai general. Exist˘ o subsecvent˘ ai . PRINCIPIUL CUTIEI LUI DIRICHLET SI APLICATII ¸ ¸ long n=10.

. < im+1 ≤ mn + 1. 8. amn+1 ... ın Presupunem c˘ afirmatia problemei nu este adev˘rat˘.114 CAPITOLUL 8. Atunci subsecventa c˘utat˘ ¸ se obtine luˆnd i = k1 + 1 ¸i j = k2 .. ceea ce este o contraditie.. < aim+1 unde 1 ≤ i1 < i2 < . sau un sub¸ir descresc˘tor de n + 1 elemente s a aj1 < aj2 < .. Pentru c˘ ¸ a ın ¸ a avem n sume partiale ¸i numai n − 1 resturi. . exist˘ un ai ¸i un aj pentru care perechile s a s de numere (xi . unde k1 < k2 ) cu acela¸i rest. Deoarece ¸irul are mn + 1 termeni. s a a ¸ Deci exist˘ un sub¸ir cresc˘tor cu m + 1 termeni sau un sub¸ir descresc˘tor a s a s a cu n + 1 termeni. adic˘: pentru toate a ¸ a a a numerele naturale xi ¸i yi avem 1 ≤ xi ≤ m ¸i 1 ≤ yi ≤ n. < jn+1 ≤ mn + 1. yi ) a s ıi unde xi este lungimea maxim˘ a sub¸irurilor cresc˘toare care ˆ a s a ıncep cu ai iar yi este lungimea maxim˘ a sub¸irurilor descresc˘toare care ˆ a s a ıncep ˆ ai . ..2.. sn = a1 + a2 + .. yj ) sunt identice (xi = xj . n − 1}. ¸ a s 8. yi ) ¸i (xj .. s Fiec˘rui element al ¸irului ˆ asociem perechea de numere naturale (xi .. atunci resturile ˆ artirii a a ¸ a ımp˘ ¸ acestor sume partiale la n nu pot fi decˆt ˆ multimea {1..2 Problema sub¸irurilor strict monotone s Se d˘ ¸irul de numere reale distincte a1 ... ˆ ¸ s ınseamn˘ c˘ exist˘ cel putin dou˘ a a a ¸ a s ¸ a a s sume partiale (sk1 ¸i sk2 .. Atunci perechile de s s numere (xi . yi = yj ). + an . a2 . sau ambele tipuri de sub¸iruri.. ALGORITMI COMBINATORIALI S˘ consider˘m urm˘toarele sume: a a a s1 s2 = a1 .3 Numere remarcabile . Dac˘ exist˘ un k astfel sk este multiplu de n atunci i = 1 ¸i j = k... a a s Dac˘ nici o sum˘ partial˘ sk nu este multiplu de n. Atunci. dar acest lucru este s imposibil (cei doi termeni ai ¸i aj ar trebui s˘ coincid˘).. < ajn+1 unde 1 ≤ j1 < j2 < . 2. = a1 + a2 . ¸irul contine as s ¸ un sub¸ir cresc˘tor de m + 1 elemente: s a ai1 < ai2 < . yi ) pot avea mn elemente distincte.

2) (8.. In a a a .11) F1 F2 + F2 F3 + . 610. 89. 233.8.3.3.. 13.3.7) (8.. 987.3. a a Folosind aceast˘ descompunere. . 34.3) (8. + Fn F1 F2 + F2 F3 + . 3.. 8. ca de exemplu: at 1 1 1 0 n = = Fn+1 Fn (−1)n Fn Fn−1 (8. + F2n−1 F2n = F2n+1 − 1 = F2n = Fn Fn+1 2 = F2n 2 = F2n+1 − 1 (8. + F2n F2n+1 Teorema 2 Orice num˘r natural n se poate descompune ˆ a ıntr-o sum˘ de numere a Fibonacci. 377.. 5.3. 144. 2.3.. Fn = Fn−1 + Fn−2 pentru n ≥ 2.1 Numerele lui Fibonacci Numerele lui Fibonacci se pot defini recursiv prin: F0 = 0. Se poate ar˘ta c˘ a a Fn = 2n 1 √ 5 1+ √ 5 n (8. NUMERE REMARCABILE 115 8. 21.3. 1. Primele numere Fibonacci sunt: 0.. atunci aceast˘ descompunere este unic˘ abstractie a a ¸ f˘cˆnd de ordinea termenilor.3.9) (8.3. De exemplu a ın 19 = 1 · 13 + 0 · 8 + 1 · 5 + 0 · 3 + 0 · 2 + 1 · 1 = (101001)F ˆ aceast˘ scriere nu pot exista dou˘ cifre 1 al˘turate.10) (8.1) − 1− √ 5 n .. + F2n F1 + F3 + . Numerele lui Fibonacci satisfac multe identit˘¸i interesante. F1 = 1. 55.3.3.. Dac˘ nu se folosesc ˆ descompunere numerele F0 ¸i F1 ¸i nici dou˘ a ın s s a numere Fibonacci consecutive..6) 2 Fn+1 Fn−1 − Fn Fn+m Fnk = Fm Fn+1 + Fm−1 Fn = multiplu de Fk ¸i s F2 + F4 + . 1597. 1.5) (8.3..8) (8. numerele naturale pot fi reprezentate asem˘n˘tor a a a reprezent˘rii ˆ baza 2..3.4) (8. + F2n−1 2 2 2 F1 + F2 + .

print("x = "). nrt++.k<=n.k<=n. public static void main (String[]args) throws IOException { int iy. System. k.k<=n.*. x=Long. cel mult! BufferedReader br=new BufferedReader(new InputStreamReader(System. return k-1.2 Numerele lui Catalan Numerele Cn = 1 Cn n + 1 2n . for(k=3. } } static int maxFibo(long nr) { int k.out.MAX_VALUE+" = Long. f[0]=0.out. f[2]=1.3. x=x-y. nrt=0.println(k+" : "+f[k]). for(k=0.k++) System. class DescFibo { static int n=92.k++) f[k]=f[k-1]+f[k-2]. while(x>0) { iy=maxFibo(x). System.out.parseLong(br.116 CAPITOLUL 8. System. for(k=1.out.k++) if (f[k]>nr) break.readLine()).out. static long[] f=new long[n+1].println(" "+Long. } } 8.y. System.io. f[1]=1.in)). // x=1234567890123456789L. long x.MAX_VALUE").println(nrt+" : "+x+" f["+iy+"] = "+y). ALGORITMI COMBINATORIALI import java.println(" x = "+x). y=f[iy].

length. n) formate din segmente orizontale ¸i a s s verticale. afisv(x). + xi ≥ 0 pentru orice i = 1. num˘rul a ın ¸ a a a segmentelor care unesc 2n puncte ˆ plan f˘r˘ s˘ se intersecteze..i++) { a[j][i+j]=y[j]*x[i]+t. Ele apar ˆ multe probleme.j++) { t=0. num˘rul modurilor de a a triangulariza un poligon. int[][]a=new int[m][n+m].j<m. t.. NUMERE REMARCABILE 117 se numesc numerele lui Catalan. s Numerele lui Catalan verific˘ ¸i relatia: as ¸ Cn+1 = 4n + 2 Cn n+2 O implementare cu numere mari este: class Catalan { public static void main (String[]args) { int n. n=x.length... int[]z=new int[m+n].8. j.. for(j=0. num˘rul drumurilor sub a a a a diagonal˘ care unesc punctele (0. 0) ¸i (n. x2 . . ¸i multe altele. for(n=1.n<=10. pentru n ≥ 0 ¸i C0 = 1. + Cn C0 .. 1} ¸i x1 + x2 + . num˘rul secventelor cu n biti ˆ care num˘rul cifrelor 1 nu dep˘¸e¸te a ¸ ¸ ın a as s num˘rul cifrelor 0 ˆ nici o pozitie plecˆnd de la stˆnga spre dreapta. 2n − 1.n++) { x=Catalan(n). m=y. for(i=0. } } static int[] inm(int[]x. x2n ) ˆ care xi ∈ {−1.. ca de exemplu: ın num˘rul arborilor binari.. .print(n+" : ").i<n.out.3. s Numerele lui Catalan sunt solutie a urm˘toarei ecuatii de recurent˘: ¸ a ¸ ¸a Cn+1 = C0 Cn + C1 Cn−1 + ...int[]y) { int i.. int[] x. System. t=a[j][i+j]/10. num˘rul ¸irurilor ın aa a a s (x1 . + x2n = 0 cu proprietatea ın s x1 + x2 + . . 2. num˘rul de parantez˘ri corecte..

. while(nr!=0) { nc++. else { int[]zz=new int[m+n-1]. System. } static int[] nrv(int nr) { int nrrez=nr. ALGORITMI COMBINATORIALI a[j][i+j]=a[j][i+j]%10.118 CAPITOLUL 8. } static int[] Catalan(int n) { int[] rez. while(nr!=0) { x[nc]=nr%10.print(" *** "+x. } return x. } a[j][i+j]=t.out.i--) System. return zz. } int[]x=new int [nc]. for(j=0. for(i=0. System. } } static void afisv(int[]x) { int i. z[j]=z[j]%10. nr=nr/10. nc++.length).j<m+n. for(i=0. } t=0.i<m. } if(z[m+n-1]!= 0) return z.print(x[i]).i++) z[j]=z[j]+a[i][j]. nr=nr/10. int nc=0.println().length-1.i++) zz[i]=z[i].i>=0.out. t=z[j]/10. nr=nrrez.j++) { z[j]=t.i<=m+n-2. for(i=x.out. nc=0.

8.4 Descompunerea ˆ factori primi ın 8. int[] x=new int[n+1].i=a. d.pαm m 2 1 Not˘m cu Ai multimea numerelor naturale mai mici ca n care sunt multipli de pi . |Ai ∩ Aj ∩ Ak | = .i++) { d=cmmdc(y[j]. for(j=2. y[j]=y[j]/d. .. } rez=nrv(1). } static int cmmdc (int a. r=d%i. x[i]=x[i]/d.j<=n. } } 119 8.. a ¸ Atunci avem: n n n |Ai | = . DESCOMPUNEREA ˆ FACTORI PRIMI IN int i. for(i=2.j<=n. int[] y=new int[n+1].i<=n..1 Functia lui Euler ¸ Functia φ(n) a lui Euler ne d˘ num˘rul numerelor naturale mai mici ca n ¸i ¸ a a s prime cu n. j.4.4.r.i++) rez=inm(rez.j++) y[j]=j. return rez. i=r. for(i=2. } return d. for(j=2.int b) { int d. if (a>b) {d=a.j++) for(i=2.i<=n.c. pi pi pj pi pj pk .i++) x[i]=n+i. d=i.i<=n..i.} while (i!=0) { c=d/i.x[i]). |Ai ∩ Aj | = .i=b. if(y[j]==1) break.nrv(x[i])).} else{d=b. Num˘rul n poate fi descompus ˆ factori primi sub forma: a ın n = pα1 pα2 .

. nrrez=nr.k++) n*=fact[k]-1. fact=new long[nfd+1].MAX_VALUE=9. public static void main (String[]args) { long n=36L. } nr=nrrez. } static long[] factori(long nr) { long d. } if(gasit) {nfd++.. System. while((nr!=1)&(nr%d==0)) { nr=nr/d.println("f("+nrez+") = "+n).k<=m. int pfc=0. // afisv(fact).807. gasit=true. gasit=true. ALGORITMI COMBINATORIALI φ(n) = n − i=1 n + pi 1≤i<j≤m n − pi pj 1≤i<j<k≤m n n + . while(nr!=1) { while((nr!=1)&(nr%d==0)) { nr=nr/d.372.gasit=false.out. for(k=1.223..775.} d=3. // nr. // Long. long[] pfact=factori(n). int nfd=0.120 Rezult˘: a m CAPITOLUL 8..k<=m.k++) n/=fact[k].. // puterea factorului curent nfd=0.854. int k. // nr.pm care este tocmai dezvoltarea produsului φ(n) = n 1 − 1 p1 1− 1 p2 . 1 − 1 pm class Euler { static long[] fact.036. // afisv(pfact).. factori distincti boolean gasit=false. } if(gasit) {nfd++. for(k=1.m=fact. + (−1)m pi pj pk p1 p2 .gasit=false. long nrez=n.length-1. long[] pf=new long[nfd+1].} d=d+2. factori distincti .

. Atunci ın s sdiv(n) = d|n d= f1 (1+e1 ) k f −1 − 1 f2 2 − 1 · · . b) = 1. b2 ..8.} d=d+2. · (1 + ek )..out. } if(gasit) {fact[++nfd]=d... a1 .. pfc++. Divizorii lui a sunt: 1. } return pf.out.length.. while(nr!=1) { while((nr!=1)&(nr%d==0)) { nr=nr/d. f1 − 1 f2 − 1 fk − 1 (1+e ) (1+e ) Demonstratie: ¸ Fie n = ab cu a = b ¸i cmmdc(a.. · k . Divizorii lui b sunt: 1. }//descfact static void afisv(long[] a) { for(int i=1. b. gasit=true...} d=3.. DESCOMPUNEREA ˆ FACTORI PRIMI IN gasit=false. a2 . pfc++.gasit=false. System.3 Fie Suma divizorilor e e e n = f1 1 · f2 2 · .pfc=0. Atunci ın s a ndiv(n) = (1 + e1 ) · (1 + e2 ) · ..4.4. d=2.i++) System.2 Fie Num˘rul divizorilor a e e e n = f1 1 · f2 2 · . gasit=true. . unde ai este divizor al lui a iar bj este divizor al lui b..pfc=0. · fk k descompunerea lui n ˆ factori primi ¸i sdiv(n) suma divizorilor lui n. s d = ai bj . 8.gasit=false.4. while((nr!=1)&(nr%d==0)) { nr=nr/d..i<a.print(a[i]+" ").pf[nfd]=pfc. Pentru orice divizor d al lui n..pf[nfd]=pfc. } if(gasit) {fact[++nfd]=d. } } 121 8.println(). . · fk k descompunerea lui n ˆ factori primi ¸i ndiv(n) num˘rul divizorilor lui n. b1 . a.

. k) = P (n − k. at ..... · sdiv(fk k ) ¸i mai departe rezult˘ relatia dat˘ initial! s a ¸ a ¸ 8.5 Partitia numerelor ¸ 8. Demonstratie: ¸ Ultimul termen ak poate fi 1 sau ≥ 2.. Dar sdiv(ab) = d|ab d= i. + b. f˘r˘ a ¸ine cont de ordinea termenilor).....122 CAPITOLUL 8... Pentru ak = 1 avem P (n − 1. k) = P (n − 1. ≥ ak ≥ 1.. + a sdiv(b) = 1 + b1 + b2 + .j ai bj = ( i ai ) · ( j bj ) = sdiv(a) · sdiv(b) De aici rezult˘ c˘: a a e e e e e e sdiv(f1 1 · f2 2 · .. k) . · fk k ) = sdiv(f1 1 ) · sdiv(f2 2 ) · .. + a′ unde a′ ≥ a′ ≥ . ≥ a′ ≥ 1. k). + ak−1 ¸i a1 ≥ a2 ≥ . i) = 1 pentru (∀)i ≥ 1 ¸i s n = a1 + a2 + . ALGORITMI COMBINATORIALI Sumele divizorilor lui a ¸i b sunt: s sdiv(a) = 1 + a1 + a2 + .. a1 ≥ a2 ≥ .... + ak . considerˆnd dou˘ descompuneri ca fiind distincte a a a dac˘ difer˘ prin cel putin un termen (deci.... k) unde P (i. ≥ ak−1 ≥ 1 ıncˆ s Pentru ak ≥ 2 putem s˘ sc˘dem 1 din toti factorii descompunerii lui n ¸i a a ¸ s a obtinem n − k = a′ + a′ + . 1) + P (n − k. k − 1) posibilit˘¸i de alegere a factorilor a1 . 1) = P (i.1 Partitia lui n ˆ exact k termeni ¸ ın Fie P (n.. a2 . deci num˘rul ¸ 2 1 2 1 k k descompunerilor lui n cu ak ≥ 2 este P (n − k. a a ¸ aa t Atunci P (n.. + P (n − k. k − 1) + P (n − k. ak−1 astfel ˆ at n − 1 = a1 + a2 + . k) num˘rul modalit˘¸ilor de a descompune num˘rul natural n ca a at a sum˘ de exact k termeni nenuli.. 2) + . Obtinem relatia ¸ ¸ P (n.5.

obtinˆnd o partitie a lui n − k ˆ cel mult k termeni nenuli.6 Partitia multimilor ¸ ¸ . k) = P (n − 1. k) num˘rul modalit˘¸ilor de a descompune num˘rul natural n ca a at a sum˘ de cel mult k termeni nenuli. considerˆnd dou˘ descompuneri ca fiind disa a a tincte dac˘ difer˘ prin cel putin un termen (deci. k) = A(n. 1) = 1 = P (n − k. k − 2) . 1) + P (n − k. ¸ ın 8.. i) pentru orice j > i iar A(n. ¸ (1) Num˘rul partitiilor ˆ cel mult k factori este egal cu num˘rul partitiilor a ¸ ın a ¸ ˆ exact k factori plus num˘rul partitiilor ˆ cel mult k − 1 termeni.5. exist˘ o corespondent˘ bijectiv˘ ˆ a ¸a a ıntre partitiile lui n ˆ exact k termeni ¸i ¸ ın s partitiile lui n − k ˆ cel mult k factori. ¸ a ¸ ın Astfel.3 Partitii multiplicative ¸ Fie A(n. 3) P (n − k + 2. P (n − k + 3. 2) Prin reducere ¸i ¸inˆnd cont c˘ P (n − k + 1. k − 1) = P (n − 2. f˘r˘ a ¸ine cont de ordinea a a ¸ aa t termenilor). 2) = P (n − k + 1.8. Atunci A(n. 3) = P (n − k + 2. k − 2) + P (n − k. 8. k − 3) + P (n − k.. 1) obtinem s t a a ¸ relatia dat˘ la ˆ ¸ a ınceput. f˘r˘ a ¸ine cont de ordinea a a ¸ aa t termenilor). 8. k − 1) 123 P (n − 2. k) P (n − 1. PARTITIA MULTIMILOR ¸ ¸ care poate fi transformat˘ astfel: a P (n. 2) + P (n − k. ın a ¸ ın (2) Dat˘ fiind o partitie a lui n ˆ exact k termeni nenuli.2 Partitia lui n ˆ cel mult k termeni ¸ ın Fie A(n. k) num˘rul modalit˘¸ilor de a descompune num˘rul natural n ca a at a sum˘ de cel mult k termeni nenuli. n) reprezint˘ num˘rul a a partitiilor lui n. putem sc˘dea 1 a ¸ ın a din fiecare termeni. k − 1) + A(n − k.6. considerˆnd dou˘ descompuneri ca fiind disa a a tincte dac˘ difer˘ prin cel putin un termen (deci.5. k − 1) + P (n − k. k − 2) = P (n − 3. k) Evident A(i. j) = A(i.

S˘ se determine num˘rul arborilor binari cu n vˆrfuri. Prin desene b1 = 1. k − 1) + k · S(n − 1. ALGORITMI COMBINATORIALI Fie S(n. k) num˘rul modalit˘¸ilor de a partitiona o multime A cu n elemente a at ¸ ¸ ˆ k submultimi nevide.. s a a a a ne mai r˘mˆn n − 1 vˆrfuri care pot ap˘rea ˆ subarborele stˆng sau drept. deci k·S(n−1. ¸ ¸ aa t ¸ Atunci S(n. s ¸ ın ¸ Num˘rul partitiilor de tipul (2) este k·S(n−1.7 Probleme rezolvate 1. Prin conventie a a ¸ b0 = 1. k)! S˘ s ın a privim putin altfel lucrurile: consider˘m toate partitiile multimii {a1 . k) = S(n − 1.. k) (eliminˆnd elementul an din a ¸ a submultimea ˆ care se afl˘. a2 . a a ¸ Ai ∩ Aj = ∅ pentru i = j.124 CAPITOLUL 8. acea submultime r˘mˆne nevid˘!).. i) = 1 pentru (∀)i ≥ 1 ¸i s A = A1 ∪ A2 ∪ . considerˆnd dou˘ partitii ca fiind distincte dac˘ difer˘ ın ¸ a a ¸ a a prin cel putin o submultime (deci. k) unde S(i. 1) = S(i. ¸i: dac˘ fix˘m r˘d˘cina arborelui. ¸ ¸ as s s Num˘rul partitiilor de tipul (1) este S(n − 1. Demonstratie: ¸ Fie A = {a1 . num˘rul acestora este S(n − 1. a a a Rezolvare: Fie b(n) num˘rul arborilor binari cu n vˆrfuri.. ¸ Elementul an poate fi (1) ˆ ıntr-o submultime cu un singur element (chiar el!). f˘r˘ a ¸ine cont de ordinea submultimilor). introducerea elementului ¸ a an ˆ aceste partitii se poate face ˆ oricare din cele k submultimi. ˆ subarborele drept trebuie s˘ fie n − 1 − k ın a a ın a .. k) ın ¸ ın ¸ reprezint˘ num˘rul partitiilor din cazul (2). k).. ∪ Ak . 8.. Se pare c˘ nu este ¸ ın a ¸ a a a a prea clar (sau evident!) de ce apare totu¸i factorul k ˆ expresia k · S(n − 1. . an−1 } ¸ a ¸ ¸ care au k submultimi. sau ¸ (2) ˆ ıntr-o submultime cu cel putin 2 elemente (printre care se g˘¸e¸te ¸i el!). A2 . . b3 = 5. Ak o partitie oarecare. a2 . b2 = 2... dac˘ a a a a ın a a ˆ subarborele stˆng sunt k vˆrfuri. an } o multime cu n elemente ¸i ¸ s A1 ... . k − 1) (f˘r˘ elementul an r˘mˆn a ¸ aa a a n − 1 elemente ¸i k − 1 submultimi ˆ partitie).

Deci. n}. atunci ¸ aa E(n) = 1 D(n) + (−1)n−1 (n − 1) . 2. rezult˘ c˘ a a 2 E(n) = = Tinˆnd cont c˘ ¸ a a |Ai1 ∩ Ai2 ∩ . Dac˘ E(n) reprezint˘ num˘rul permut˘rilor pare1 a a a a ale multimii X f˘r˘ puncte fixe.. ∩ Aik | = (n − k)! rezult˘ formula cerut˘.. 2 1 Dac˘ i < j. PROBLEME REZOLVATE 125 vˆrfuri. Fie X = {1.. Deoarece ¸ a ıncˆ a a num˘rul permut˘rilor pare este 1 n!. .. adunˆnd aceste a s ın a valori pentru k = 0. ¸i cele n − p ın p puncte r˘mase nu mai sunt puncte fixe pentru permutare.7. Care este num˘rul permut˘rilor a n obiecte cu p puncte fixe? a a s Rezolvare: Deoarece cele p puncte fixe pot fi alese ˆ Cn moduri. f˘r˘ puncte fixe.. ∪ An | = 2 1 1 2 n n! − Cn (n − 1)! + Cn (n − 2)! + . ¸i ˆ permutare i apare dup˘ j.. + (−1)n Cn ..8.. n − 1 vom obtine valoarea lui bn . spunem c˘ avem o inversiune. O permutare este a s ın a a par˘ dac˘ are un num˘r par de inversiuni a a a . cu ace¸ti subarbori se pot forma ˆ total bk bn−1−k arbori.. pentru n ≥ 1 ¸ n−1 bn = b0 bn−1 + b1 bn−2 + . . a a 1 n! − |A1 ∪ . rezult˘ c˘ num˘rul a a a a permut˘rilor cu p puncte fixe este egal cu a bn = p Cn D(n − p) Se obtine ¸ deoarece pentru fiecare alegere a celor p puncte fixe exist˘ D(n − p) permut˘ri ale a a obiectelor r˘mase. a aa 3. 2 Rezolvare: Fie Ai multimea permut˘rilor pare p astfel ˆ at p(i) = i.. 1.... + bn−1 b0 = k=0 bk bn−1−k 1 Cn n + 1 2n 2..

126 CAPITOLUL 8. ALGORITMI COMBINATORIALI .

telefon poate fi organizat˘ cu ajutorul a doi ¸ a a vectori (nume ¸i telefon) iar num˘rul curent este indicele din vector.equals(nume[i])) ++i. } se poate scrie ¸i sub forma: s static int cauta (String x) { int i = 0. a ın O tabel˘ cu cˆmpuri care nu sunt de acela¸i tip se poate organiza cu ajutorul a a s vectorilor dac˘ num˘rul de coloane este suficient de mic. i < N. while (i < N && !x. De exemplu. o tabel˘ cu a a a trei informatii: num˘r curent. s a 9. else return 1. if (i < N) return telefon[i].equals(nume[i])) return telefon[i]. return 1. } 127 .1 Problema c˘ut˘rii a a Problema c˘ut˘rii este: se d˘ un vector a cu n elemente ¸i o valoare x de a a a s acela¸i tip cu elementele din a. S˘ se determine p astfel ˆ at x = a[p] sau −1 dac˘ s a ıncˆ a nu exist˘ un element cu valoarea v ˆ a. ++i) if (x.2 C˘utarea secvential˘ a ¸ a static int cauta (String x) { for (int i = 0. nume.Capitolul 9 Algoritmi de c˘utare a 9.

out. if (args.length == 1) System. a a a ın ın a C˘utarea secvential˘ se mai nume¸te ¸i c˘utare linear˘. 2702.000 elemente. telefon[4] "Marius". a class Tabela { final static int N = 6. ceea ce ˆ a a ¸ ın ınseamn˘ un a consum de timp de aproximativ 0.equals(nume[i])) ++i. telefon[2] "Ana". telefon[0] "Robert". c˘utarea execut˘ 5. 2806. 2805. } public static void main (String args[]) { initializare(). 2701. Iat˘ un program complet care utilizeaz˘ c˘utarea linear˘ ˆ a a a a ıntr-o tabel˘. static String nume[] = new String[N+1]. telefon[1] "Laura". a a ın a static int cauta (String x) { int i = 0. } Scrierea procedurii de c˘utare ˆ a ıntr-un tabel de nume este ˆ acest caz mai ın eficient˘. ¸i N operatii ˆ cazul cel mai defavorabil.println(cauta(args[0])). return tel[i]. static int telefon[] = new int[N+1].005 ms (milisecunde). } } .128 ˘ CAPITOLUL 9. telefon[5] = = = = = = 2811. i < N. ˆ ¸ ın s ¸ ın Intr-un tablou cu 10. pentru c˘ se execut˘ N/2 a ¸ a s s a a a a operatii ˆ medie. telefon[3] "Tudor". pentru c˘ nu se face decˆt un singur test ˆ plus aici (ˆ loc de dou˘ teste). ALGORITMI DE CAUTARE O alt˘ posibilitate este de a pune o santinel˘ ˆ cap˘tul tabelei. ++i) if (x.equals(nume[i])) return tel[i]. 4501. static void nume[0] = nume[1] = nume[2] = nume[3] = nume[4] = nume[5] = } initializare() { "Paul".000 operatii ˆ medie. nume[N] = x. static int cauta(String x) { for (int i = 0. while (! x. return 1. telefon[N] = 1.

acela a a ˆ care v nu se g˘se¸te ˆ a. ˆ a ıncepˆnd cu primul.i++) if(x==a[i]) { p=i. cmp = x. } while (s <= d). cmp. atunci CN ≈ 14.˘ ˘ 9. Dac˘ acesta este acela¸i. s = 0. Deci a complexitatea algoritmului pentru cazul cel mai defavorabil este O(n). Dac˘ tabela are 10. Presupunem c˘ a a a ın a a a tabela de nume este sortat˘ ˆ ordine alfabetic˘ (cum este cartea de telefoane).1. log2 N va fi scris mai simplu log N . mai mare) decˆt numele din mijlocul a a a tabelei. } S˘ analiz˘m complexitatea acestui algoritm pe cazul cel mai defavorabil.000 de operatii necesare la c˘utarea linear˘. a a a a a care testeaz˘ elementele vectorului unul dupa altul. se compar˘ cheia de c˘utare cu numele care se af˘ In a ¸ a a a la mijlocul tabelei de nume.i<n. for(i=0. s. a ın a ˆ loc de a c˘uta secvential. num˘rul de astfel de operatii a ¸ a a ¸ elementare efectuate va fi egal cu numarul de elemente al lui a. return -1. ın a s ın In a Considerˆnd ca operatie elementar˘ testul v == a[i]. d = N1. do { i = (s + d) / 2. d. De acum ˆ ınaninte. se returneaz˘ num˘rul de a s a a telefon din mijloc. a p=-1. Acesta reprezint˘ un a a beneficiu considerabil ˆ raport cu 5. static int cautareBinara(String x) { int i. ˆ acest caz se va face o parcurgere complet˘ a lui a. altfel se reˆ ıncepe c˘utarea ˆ prima jum˘tate (sau ˆ a doua) a ın a ın dac˘ numele c˘utat este mai mic (respectiv. if (cmp == 0) return telefon[i]. } Num˘rul CN de comparatii efectuate pentru o tabel˘ de dimensiune N este a ¸ a CN = 1 + C⌊N/2⌋ . else s = i + 1. 9. break.3 C˘utare binar˘ a a O alt˘ tehnic˘ de c˘utare ˆ tabele este c˘utarea binar˘. if (cmp < 0) d = i .3.compareTo(nume[i]). Deci CN ≈ log2 N . CAUTARE BINARA 129 Cel mai simplu algoritm care rezolv˘ aceast˘ problem˘ este c˘utarea liniar˘.000 elemente. unde C0 = 1. adic˘ n. ın ¸ a a .

putem a a as a ¸ a face o ”regul˘ de trei simpl˘” pentru g˘sirea indicelui elementului de referinta a a a ¸˘ pentru comparare. ¸ a s ¸ De exemplu. Aceast˘ metod˘ se nume¸te c˘utare prin interpolare. ¸i s˘ urm˘m restul algoritmului de ın a s a a c˘utare binar˘. boolean gasit. a a ¸ s Pentru cazul secvential este suficient s˘ ad˘ug˘m la cap˘tul tabelei noul ¸ a a a a element.000 elemente ¸i 5 operatii pentru 109 elemente ˆ tabel˘. Aceasta este destul de rar˘ ˆ cazul c˘rtii de telefon a a ın a¸ dar ˆ alte aplicatii. } if(gasit) p=m. ceea ce ˆ a ınseamn˘ cam 4 operatii pentru o tabel˘ de a ¸ a 10. recursiv. else p=-1. 9. Pentru tabele de dimensiuni mari.4 Inserare ˆ tabel˘ ın a La c˘utarea secvential˘ sau binar˘ nu am fost preocupati de inserarea ˆ a ¸ a a ¸ ın tabel˘ a unui nou element. ˆ cazul a ın c˘ut˘rii secventiale ¸i binare. dr. se apeleaz˘ o procedur˘ error care afi¸eaz˘ a a a a s a un mesaj de eroare. Algoritmul poate fi descris. m. Timpul de a a a a s a c˘utare este O(log log N ). foarte elegant. a a Se poate obtine un timp sub-logaritmic dac˘ se cunoa¸te distributia obiectelor. if(am]==x) gasit=true. ˆ loc s˘ alegem mijlocul. Este spectaculos! s ¸ ın a O implementare iterativ˘ a c˘ut˘rii binare ˆ a a a ıntr-un vector ordonat (cresc˘tor a sau descresc˘tor) este: a int st. . st=0. dac˘ are loc. ¸i ea se poate a ¸ a s s folosi pentru tabele de dimensiuni mici. else st=m+1. while((st < dr) && !gasit) { m=(st+dr)/2.130 ˘ CAPITOLUL 9. c˘utarea a binar˘ este mult mai interesant˘. Dac˘ nu are loc. gasit=false. dr=n-1. a Vom vedea cum se realizeaz˘ inserarea unui element ˆ a ıntr-o tabel˘. ALGORITMI DE CAUTARE Desigur c˘utarea secvential˘ este foarte u¸or de programat. sau ˆ dictionar. Presupunˆnd distributia uniform˘. ˆ cartea de telefon. se ¸tie apriori c˘ un nume care ın ın ¸ s a ˆ ıncepe cu litera V se afl˘ c˘tre sfˆr¸it. else if(a[m] > x) dr=m-1. de exemplu lista utilizatorilor unui sistem informatic. este ın ¸ frecvent utilizat˘.

la ma¸inile (calculatoarele) moderne. i < x. De obicei se ia B = 128 sau B = 256 ¸i se presupune c˘ dimensiunea tabelei s a N este un num˘r prim. } 131 Inserarea se face deci ˆ timp constant. De asemenea. Aceasta necesit˘ log n+n ¸ ın a operetii. + xm−1 B + xm mod N. Scopul este de a avea o functie h.length().9. trebuie g˘sit˘ pozitia sa printr-o c˘utare binar˘ (sau ın a a a ¸ a a secvential˘). numerele fiind reprezentate ˆ binar. O astfel de functie este a ¸ ¸ h(x) = x1 B m−1 + x2 B m−2 + .charAt(i)) % N. atunci h(x) = x(m) este functia h a a ¸ care nu depinde decˆt de ultimul caracter al lui x. int val) { ++n. aceasta se face ¸ a pentru a evita orice interferent˘ ˆ ¸a ıntre ˆ ıntre ˆ ınmultirile prin B ¸i ˆ artirile prin ¸ s ımp˘ ¸ N. Pentru inserarea In a a ¸ unui element nou ˆ tabel˘. Se utilizeaz˘ o functie h a a a ın a ¸ de grupare a cheilor (adesea ¸iruri de caractere) ˆ s ıntr-un interval de numere ˆ ıntregi. a ¸ de dispersie. de ordinul O(1). ++i) r = ((r * B) + x. N − 1]. Cˆt despre alegerea lui N .5 Dispersia O alt˘ metod˘ de c˘utare ˆ tabele este dispersia. Pentru o cheie x. Inserarea ˆ ¸ ıntr-o tabel˘ ordonat˘ cu n elemente este deci de ordinul O(n). for (int i = 0. dac˘ de exemplu B = N = 256.. s a a ¸ Calculul functiei h se face prin functia h(x. simplu de calculat ¸i avˆnd o bun˘ distributie pe intervalul [0. } . s ¸ ¸ ın ˆ general. ˆ Intr-adev˘r. if (n >= N) error ("Depasire tabela"). return r. telefon[n] = val. numem[n] = x.5. Totul este ˆ ordine dac˘ a ın a ın a h este o aplicatie injectiv˘. DISPERSIA void inserare(String x. a a 9. trebuie mentinut tabelul ordonat. ¸ ¸ s static int h(String x){ int r = 0. De ce? Pentru c˘ ˆ a a ınmultirea puterilor lui 2 se poate face ¸ foarte u¸or prin operatii de deplasare pe biti. unde m este lungimea ¸irului x. aceste operatii sunt net mai rapide In s ¸ decˆt ˆ a ınmultirea numerelor oarecare. este bine ca functia aleas˘ s˘ permit˘ ¸ a ¸ a a a evaluarea cu un num˘r mic de operatii. apoi se deplaseaz˘ toate elementele din spatele ei spre dreapta cu o ¸ a a pozitie pentru a putea insera noul element ˆ locul corect.. h(x) este locul unde se af˘ x ˆ tabel˘. ın ˆ cazul c˘ut˘rii binare.m).

Se spune atunci a a ¸ a ıncˆ c˘ exist˘ o coliziune. O metod˘ simpl˘ a a s a a a este de a lista coliziunile ˆ ıntr-un tabel col paralel cu tabelul nume. procedura de c˘utare consum˘ un timp mai mare sau egal ca lungimea a a medie a claselor de echivalent˘ definite pe tabel˘ de valorile h(x). Dac˘ da. return 1. c˘utarea este terminat˘. Se poate ¸ a a ın a apoi verifica dac˘ x = nume[h(x)]. a a a a a ˆ ınseamn˘ c˘ tabela contine o alt˘ cheie astfel ˆ at h(x′ ) = h(x). Tabela de coliziuni d˘ o alt˘ intrare i ˆ tabela de nume unde se poate g˘si cheia c˘utat˘. unde M este num˘rul claselor de echivalent˘ definite a ¸a de h. Dac˘ functia de dispersie este perfect uniform˘. i != 1. Se continu˘ astfel cˆt timp col[i] = −1. ¸i u¸or de programat. Dispersia nu face decˆt s˘ reduc˘ printr-un a a a a factor constant timpul c˘ut˘rii secventiale. ALGORITMI DE CAUTARE Deci functia h d˘ pentru toate cheile x o intrare posibil˘ ˆ tabel˘. a a a static int cauta(String x) { for (int i = h(x). i = col[i]) if (x. } Astfel. a a ın a a a Dac˘ nu se g˘se¸te valoarea x ˆ aceast˘ nou˘ intrare i. ¸i tabela trebuie s˘ gestioneze coliziunile. adic˘ de lungimea ¸a a a medie a listei de coliziuni.132 ˘ CAPITOLUL 9. c˘utarea ia un timp de N/M . nu apar a ¸ a coliziuni ¸i determin˘ toate elementele printr-o singur˘ comparatie. Acest caz este s a a ¸ foarte putin probabil. se continu˘ cu intrarea i′ a a s ın a a a dat˘ de i′ = col[i].equals(nume[i])) return telefon[i]. Dac˘ num˘rul mediu de elemente avˆnd aceea¸i valoare de ¸ a a a s dispersie este k = N/M . Dac˘ nu. Interesul fat˘ de dispersie este pentru a a ¸ ¸a c˘ adesea este foarte eficace. a s s .

aceast˘ sortare. 11. 8 va deveni 3. pentru s ın a ın N = 10. 13. ın ıi 10.1 Introducere Ce este sortarea? Presupunem c˘ se d˘ un ¸ir de N numere ˆ a a s ıntregi ai . se d˘ cu ele de p˘mˆnt ¸i se reˆ a a a a s ıncepe. 10.Capitolul 10 Algoritmi elementari de sortare Tablourile sunt structuri de baz˘ ˆ informatic˘. ¸i s se dore¸te aranjarea lor ˆ ordine cresc˘toare. 8. ˆ acest al doilea caz. Aceast˘ problem˘ este clasic˘ ˆ informatic˘ ¸i a fost studiat˘ ˆ detaliu. 3. Un tablou reprezint˘. In ¸ a Un algoritm amuzant const˘ ˆ a vedea dac˘ setul de c˘rti de joc din mˆn˘ a ın a a¸ a a este deja ordonat. 11. 18. a a 133 . ın In a ıntˆ s a a stabilirea clasamentului ˆ ıntre studenti. Dac˘ nu este. 25. De exemplu. 3. 23. ˆ [23]. De exemplu. 9. 10. ¸ s Un tablou permite accesul direct la un element. ¸irul s 18. poate s˘ nu se a a¸ a termine niciodat˘. ˆ sens larg. 9. 25. ¸i noi vom utiliza intens aceast˘ s a proprietate ˆ algoritmii de sortare pe care ˆ vom considera. 13. de a a a ın as a ın exemplu. Desigur. 23. construirea unui dictionar. Dup˘ a un anumit timp. Trebuie ¸ ¸ f˘cut˘ o distintie ˆ a a ¸ ıntre sortarea unui num˘r mare de elemente ¸i a unui num˘r mic a s a de elemente. metoda de sortare este putin important˘. ˆ proctic˘ se ˆ alne¸te adesea aceast˘ problem˘. etc. 3. exist˘ riscul de a avea c˘rtile ordonate. ˆ a ın a a ın functie de dimensiunile sale. un vector sau o matrice cu elemente de acela¸i tip. pentru noi.

se a¸ a ın a a ¸ a a face interschimbarea c˘rtilor de joc ¸i se caut˘ o alt˘ transpozitie.. a[m] = a[1]. a3 . din considerente pedagogice nu este posibil de a prezenta o sortare a mii de elemente.. aN ). Procedeul se a¸ s a a ¸ repet˘ pˆn˘ cˆnd nu mai exist˘ transpozitii. tot la fel. a ın a De aceast˘ dat˘ vom prezenta programul complet. Aceasta se realizeaz˘ prin introducerea unei variabile s a a a i care ia toate valorile ˆ ıntre 1 ¸i N . s a a a s ¸ C˘utarea celui mai mic element ˆ a ıntr-un tablou este un prim exercitiu de ¸ programare. Pentru alti algoritmi. frecvent utilizat˘ la un joc a a a a de c˘rti. 10. El const˘ ˆ g˘sirea pozitiei ˆ tablou a elementului cu valoarea cea mai ¸ a ın a ¸ ın mic˘. a ¸ s c˘utˆndu-se elementul cel mai mic din acest ¸ir ¸i interschimbˆndu-l cu a2 . const˘ ˆ a vedea dac˘ exist˘ o transpozitie de efectuat. . ALGORITMI ELEMENTARI DE SORTARE O alt˘ tehnic˘ (discutˆnd serios de data aceasta). ne vom limita la descrierea efectiv˘ a sort˘rii. a[1] = t. s Toate acestea sunt ar˘tate ˆ programul care urmeaz˘. Cu alte cuvinte. Odat˘ g˘sit˘ aceast˘ a a a a pozitie m. Determinarea pozitiei acestui element este foarte simpl˘. presupunem c˘ trebuie s˘ sort˘m un num˘r de ˆ In a a a a a ıntregi care se g˘sesc ˆ a ıntr-un tablou (vector) a. ea se poate ¸ a efectua cu ajutorul instructiunilor urm˘toare: ¸ a m = 0. apoi cu 3 ¸i s a¸a mai departe pˆn˘ la N . Schimbarea ˆ ıntre ele a celor dou˘ elemente necesit˘ o variabil˘ temporar˘ t a a a a ¸i se efectueaz˘ prin: s a t = a[m]. ++j) if (a[j] < a[m]) m = i.2 Sortare prin selectie ¸ ˆ cele ce urmeaz˘. ¸ a a . Nu este s a a cazul s˘ se caute o metod˘ sofisticat˘ pentru a sorta 10 elemente. adic˘ ˆ a a ıntregul m pentru care ai ≥ am pentru orice i. j < N. a ¸ a¸ Este u¸or de intuit c˘ num˘rul obiectelor de sortat este important. Algoritmul de sortare cel mai simplu este prin selectie. se schimb˘ ˆ ¸ a ıntre ele elementele a1 ¸i am . for (int j = 1. Acest set de operatii trebuie reluat prin ˆ ¸ ınlocuirea lui 1 cu 2.134 CAPITOLUL 10. Aceast˘ metod˘ functioneaz˘ foarte a a a a a ¸ a a ¸ a bine pentru o bun˘ distributie a c˘rtilor. Si a a s s a ¸ a¸a mai departe pˆn˘ la un moment dat cˆnd ¸irul va contine un singur element. Dac˘ exist˘. s Apoi se reˆ ıncepe aceast˘ operatie pentru ¸irul (a2 .. a a Procedurile de achizitionare a datelor ¸i de returnare a rezultatelor sunt ¸ s deasemenea prezentate. a a a nu se trage cu tunul ˆ ıntr-o musc˘! a Exemplele tratate ˆ ıntr-un curs sunt ˆ ıntotdeauna de dimensiuni limitate.

for (i = 0. a[min] = a[i]. System. afisare(). i < N. a[i] = t. } static void sortSelectie() { int min. j < N.print (a[i] + " ").out. i < N. ++i) { min = i. static int[] a = new int[N]. static void initializare() { int i. t = a[min]. ++j) if (a[j] < a[min]) min = j. } } 135 .out. t. j. afisare(). for (i = 0. for (i = 0. SORTARE PRIN SELECTIE ¸ class SortSelectie { final static int N = 10. ++i) System. } static void afisare() { int i. } } public static void main (String args[]) { initializare().10. ++i) a[i] = (int) (Math. sortSelectie(). for (j = i+1.println(). int i.2.random() * 128). i < N 1.

¸i un indice j care permite deplasarea c˘tre marginea i. Se fac deci a s a N − i comparatii. a2 . ¸ a a ¸ O variant˘ a sort˘rii prin selectie este metoda bulelor... aN −1 ). aj ) neordonate. ¸ s a Sortarea prin selectie execut˘ un num˘r de comparatii de ordinul N 2 . . Se ˆ ¸ ıncepe cu i = 1 ¸i se termin˘ cu i = N − 1. Se reˆ ¸ ıncepe cu prefixul (a... . se s a ¸ ¸ pleac˘ de la elementul ai ¸i se compar˘ succesiv cu ai+1 . aN . Procedura corespunz˘toare utilizeaz˘ un indice i care marcheaz˘ sfˆr¸itul a a a as prefixului ˆ sortare.. La fiecare iteratie. . ai+2 . a2 .. se fac s a (N − 1) + (N − 2) + .. + 2 + 1 = N (N − 1)/2 comparatii ¸i N − 1 interschimb˘ri. aN ) inversˆnd toate perechile de elemente consecutive s a (aj − 1.... ALGORITMI ELEMENTARI DE SORTARE Un exemplu de executii este: ¸ 1 2 3 4 5 6 7 12 4 30 3 4 23 14 ⇒ ↑ m 1 2 3 4 5 6 7 12 4 30 7 4 23 14 ⇒ ↑ ↑ i m 1 2 3 4 5 6 7 4 12 30 7 4 23 14 ⇒ ↑ ↑ i m 1 2 3 4 5 6 7 4 4 30 7 12 23 14 ⇒ ↑ ↑ i m 1 2 3 4 5 6 7 4 4 7 30 12 23 14 ⇒ ↑ ↑ i m 1 2 3 4 5 6 7 4 4 7 12 30 23 14 ⇒ ↑ ↑ i m 1 2 3 4 5 6 7 4 4 7 12 14 23 30 ⇒ ↑↑ im 1 2 3 4 5 6 7 ⇒ 4 4 7 12 14 23 30 0 7 ↑ i 0 3 0 3 ↑ i 0 3 1 12 2 4 3 30 0 3 0 3 1 4 ↑ i 1 4 0 3 0 3 1 4 2 12 ↑ m 2 4 ↑ i 2 4 4 7 ↑ m 3 4 30 7 5 4 6 23 7 14 5 4 6 23 7 14 3 30 0 3 0 3 1 4 2 4 3 7 ↑ i 3 7 0 3 0 3 1 4 2 4 3 7 0 3 0 3 1 4 2 4 3 7 0 3 0 3 1 4 2 4 3 7 5 6 12 23 ↑ m 4 5 6 30 12 23 ↑ m 4 5 6 12 30 23 ↑ ↑ i m 4 5 6 12 14 23 ↑ i 4 5 6 12 14 23 ↑↑ im 4 5 6 12 14 23 4 7 7 14 7 14 7 14 7 30 ↑ m 7 30 7 30 Este u¸or de calculat num˘rul de operatii necesare. Principiul ei este de a a ¸ a parcurge ¸irul (a1 ... elementul maxim se va afla pe a pozitia N . (a1 .. Deci.136 CAPITOLUL 10. a2 ). Dup˘ prima parcurgere. ın s a . ..

eventual interschimb˘ri (dac˘. i) for (int j = 1. for (int i = N1. i >= 0. de exemplu. ¸ ın a static void sortBule() { int t. a[j1] = a[j]. a[j] = t.2. 6 7 14 30 ↑ i 6 7 14 30 ↑ i 6 23 0 7 1 4 3 30 0 7 1 4 2 12 3 3 4 3 ↑ j 4 30 5 4 6 23 0 7 1 4 0 7 1 4 2 12 3 3 4 4 5 4 ↑ j 5 30 6 23 0 7 1 4 0 7 1 4 2 12 3 3 4 4 5 23 6 23 ↑ j 6 30 0 7 1 4 0 7 1 4 2 12 3 3 4 4 5 23 0 7 0 4 Dup˘ prima parcurgere 30 a 1 2 3 4 5 6 4 12 3 4 23 14 ↑ ↑ j i 1 2 3 4 5 6 7 7 12 3 4 23 14 30 ↑ ↑ j i locul s˘u (ultimul loc ˆ a ın 0 1 2 3 4 5 4 7 12 3 4 23 ↑ j 0 1 2 3 4 5 4 7 3 12 4 23 ⇒ ↑ j . SORTARE PRIN SELECTIE ¸ 137 Se poate calcula de asemenea foarte u¸or num˘rul de operatii ¸i se obtine un s a ¸ s ¸ num˘r de ordinul O(N 2 ) comparatii ¸i. a ¸ s a a tabloul este initial ˆ ordine descresc˘toare). ++j) if (a[j1] > a[j]) { t = a[j1]. } } 0 7 1 12 2 4 ↑ j 2 12 3 30 4 3 5 4 6 23 7 14 ⇒ ↑ i 7 14 ⇒ ↑ i 7 14 ⇒ ↑ i 7 14 ⇒ ↑ i 7 14 ⇒ ↑↑ ji a ajuns pe 7 30 ⇒ 0 7 1 4 2 3 4 5 12 30 3 4 ↑ j 2 3 4 5 12 3 30 4 ↑ j 2 3 4 5 12 3 4 30 ↑ j 2 3 4 5 12 3 4 23 7 14 ↑ i 6 7 23 14 ↑ i 6 7 23 14 ↑ i 6 7 30 14 ↑ ↑ j i 6 7 14 30 ↑↑ ji vector). j <= i.10.

la aceast˘ parcurgere s-au mai aranjat a cˆteva elemente!). j <= i. O idee bun˘ este introducerea unei a a variabile care s˘ contorizeze num˘rul de interschimb˘ri din cadrul unei parcurgeri. s a 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 4 3 4 7 12 14 23 30 3 4 4 7 12 14 23 30 ⇒ ↑ ↑ ↑ ↑ j i j i Dup˘ a patra parcurgere 12 a ajuns pe locul s˘u (de fapt era deja pe locul s˘u a a a de la ˆ ınceputul acestei parcurgeri. a¸a c˘ urm˘toarele parcurgeri se fac. for (int j = 1. oricum. f˘r˘ s a a aa s˘ se execute nici o interschimbare de elemente. a a ın 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 4 7 3 4 12 14 23 30 4 3 7 4 12 14 23 30 ⇒ ↑ ↑ ↑ ↑ j i j i 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 4 3 7 4 12 14 23 30 4 3 4 7 12 14 23 30 ⇒ ↑ ↑ ↑ ↑ j i j i Dup˘ a treia parcurgere 14 a ajuns pe locul s˘u (de fapt era deja pe locul a a s˘u de la ˆ a ınceputul acestei parcurgeri. a a a Dac˘ nu s-a efectuat nici o interschimbare atunci vectorul este deja sortat a¸a c˘ a s a se poate ˆ ıntrerupe executia urm˘toarelor parcurgeri. } } . i >= 0. ++j) if (a[j1] > a[j]) {t = a[j1]. Programul modificat este: ¸ a static void sortBule() { int t. k++. de asemenea. a La urm˘toarea parcurgere nu se efectueaz˘ nici o interschimbare de elemente. a[j] = t. i) { k=0.} if(k==0) break. for (int i = N1. k. s-au mai aranjat totu¸i cˆteva elemente!). a a Vectorul este deja sortat. a[j1] = a[j].138 0 4 1 7 2 3 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE 3 12 4 4 ↑ j 4 12 5 23 6 7 0 1 2 3 4 5 6 7 14 30 4 7 3 4 12 23 14 30 ⇒ ↑ ↑ ↑ i j i 0 1 2 3 5 6 7 0 1 2 3 4 5 6 7 4 7 3 4 23 14 30 4 7 3 4 12 14 23 30 ⇒ ↑↑ ↑↑ ji ji Dup˘ a doua parcurgere 23 a ajuns pe locul s˘u (penultimul loc ˆ vector).

++i) { v = a[i]. } a[j] = v. a a 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 7 12 4 30 3 4 23 14 4 7 12 30 3 4 23 14 ⇒ ↑ ↑ ↑ ↑ j i j i . Aceasta este metoda a a ¸ utilizat˘ pentru sortarea unui pachet de c˘rti de joc. SORTARE PRIN INSERTIE ¸ 139 10. i < N. j.3. Se continu˘ pˆn˘ la i = N . c˘ci va ie¸i din tablou spre stˆnga. Se ia prima carte. a a a 10. atunci sortarea se nume¸te sortare prin insertie ¸ s ¸ direct˘. Se a a a¸ a ia a i-a carte ¸i se plaseaz˘ pe pozitia corespunz˘toare relativ la primele i − 1 c˘rti s a ¸ a a¸ deja sortate.3. Se spune c˘ a fost plasat˘ ¸ a a a o santinel˘ la stˆnga tabloului a. for (int i = 1. Aceasta nu este ˆ a a ıntotdeauna posibil. Se poate remedia aceast˘ a s a a situatie plasˆnd un element a0 de valoare −max int. se face secvential. while (j > 0 && a[j-1] > v) { a[j] = a[j-1]. Un exemplu numeric a ın este cel care urmeaz˘: a Inserarea lui 12 nu necesit˘ deplas˘ri de elemente. dac˘ ai este cel mai mic ¸ a ¸ a a element din tablou.3 Sortare prin insertie ¸ O metod˘ complet diferit˘ este sortarea prin insertie.10. apoi a a a¸ doua ¸i se aranjeaz˘ ˆ ordine cresc˘toare. Procedura care realizeaz˘ aceast˘ortare este: a a s static void sortInsertie() { int j. } } Pentru plasarea elementului ai din vectorul nesortat (elementele de pe pozitiile ¸ din stˆnga fiind deja sortate) se parcurge vectorul spre stˆnga plecˆnd de la pozitia a a a ¸ i − 1. ˆ sub¸irul format de primele a ¸ a ın s i − 1 elemente. j = i. Elementele vizitate se deplaseaz˘ cu o pozitie spre dreapta pentru a permite a ¸ plasarea elementului ai (a c˘rui valoare a fost salvat˘ n variabila temporar˘ v) pe a aˆ a pozitia corespunz˘toare. Procedura contine o mic˘ eroare. Se ia a treia carte ¸i se plaseaz˘ pe s a ın a s a pozitia corespunz˘toare fat˘ de primele dou˘ c˘rti. ¸i atunci s trebuie ad˘ugat un test asupra indicelui j ˆ bucla while. s˘ presupunem c˘ primele i − 1 c˘rti sun deja sortate cresc˘tor.1 Insertie direct˘ ¸ a Dac˘ determinarea pozitiei corespunz˘toare. Pentru ¸ a ¸a a a¸ s s cazul general. v. ¸i a¸a mai departe.

ea are o proprietate foarte bun˘: num˘rul de operatii depinde puternic In a a ¸ de ordinea initial˘ din tablou. Cea mai bun˘ metod˘ de sortare necesit˘ a a ¸ a a a n log2 n comparatii. ˆ cazul ˆ care tabloul este aproape ordonat. Deci. aceast˘ metod˘ de sortare este mai s s a a eficient˘ decˆt sortarea prin selectie. ¸i drept ¸ a In ın s urmare exist˘ putine inversiuni ¸i sunt necesare putine operatii. a Sortarea prin inserare este deci o metod˘ de sortare bun˘ dac˘ tabloul de a a a sortat are ¸ansa de a fi aproape ordonat. a a 4 5 6 7 30 4 23 14 ⇒ ↑ i 1 2 3 5 6 7 1 2 3 4 5 6 7 4 7 12 4 23 14 4 4 7 12 30 23 14 ⇒ ↑ ↑ ↑ ↑ j i j i 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 3 4 4 7 12 30 23 14 3 4 4 7 12 23 30 14 ⇒ ↑ ↑ ↑ ↑ j i j i 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 3 4 4 7 12 23 30 14 3 4 4 7 12 14 23 30 ⇒ ↑ ↑ ↑ ↑ j i j i Num˘rul de comparatii pentru inserarea unui element ˆ secventa deja sortat˘ a ¸ ın ¸ a este egal cu num˘rul de inversiuni plus 1. num˘rul total de comparatii pentru sortarea prin insertie a ¸ ¸ este N 0 4 ↑ j 0 3 1 7 2 12 3 30 4 3 ↑ i 4 30 5 4 6 23 7 14 0 3 ↑ j 0 3 1 4 2 7 3 12 Cπ = i=2 ci = N − 1 + inv(π). ALGORITMI ELEMENTARI DE SORTARE Inserarea lui 30 nu necesit˘ deplas˘ri de elemente. a Fie ci num˘rul de comparatii. Atunci a ¸ ci = 1 + card{aj |aj > ai . unde num˘rul de a s a a inversiuni este inv(π). a a De¸i ordinul de cre¸tere este tot N 2 . j < i} Pentru o permutare π corespunz˘toare unui ¸ir de sort˘ri. s . num˘rul mediu de comparatii este a ¸ CN = 1 N! Cπ = N − 1 + N (N − 1) N (N + 3) = −1 4 4 π∈SN unde Sn reprezint˘ grupul permut˘rilor de n elemente.140 CAPITOLUL 10. ¸ ˆ plus. spre deosebire de a ¸ s ¸ ¸ primele dou˘ metode de sortare.

for(int j=k. int i) { if (i != k) { int x=a[k].10.out.5. } static int pozitiaCautBin(int p.println().4}.1.8.3. iar ˆ loc s˘ se fac˘ insertia direct˘ ˆ ın a ın a a ¸ a ın aceast˘ secvent˘. Un program complet este: a import java. static void afiseaza() { int j. int u.length.out. int x) { int i=u+1. } static void deplasare(int k. } } .4.print(a[j] + " "). a[i]=x. else return i. j>=i+1. while (p <= u) { i=(p+u)/2. if (x>a[i]) p=i+1. j<a.*. SORTARE PRIN INSERTIE ¸ 141 10. class SortInsBinApl { static int[] a={3.2 Insertie binar˘ ¸ a Metoda anterioar˘ se poate ˆ a ımbunat˘¸i dac˘ ¸inem cont de faptul c˘ secventa at at a ¸ ˆ care se face inserarea este deja ordonat˘. else if (x<a[i]) u=i-1. for(j=0.6. c˘utarea pozitiei pe care se face inserarea se face prin c˘utare a ¸a a ¸ a binar˘.9. j--) a[j]=a[j-1].io. } return p. System. j++) System.3.

i<n-1.k<=N-1.a[k]).4 Sortare prin interschimbare Aceast˘ metod˘ folose¸te interschimbarea ca ¸i caracteristic˘ principal˘ a a a s s a a metodei de sortare.length. ˆ cadrul acestei metode se compar˘ ¸i se interschimb˘ perechi In as a adiacente de chei pan˘ cˆnd toate elementele sunt sortate. deplasare(k.k-1. n=a. a[i+1] = x. i++) if (a[i]>a[i+1]) { x = a[i].i). a a static void interschimbare(int a[]) { int x. } } 10. while (!schimb) { schimb=false.k++) { i=pozitiaCautBin(0. } } public static void main(String[] args) { afiseaza(). afiseaza().i. sorteaza(). ALGORITMI ELEMENTARI DE SORTARE static void sorteaza() { int N=a. a[i] = a[i+1].142 CAPITOLUL 10. i. for(i=0. boolean schimb = true. schimb=true. } } } .length. for(int k=1.

SORTARE PRIN MICSORAREA INCREMENTULUI . m<4. a • dac˘ cei t incrementi sunt h1 . 42. Rezult˘: a ¸ a a 6.10. 18. 55. } } } . 94. 2. k. for (m=0. i++) { x = a[i]. (j>=0) && (a[j]>x). 97 Se observ˘ urm˘toarele: a a • un proces de sortare i − sort combin˘ 2 grupuri sortate ˆ procesul 2i − sort a ın anterior • ˆ exemplul anterior s-a folosit secventa de incrementi 4. 94. 55. i. a[j+k] = x. 3. 42.sort a 6. 12. 44. ˆ cazul cel ¸a ¸ a a In mai defavorabil. 55. x. 55. n=a. 1}. 5. int m. 6. h2 . 42.SHELL ¸ 143 10.. ˆ ultimul pas se face totul. 06. j-=k) a[j+k] = a[j]. ¸i se sorteaz˘ separat. 12. 1 dar orice ın ¸ secvent˘. a ¸ ¸a s a Acest proces este numit numit 4 sort. 67. 44. 18. j.5 Sortare prin mic¸orarea incrementului . . 97 Apoi se sorteaz˘ sirul rezultat ˆ a ıntr-o singur˘ trecere: 1 . m++) { k = h[m]. for (j = i-k. Rezult˘ ¸irul: as 44. cu conditia ca cea mai fin˘ sortare s˘ fie 1 − sort. int n) { static int h[] = {9. 94. Se grupeaz˘ elementele aflate la 4 pozitii distant˘. 18. fiecare hi -sort a s se poate implementa ca ¸i o sortate prin insertie direct˘. dar cu multe comparatii ¸i ın ¸ s interschimb˘ri. 12. s a void shell(int a[]. ht . /* sortare elemente aflate la distanta k in tablul a[] */ for (i=k. 67 Apoi se sorteaz˘ elementele aflate la 2 pozitii distant˘.shell s Prezent˘m metoda pe urm˘torul ¸ir: a a s 44. 94. 12. 18.5.length. i<n. ht = 1 ¸i hi+1 < hi . 42.

144 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE .

¸ a independent de natura elementelor sale. Referinta c˘tre prima celul˘ a ¸ ¸a a a ¸ a a este continut˘ ˆ ¸ a ıntr-o variabil˘. Mai precis. ˆ sensul c˘ num˘rul elementelor variaz˘ ˆ ın a a a ın cursul executiei programului. prin ad˘ug˘ri sau ¸tergeri de elemente pe parcursul ¸ a a s prelucr˘rii.Capitolul 11 Liste Scopul listelor este de a genera un ansamblu finit de elemente al c˘rui num˘r a a nu este fixat apriori.1 Liste liniare Fiecare element al listei este continut ˆ ¸ ıntr-o celul˘ care contine ˆ plus adresa a ¸ ın elementului urm˘tor. a class Lista { int continut. obiecte informatice complexe. etc. operatiile permise sunt: a ¸ • testarea dac˘ ansamblul este vid a • ad˘ugarea de elemente a • verificarea dac˘ un element este ˆ ansamblu a ın • ¸tergerea unui element s 11. ¸iruri de caractere. Nu suntem acum interesati s ¸ de elementele acestui ansamblu ci de operatiile care se efectueaz˘ asupra acestuia. numit ¸i pointer. Java permite realizarea listelor cu ajutorul a s claselor ¸i obiectelor: celulele sunt obiecte (adic˘ instante ale unei clase) ˆ care s a ¸ ın un cˆmp contine o referint˘ c˘tre celula urm˘toare. 145 . Listele sunt obiecte dinamice. Elementele acestui ansamblu pot fi numere ˆ ıntregi sau reale. Lista urmator.

146 Lista (int x. De asemenea s a ın as a new Lista(2. new Lista(11. Aceast˘ modalitate a ın a de a introduce elemente ˆ capul listei este util˘ pentru ca num˘rul de operatii ın a a ¸ necesare ad˘ug˘rii de elemente s˘ fie independent de m˘rimea listei. este suficient˘ a a a a a modificarea valorii capului listei. 7.1: Ad˘ugarea unui element ˆ list˘ a ın a Functia cauta. Obiectul null apartine tuturor s ¸ ¸ claselor ¸i reprezint˘ ˆ cazul listelor marcajul de sfˆr¸it de list˘.urmator. a = a.urmator pentru a parcurge a elementele listei pˆn˘ se g˘se¸te x sau pˆn˘ se g˘se¸te sfˆr¸itul listei (a = null). } return false. // capul vechi se va regasi dupa x } cap cap e4 e3 e2 e1 e4 e3 e2 e1 Figura 11. 11. a). LISTE Instructiunea new Lista(x. Lista a) { return new Liste (x. Func tia Lista(x.a) este un constructor al clasei Lista (un constructor este o functie nestatic˘ care se distinge prin tipul rezultatului s˘u care este cel al clasei ¸ a a curente. } Procedura adauga insereaz˘ un element ˆ capul listei. efectueaz˘ o parcurgere ¸ a a ın a a listei. a a a s a a a s as static boolean cauta (int x. ceea ce se face simplu prin: static Lista adaug (int x. new Lista(7. urmator = a. a static boolean esteVida (Lista a) { return a == null. Lista a) { continut = x. Variabila a este modificat˘ iterativ prin a=a.a) construie¸te o nou˘ celul˘ cu cˆmpurile x ¸i ¸ s a a a s a. } } CAPITOLUL 11. Lista a) { while (a != null) { if (a.null))) reprezint˘ lista 2. care verific˘ dac˘ elementul x este ˆ lista a. ¸i prin absenta numelui de identificare).continut == x) return true. } .

continut == x) return true. } sau sub forma: static boolean cauta (int x. a = a. while (a != null) { ++lg. a.urmator). Lista a) { if (a == null) return false. Tipul list˘ verific˘ ecuatia urm˘toare: a a ¸ a Lista = {Lista vida} ⊕ Element × Lista unde ⊕ este sau exclusiv iar × este produsul cartezian. else return (a. De ¸ a ın a a exemplu. } care este mai elegant˘ decˆt scrierea iterativ˘: a a a static int lungime(Lista a) { int lg = 0. lungimea listei se poate determina prin: static int lungime(Lista a) { if (a == null) return 0. else return 1 + longime(a.11.urmator). } return lg. a. LISTE LINIARE Functia cauta poate fi scris˘ ˆ mod recursiv: ¸ a ın static boolean cauta (int x.urmator. } sau sub forma: static boolean cauta (int x.urmator)). a.continut == x || cauta(x. else return cauta(x. Toate procedurile sau functiile care opereaz˘ asupra listelor se pot scrie recursiv ˆ aceast˘ manier˘.1. else if (a. } 147 Aceast˘ sciere recursiv˘ este sistematic˘ pentru functiile care opereaz˘ asupra a a a ¸ a listelor. Lista a) { return a != null && (a. } . Lista a) { if (a == null) return false.continut == x) || cauta(x.urmator).

urmator. if (b. a. Lista a) { if (a != null) if (a. Lista a) { if (a != null) if (a.2: Eliminarea unui element din list˘ a O procedur˘ iterativ˘ solicit˘ un plus de atentie.urmator. Pe de alt˘ parte se spune c˘ scrierea iterativ˘ este mai eficient˘ pentru c˘ a a a a a folose¸te mai putin˘ memorie.urmator. else a.continut == x) a = a.urmator != null && b. return a. Gratie noilor tehnici de compilare. este o problem˘ foarte putin critic˘.urmator.urmator).urmator. pentru calculatoarele de ¸ a a ast˘zi. else { Lista b = a .urmator = elimina (x. acest lucru este s ¸ a ¸ mai putin adev˘rat.continut == x) a = a.urmator != null) b. while (b. a a ¸ a Eliminarea unei celule care contine x se face modificˆnd valoarea cˆmpului ¸ a a urmator continut ˆ predecesorul lui x: succesorul predecesorului lui x devine ¸ ın succesorul lui x.148 CAPITOLUL 11. } . LISTE Alegerea ˆ ıntre maniera recursiv˘ sau iterativ˘ este o problem˘ subiectiv˘ ˆ a a a a ın general. Un tratament particular trebuie f˘cut dac˘ elementul care trebuie a a eliminat este primul element din list˘.continut != x) b = b. } cap cap e4 e3 e2 e1 e4 e3 e2 e1 Figura 11. a a a ¸ static Lista elimina (int x.urmator = b.urmator. ocuparea de memorie suplimentar˘. } return a. Procedura recursiv˘ de eliminare este foarte a a compact˘ ˆ definitia sa: a ın ¸ static Lista elimina (int x.

contine elementele de informatii. care permite evitarea unor teste pentru a a a eliminarea unui element dintr-o list˘ ¸i. Astfel obtinem o list˘ p˘zit˘.11. Pentru a face acest lucru. ˆ general. trebuie recopiat˘ o parte a listei a ˆ ¸ a ıntr-o nou˘ list˘. else if (a. LISTE LINIARE 149 ˆ cadrul functiilor anterioare. una care contine x ¸i alta identic˘ cu precedenta dar care nu mai ¸ s a contine x.urmator)).urmator. Aceasta este o diferent˘ important˘ fat˘ de limbajele a ¸a a ¸a de programare precum Pascal sau C. care nu are informatie semnificativ˘ ¸ a ˆ cˆmpul continut. s a Exemplu 1. ¸ ¸ contine adresa elementului urm˘tor din tabloul continut. Se a a s a utilizeaz˘ o celul˘ plasat˘ la ˆ a a a ınceputul listei. numit urmator.continut == x) return a. else return new Lista (a. elimina (x. Este deci foarte a ¸ eficient˘. care modific˘ lista a. a a a static Lista elimina (int x. Consider˘m construirea unei liste de numere prime mai mici a decˆt un num˘r natural n dat. Aceast˘ notiune este a a a ¸ un pic echivalent˘ cu notiunea de santinel˘ pentru tablouri. Oricum. adresa adev˘ratei prime celule se afl˘ ˆ cˆmpul urmator al ın a a a ın a acestei celule. unde trebuie s˘ fim preocupati de spatiul de a ¸ ¸ memorie pierdut. ˆ a a In reprezentarea anterioar˘ lista vid˘ nu a avut aceea¸i structur˘ ca listele nevide. ¸ a Procedura adauga efectueaz˘ numai 3 operatii elementare. Se utilizeaz˘ aceast˘ a ¸ a a a tehnic˘ ˆ proceduri asupra ¸irurilor prea lungi. Procedurile cauta ¸i elimina sunt mai lungi pentru c˘ trebuie s˘ parcurg˘ a s a a a ˆ ıntreaga list˘. a ın a a a Pentru implementarea listelor se pot folosi perechi de tablouri : primul tablou. ˆ ın . numit continut.continut. a. Se poate estima c˘ num˘rul mediu de operatii este jum˘tate din a a a ¸ a lungimea listei. recuperarea a se efectueaz˘ foarte rapid. devin mai simple. se pot defini liste circulare cu gard˘ / paznic ˆ care ˆ prima a ın ın celul˘ ˆ cˆmpul urmator este ultima celul˘ din list˘. avantajul fiind c˘ lista vid˘ contine ¸ a a a a a ¸ numai garda ¸i prin urmare un num˘r de programe. } Exist˘ o tehnic˘. Cu tehnicile actuale ale noilor versiuni ale GC. spatiul de memorie pierdut este recuperat de a ¸ culeg˘torul de spatiu de memorie GC (Garbage Colection) din Java. C˘utarea binar˘ efectueaz˘ un num˘r logaritmic iar c˘utarea cu a a a a a tabele hash (de dispersie) este ¸i mai rapid˘.1. cum face programul urm˘tor. dac˘ nu mai a ¸ a este folosit˘ lista a. utilizat˘ adesea. care f˘ceau un caz special din s a a lista vid˘ sau din primul element din list˘. Pentru construirea acestei liste vom ˆ a a ıncepe. iar al doilea. Lista a) { if (a != null) return null. unde exist˘ o utilizare putin important˘ a a a a ¸ a de memorie suplimentar˘. a ın s De asemenea. Aceasta const˘ ˆ utilizarea unui fanion / santinel˘ ¸ a ın a care permite tratarea omogen˘ a listelor indiferent dac˘ sunt vide sau nu. pentru simplificarea program˘rii a s ın a operatiilor asupra listelor. dac˘ trebuie s˘. nu se dispune de dou˘ In ¸ a a liste distincte.l reutiliz˘m.

a != null.150 CAPITOLUL 11. --i) { a=adauga(i. } static int cauta (String x) { for (int a = al[h(x)].a).continut])) return telefon[a. al[i] = adauga (x. int k. } return -1. ınl˘ ¸ a s a a a a a Lista al[] = new Lista[N1]. } . int val) { int i = h(x). for(Lista b=a.. k*k<=n. a a ¸ a a num˘rul de coliziuni devine important.a). b=b. Aceasta se realizeaz˘ prin procedura urm˘toare: s a ¸ a a static Lista Eratostene (int n) { Lista a=null. } k=a. Elementele a ınl˘ ¸ a a listei sunt intr˘ri care permit accesul la tabloul de nume ¸i numere de telefon. al[i]). dac˘ functia h este r˘u aleas˘.urmator) { k=b. j<=n/k. LISTE prima faz˘. } return a.urmator) { if (x. Vom utiliza metoda clasic˘ a ın a ın a a ciurului lui Eratostene: consider˘m succesiv elementele listei ˆ ordine cresc˘toare a ın a ¸i suprim˘m toti multiplii lor. i>=2.continut.continut]. multe liste devin de dimensiuni enorme ¸i a s f˘rˆmitarea risc˘ s˘ devin˘ la fel de costisitoare ca ¸i c˘utarea ˆ aa ¸ a a a s a ıntr-o list˘ simplu a ˆ antuit˘ obi¸nuit˘.continut. c˘utarea este rapid˘. a s Procedurile de c˘utare ¸i inserare a unui element x ˆ a s ıntr-un tablou devin proceduri de c˘utare ¸i ad˘ugare ˆ a s a ıntr-o list˘. a = a. prin ad˘ugarea numerelor de la 2 la n ˆ a a ıncepˆnd cu n ¸i terminˆnd cu a s a 2. Tot astfel. static void insereaza (String x. Astfel numerele vor fi ˆ list˘ ˆ ordine cresc˘toare. Dac˘ h este bine aleas˘. for(int j=k. ++j) a=elimina(j*k. for(int i=n.n − 1] se construie¸te s o list˘ simplu ˆ antuit˘ format˘ din toate cheile care au h(x) = i. Pentru fiecare intrare i din intervalul [0. } Exemplu 2.equals(nume[a.

f alse}. ¸ a ın esteV ida(C) aste egal˘ cu true dac˘ ¸i numai dac˘ coada C este vid˘. ˆ englez˘ se spune a as In a strategie FIFO (First In First Out). as • valoare este o aplicatie definit˘ pe Coada(E)−C0 cu valori ˆ E care asociaz˘ ¸ a ın a unei cozi C. elementul aflat ˆ cap.2. COZI 151 11. C0 )) = x – esteV ida(C0 ) = true O prim˘ idee de realizare sub form˘ de programe a operatiilor asupra cozilor a a ¸ este de a ˆ ımprumuta tehnica folosit˘ ˆ locurile unde clientii stau la coad˘ pentru a ın ¸ a a fi serviti. a as a a • adauga este o aplicatie definit˘ pe E × Coada(E) cu valori ˆ Coada(E). valoare.11. C)) = valoare(C) • Pentru toate cozile C – esteV ida(adauga(x. a ın • elimina este o aplicatie definit˘ pe Coada(E)−C0 cu valori ˆ Coada(E) care ¸ a ın asociaz˘ unei cozi nevide C o coad˘ obtinut˘ plecˆnd de la C ¸i eliminˆnd a a ¸ a a s a primul element. ˆ opozitie cu strategia LIFO (Last In Firs ın ¸ Out) utilizat˘ la stive.2 Cozi Cozile sunt liste liniare particulare utilizate ˆ programare pentru gestionarea ın obiectelor care sunt ˆ a¸teptarea unei prelucr˘ri ulerioare. Elementele sunt s ad˘ugate sistematic la coad˘ ¸i sunt eliminate din capul listei. adauga. C)) = f alse • Pentru coada C0 – elimina(adauga(x. elimina: ¸ • esteV ida este o aplicatie definit˘ pe Coada(E) cu valori ˆ {true. etc. C) este coada obtinut˘ plecˆnd de la coada C ¸i inserˆnd elementul ¸ a a s a x la sfˆr¸itul ei. iar operatiile asupra cozilor sunt: esteV ida. coada vid˘ (care nu contine nici un element) a a ¸ este C0 . C)) = adauga(x. elimina(C)) – valoare(adauga(x. de exemplu procesele ın s a de a¸teptare a resurselor unui sistem. de exemplu la gar˘ pentru a lua bilete. nodurile unui graf. C0 )) = C0 – valoare(adauga(x. ¸ a ın adauga(x. multimea cozilor cu a ¸ ¸ elemente din E este notat˘ Coada(E). consider˘m o multime de elemente E. Operatiile asupra cozilor satisfac urm˘toarele relatii: ¸ a ¸ • Pentru C = C0 – elimina(adauga(x. a Mai formalizat. nevid˘. . sau la cas˘ ˆ ¸ a a ıntr-un magazin.

vida. a • atunci cˆnd functionarul este liber el poate servi un alt client. gestionarul trebuie s˘ cunoasc˘ dou˘ a a a numere: num˘rul obtinut de c˘tre ultimul client sosit ¸i num˘rul obtinut de c˘tre a ¸ a s a ¸ a ultimul client servit. s a as a • atunci cˆnd sose¸te un nou client. se incrementeaz˘ sf arsit ¸i se d˘ acest a s a s a num˘r clientului respectiv. } . plina= false. dac˘ coada a ¸ a nu este vid˘. boolean plina. c. int inceput. } static void facVida (Coada c) { c. LISTE Fiecare client care se prezint˘ obtine un num˘r ¸i clientii sunt apoi chemati de c˘tre a ¸ a s ¸ ¸ a functionarul de la ghi¸eu ˆ ordinea cresc˘toare a numerelor de ordine primite la ¸ s ın a sosire. int sfarsit. Coada () { inceput = 0. Se spune c˘ este un tablou (sau tampon) a circular. vida.sfarsit = 0.plina = false. info = new int[MaxC]. vida = true. int continut[]. a a s a a ˆ cele ce urmeaz˘ sunt prezentate toate operatiile ˆ Java utilizˆnd tehnicile In a ¸ ın a urm˘toare: se reatribuie num˘rul 0 unui nou client atunci cˆnd se dep˘¸e¸te un a a a as s anumit prag pentru valoarea sf arsit.vida = true. } static Coada vida(){ return new Coada(). sfarsit = 0. a a class Coada { final static int MaxC = 100.inceput = 0. } static boolean esteVida(Coada c) { return c. incrementeaz˘ inceput ¸i cheam˘ posesorul acestui num˘r.152 CAPITOLUL 11. sau coad˘ circular˘. c. Not˘m aceste dou˘ numere inceput ¸i sf arsit ¸i gestion˘m a a s s a sistemul ˆ modul urm˘tor: ın a • coada de a¸teptare este vid˘ dac˘ ¸i numai dac˘ inceput = sf arsit. Pentru gestionarea acestui sistem. c.

vida = c.plina = c.").inceput = succesor(c. c. c.inceput). c. COZI 153 static boolean estePlina(Coada c) { return c.inceput.sfarsit] = x. } static void adaug(int x.info[c.plina = false.inceput. } private static int succesor(int i) { return (i+1) % MaxC.2.11.sfarsit)..sfarsit == c.3: Coad˘ de a¸teptare implementat˘ ca list˘ a s a a O alt˘ modalitate de gestionare a cozilor const˘ ˆ utilizarea listelor ˆ antuite cu a a ın ınl˘ ¸ . } static void elimina (Coada c) { if (c. return c. } static int valoare(Coada c) { if (c.vida) erreur ("Coada Vida.plina. c. en Figura 11.inceput].vida = false. c.")."). } } inceput sfarsit e1 e2 . Coada c) { if (c.info[f. c.sfarsit == c..plina) erreur ("Coada Plina.vida) erreur ("Coada Vida.sfarsit = succesor(c. c.

next. Lista b) { inceput = a."). c.sfarsit. c. } static void facVida (Coada c) { Lista garda = new Lista().inceput == c.next = a.next.sfarsit = garda.inceput.inceput = c.sfarsit.154 CAPITOLUL 11. Coada c) { Lista a = new Lista (x.inceput. } static void elimina (Coada c) { if (esteVida(c)) erreur ("Coada Vida. Coada (Lista a. LISTE gard˘ ˆ care se cunosc adresele primului ¸i ultimului element. } static Coada vida() { Lista garda = new Lista(). } static void adauga (int x. } static int valoare (Coada c) { Lista b = c.info. } } Cozile se pot implementa fie cu ajutorul tablourilor fie cu ajutorul listelor sim- . null). garda). return new Coada (garda. return b. c. Aceasta d˘ operatiile a ın s a ¸ urm˘toare: a class Coada { Lista inceput. Lista sfarsit.inceput = c. sfarsit = b. f.sfarsit = a. } static boolean esteVida (Coada c) { return c.

stiva vid˘ (care nu ¸ a a contine nici un element) este S0 . rolul s˘u principal ¸ a ın ın a fiind la implementarea apelurilor de proceduri. ¸ ¸ adauga. valoare. elimina. De aceast˘ dat˘. relatiile satisf˘cute sunt s a a ¸ a urm˘toarele: a • elimina(adauga(x. Stiva() { inaltime = 0. Ansamblul functiilor care opereaz˘ asupra ¸ a cozilor se pot plasa ˆ ıntr-un modul care manipuleaz˘ tablouri sau liste.3 Stive Notiunea de stiv˘ intervine ˆ mod curent ˆ programare. STIVE 155 plu ˆ antuite. S)) = x • esteV ida(S0 ) = true Cu ajutorul acestor relatii se pot exprima toate operatiile relative la stive.11. } . Element continut[]. se consider˘ o multime ın a In a ¸ E. Scrierea programelor const˘ ˆ primul rˆnd ˆ alegerea structurilor ınl˘ ¸ a ın a ın de date pentru reprezentarea cozilor.3. multimea stivelor cu elemente din E este notat˘ Stiva(E). O stiv˘ se poate imagina ca o cutie a ˆ care sunt plasate obiecte ¸i din care se scot ˆ ordinea invers˘ fata de cum au ın s ın a ¸ fost introduse: obiectele sunt puse unul peste altul ˆ cutie ¸i se poate avea acces ın s numai la obiectul situat ˆ vˆrful stivei. ¸ ¸ Realizarea operatiilor asupra stivelor se poate face utilizˆnd un tablou care ¸ a contine elementele ¸i un indice care indic˘ pozitia vˆrfului stivei. ¸ s a ¸ a class Stiva { final static int maxP = 100. adauga. S)) = S • esteV ida(adauga(x. ¸ ¸a ın 11. continut = new Element[maxP]. S)) = f alse • valoare(adauga(x. } static Fir vid () { return new Stiva(). elimina. ca ¸i la fire. valoare. Utilizarea a cozilor se face cu ajutorul functiilor vida. int inaltime. operatiile efectuate asupra stivelor sunt vida. Aceasta este ¸ deci interfata cozilor care are important˘ ˆ programe complexe. ˆ mod formalizat.

ınl˘ ¸ .inaltime] = x.inaltime = 0. s.156 static void facVida (Stiva s) { s. } static boolean esteVida (Stiva s) { return s. } static void supprimer (Stiva s) throws ExceptionStiva { if (esteVida (s)) throw new ExceptionStiva ("Stiva vida").continut[s. return s. } static Element valoare (Stiva s) throws ExceptionStiva { if (esteVida (s)) throw new ExceptionStiva("Stiva vida").inaltime.inaltime == maxP.continut[s. ++ s. } } unde class ExceptionStiva extends Exception { String text. public ExceptionStiva (String x) { text = x. LISTE static void adauga (Element x. } static boolean estePlina (Stiva s) { return s.inaltime == 0. } } Stivele se pot implementa atˆt cu ajutorul tablourilor (vectorilor) cˆt ¸i cu a a s ajutorul listelor simplu ˆ antuite. s.inaltime. } CAPITOLUL 11.inaltime-1]. Stiva s) throws ExceptionStiva { if (estePlina (s)) throw new ExceptionStiva("Stiva plina").

} Procedura de evaluare const˘ ˆ stivuirea rezultatelor intermediare. EVALUAREA EXPRESIILOR ARITMETICE PREFIXATE 157 11. a De exemplu. iar al treilea este este un simbol dac˘ at a a a entitatea este simbol. variabile ¸i operatii aritmetice (ne vom limita numai la + ¸ s ¸ ¸i *). atunci se plaseaz˘ ˆ stiv˘. vom utiliza un tablou ale ın c˘rui elemente sunt entit˘¸ile expresiei. pentru expresia (+ (* (+ 35 36) (+ 5 6)) (* (+ 7 8) (*9 9 ))) evaluarea decurge astfel: . s a Expresiile prefixate contin simbolurile: numere naturale. } return 1. int x. a static int calcul (char a. class Element { boolean esteOperator. stiva a ın continˆnd operatori ¸i numere. int valoare. Elementele tabloului sunt obiecte cu trei a at cˆmpuri: primul reprezint˘ natura entit˘¸ii (simbol sau num˘r). ( ¸i ). int y) { switch (a) { case ’+’: return x + y.4 Evaluarea expresiilor aritmetice prefixate Vom ilustra utilizarea stivelor printr-un program de evaluare a expresiilor aritmetice scrise sub o form˘ particular˘. +.11. ¸ a s a ¸ Se examineaz˘ succesiv entit˘¸ile expresiei dac˘ entitatea este un operator sau un a at a num˘r ¸i dac˘ vˆrful stivei este un operator. s s Pentru reprezentarea unei expresii prefixate ˆ Java. *.4. al doilea reprezint˘ a a at a a valoarea entit˘¸ii dac˘ aceasta este num˘r. Dac˘ a s a a a ın a a este un num˘r ¸i vˆrful stivei este de asemenea un num˘r. Dac˘ e1 ¸ s a ¸i e2 sunt expresii prefixate atunci (+e1 e2 ) ¸i (∗e1 e2 ) sunt expresii prefixate. Vom considera ca intr˘ri numai numere naturale. char simbol. actioneaz˘ operatorul a s a a ¸ a care precede vˆrful stivei asupra celor dou˘ numere ¸i se repet˘ operatia asupra a a s a ¸ rezultatului g˘sit. dar niciodat˘ nu va contine consecutiv numere. ˆ programare o expresie aritmetic˘ a a In a poate contine numere. case ’*’: return x * y. } Vom utiliza functiile definite pentru stiv˘ ¸i procedurile definite ˆ cele ce ¸ a s ın urmeaz˘.

} try { System. } } .println("Stiva " + x.s).valoare).valoare = calcul(op.elimina(s). op = Stiva. y.158 CAPITOLUL 11. } Stiva.nom). x. ¸ public static void main (String args[]) { Element exp[] = new Element [args. x.length .esteVida(s) || x. Stiva s) throws ExceptionStiva { Element y.valoare(s). s. for (int i = 0.valsimb.esteOperator || Stiva. } ˆ acest caz. i < u. s).equals("*")) exp[i] = new Element (true.valoare(s).length. op. } catch (ExceptionStiva x) { System. ’ ’).parseInt(s).valoare(s). este util˘ prezentarea unui program principal care utilizeaz˘ In a a aceste functii. 0.charAt(0)).adauga(x.valoare.esteOperator)) { y = Stiva.valoare(s). } return Stiva. } static int calcul (Element u[]) throws ExceptionStiva { Stiva s = new Stiva(). ++i) { String s = args[i].length]. LISTE 9 * 15 * 781 + + * + + * + 35 + * + 71 * + + 71 * + 5 + 71 * + 781 + * 781 + + * 781 + 7 + * 781 + * 15 * 781 + static void insereaza (Element x.err.println(calcul(exp)). Stiva.equals("+") || s. for (int i = 0. i < args. if (s. Stiva. else exp[i] = new Element (false. ++i) { insereaza(u[i].out.valoare. Integer.elimina(s). while (!(Stiva.

} static Lista cons (Object x.11. Totusi. next = a. prima list˘ este transformat˘ ın a a a pentru a retine rezultatul. Lista a) { return new Lista (x. } static Object head (Lista a) { if (a == null) erreur ("Head d’une liste vide. In a ¸ ¸ at Ace¸tia sunt utilizati ˆ limbajele de programare care au listele ca structuri de s ¸ ın baz˘. a a . ˆ a doua procedur˘. partajeaz˘ finalul listei rezultat cu al doilea argument. a). concat. OPERATII ASUPRA LISTELOR ¸ 159 11. return a. return a. } static Lista tail (Lista a) { if (a == null) erreur ("Tail d’une liste vide."). a a a pun o list˘ la cap˘tul celeilalte liste. cele dou˘ liste a a In a a nu sunt modificate. ea suprim˘ primul element al listei."). ˆ prima procedur˘ append. } Procedurile asupra listelor construiesc o list˘ plecˆnd de la alte dou˘ liste.5 Operatii asupra listelor ¸ ˆ aceast˘ sectiune sunt prezentati cˆ¸iva algoritmi de manipulare a listelor. Lista next. se poate remarca faptul c˘. Lista(Object x. Lista a) { info = x.5. Functia Tail este o primitiv˘ clasic˘.next. dac˘ append copiaz˘ ¸ a a a primul s˘u argument. a ¸ a a a a Tail(a) e1 e2 e3 e4 e1 e2 e3 e4 Figura 11.info.4: Suprimarea primului element din list˘ a class Lista { Object info.

Lista b) { if (a == null) return b.next != null) c = c. b)) . return a.next. while (c.info.160 a b CAPITOLUL 11. Lista b) { if (a == null) return b.5: Concatenarea a dou˘ liste prin append a static Lista append (Lista a. } } . else return adauga(a. LISTE a1 a2 append(a. c.next.next = b.6: Concatenarea a dou˘ liste prin concat a static Lista concat(Lista a. else { Lista c = a.b} a3 b1 b2 b3 b4 a1 a2 a3 Figura 11. } a b a1 a2 concat(a.b} a3 b1 b2 b3 b4 Figura 11. append (a.

Lista b) { if (a == null) return b. c). a a a a a a Noua metod˘ nReverse modific˘ argumentul s˘u. } c b e1 e2 e3 e4 a e5 nill Figura 11. a. adauga(a. } } 161 Procedura de calcul de oglindire a unei liste a const˘ ˆ construirea unei a ın liste ˆ care elementele listei a sunt ˆ ordine invers˘.5.7: Transformarea listei prin inversarea leg˘turilor a static Lista Reverse (Lista a) { if (a == null) return a. dar clasic˘. complexitatea este O(n2 ) deci p˘tratic˘. while (a != null) { Lista c = a.next.next = b.info. else { a. s a a static Lista nReverse (Lista a) { Lista b = null. OPERATII ASUPRA LISTELOR ¸ Aceast˘ ultim˘ procedur˘ se poate scrie recursiv: a a a static Lista concat (Lista a. Realizarea acestei proceduri ın ın a este un exercitiu clasic de programare asupra listelor.next = concat (a. b = a.next). cealalt˘ recursiv˘. } . } return b. ˆ timp ce Reverse nu-l modific˘ a a a ın a ci construie¸te o alt˘ list˘ pentru rezultat. null)). a = c. else return append(Reverse (a.next. una ¸ a a ¸ iterativ˘. D˘m aici dou˘ soluti.11. return a.

b). a. return a. Procedura de ad˘ugare devine ceva mai complex˘ ın a a a pentru c˘ trebuie g˘sit˘ pozitia celulei care trebuie ad˘ugat˘ dup˘ parcurgerea a a a ¸ a a a unei p˘rti a listei. Pentru o a ¸ a ın a astfel de list˘. while (b. valoarea cˆmpului inf o din prima celul˘ nu are nici o semnificatie a a a ¸ (este celula de gard˘).next != a && v > head(b. Lista a) { if (a == null) return b. a. } static Lista nReverse1 (Lista b. Cˆmpul next din ultima celul˘ contine adresa primei celule.next).next). b. a). LISTE Se poate scrie o versiune iterativ˘ a versiunii recursive. else return nReverse1(adauga(a.8: List˘ circular˘ cu gard˘ a a a static Lista insereaza (int v. Lista a) { Lista b = a.next.162 CAPITOLUL 11. b.next = adauga(v. } . } Un alt exercitiu formator const˘ ˆ a administra liste ˆ care elementele sunt ¸ a ın ın aranjate ˆ ordine cresc˘toare. a a a ¸ a a1 garda a2 a3 a4 a5 a6 Figura 11.info = head(a) + 1.info. a¸ Nu trat˘m acest exercitiu decˆt ˆ cazul listelor circulare cu gard˘.next)) b = b. gratie unei functii auxiliare a ¸ ¸ care acumuleaz˘ rezultatul ˆ a ıntr-unul din argumentele sale: static Lista nReverse (Lista a) { return nReverse1(null.

. nk ın divideEtImpera(P1 . ın s • Compunerea subsolutiilor pentru a obtine solutia problemei (subproblemei) ¸ ¸ ¸ initiale. . Sk ) combin˘ S1 . ... Pk de dimensiuni n1 .. n1 .. S) if (n ≤ n0 ) then rezolv˘ subproblema P prin tehnici elementare a else ˆ ımparte P ˆ P1 ....Capitolul 12 Algoritmi divide et impera 12. ˆ englez˘ ın a 163 . • Rezolvarea ˆ acela¸i mod (recursiv) a tuturor subproblemelor.. divideEtImpera(Pa .. s a a 1 divide-and-conquer. Sk pentru a obtine S a ¸ Exemplele tipice de aplicare a acestei metode sunt algoritmii de parcurgere a arborilor binari ¸i algoritmul de c˘utare binar˘. n. ..1 Tehnica divide et impera Divide et impera1 este o tehnic˘ de elaborare a algoritmilor care const˘ ˆ a a ın: • Descompunerea repetat˘ a problemei (subproblemei) ce trebuie rezolvat˘ ˆ a a ın subprobleme mai mici. S1 ) . Metoda poate fi descris˘ astfel: a procedure divideEtImpera(P. ¸ Descompunerea problemei (subproblemelor) se face pˆn˘ n momentul ˆ care a aˆ ın se obtin subprobleme de dimensiuni atˆt de mici ˆ at au solutie cunoscut˘ sau ¸ a ıncˆ ¸ a pot fi rezolvate prin tehnici elementare. nk .

a Presupunem c˘ divizarea problemei ˆ subprobleme ¸i compunerea solutiilor a ın s ¸ subproblemelor necesit˘ un timp de ordinul O(nk )... pasul de divizare reduce o subproblem˘ la altele de dimensiuni a mai mici. dac˘ n ≤ n0 a .164 CAPITOLUL 12. Distingem cazurile: 0 1.. = am T (n0 ) + c am−1 n bm−1 m k n b n +c a b k + cnk + nk n b k k + . Seria m i=0 bk a i este convergent˘ ¸i deci ¸irul sumelor partiale este as s ¸ convergent.2) Demonstratie: Putem presupune. + a k + nk = am cnk + c am−1 bk nk + .. dac˘ a = bk a . De ¸ aa a a asemenea.. + a i bk a = cam i=0 bk a unde am notat cnk prin c. ALGORITMI DIVIDE ET IMPERA 12. f˘r˘ a restrˆnge generalitatea. De aici rezult˘ c˘ T (n) = O(am ) = O(alogb n ) = O(nlogb a ) a a . c˘ n = bm · n0 .. dac˘ a > bk a . presupunem c˘ T (n) = c · nk dac˘ n ≤ n0 ¸i T (n) = a · T ( n ) + c · nk a a s 0 b dac˘ n > n0 . + a bm−1 0 0 = cnk am 1 + 0 m nk + (bm )k nk 0 0 bk + . ceea ce asigur˘ terminarea subprogramului recursiv.2. dac˘ n > n0 a (12. a ¸ b unde b > 1.1) Teorema 3 Dac˘ n > n0 atunci: a  O(nlogb a )  T (n) = O(nk logb n)   O(nk ) .2...2 Ordinul de complexitate Vom presupune c˘ dimensiunea ni a subproblemei i satisface relatia ni ≤ n . Atunci. Pentru n > n0 avem: a n T (n) = aT + cnk b = aT bm−1 n0 + cnk = a aT bm−2 n0 + c = a2 T bm−2 n0 = . complexitatea timp a T (n) a algoritmului divideEtImpera este dat˘ de relatia de recurent˘: a ¸ ¸a T (n) = O(1) a · T ( n ) + O(nk ) b . Astfel. a > bk . dac˘ a < bk a (12.

5. elemente mai mari ca a s ¸i x. avˆnd tabloul a[].quicksort Se bazeaz˘ pe metoda interschimb˘rii. class QuickSort { static int x[]={3. Dup˘ aceasta.5. se aplic˘ urm˘torul algoritm: ¸ a a a 1. EXEMPLE 165 2. se interschimb˘ ai cu aj a 5. Avem T (n) = O(am ( ba )m ) = O(bkm ) = O(nk ).3}. k 12. Rezult˘ c˘ am = bkm = cnk ¸i de aici T (n) = O(nk m) = O(nk logb n). tabloul a[] va fi partitionat ˆ 2 astfel.4. 3. Astfel.3 Exemple Dintre problemele clasice care se pot rezolva prin metoda divide et impera mention˘m: ¸ a • c˘utare binar˘ a a • sortare rapid˘ (quickSort) a • problema turnurilor din Hanoi • sortare prin interclasare .2. se scaneaz˘ tabloul a[] la stˆnga lui x pˆn˘ cˆnd se g˘se¸te un element ai > x a a a a a a s 3. 4 pˆn˘ cˆnd scan˘rile se vor ˆ alni pe undeva la mijlocul a s a a a a ıntˆ tabloului. pˆn˘ cˆnd aceste sub¸iruri sunt suficient de mici (se reduc la a a a s un singur element). se aplic˘ acela¸i proces sub¸irurilor de la stˆnga ¸i de la s a a s s a s dreapta lui x.4.2. a a s 3.1.8. a = bk . a < bk .Merge sort • dreptunghi de arie maxim˘ ˆ placa cu g˘uri.3. la stˆnga In ın a lui x se vor g˘si elemente mai mici ca ¸i x.12.1 Sortare prin partitionare . se repet˘ pa¸ii 2.6. ˆ a din nou. public static void main(String[]args) { .3. ˆ acel moment.3. interschimbarea se face a a ıns˘ pe distante mai mari. etc a ın a 12. la dreapta. se scaneaz˘ tabloul la dreapta lui x pˆn˘ cˆnd se g˘se¸te un element aj < x a a a a a s 4. se alege la ˆ amplare un element x al tabloului ıntˆ 2.

}//main static void quickSort(int st.3. s a Din Teorema 3 pentru a = 2.4.i<x. b = 2 ¸i k = 1 rezult˘ c˘ ordinul de complexitate s a a al algoritmului MergeSort este O(n log2 n) unde n este dimensiunea vectorului initial supus sort˘rii. if(i<=j) { temp=a[i]. ˆ practic˘ (ˆ medie)! ın a ın 12.8. for(int i=0. x. Vectorii de lungime 1 sunt evident ¸ ¸ considerati sortati.MergeSort Ideea este de a utiliza interclasarea ˆ etapa de asamblare a solutiilor.int a[]) { int i=st. a[j]=temp. while (a[j]>x) --j.j.a). temp.4..2. ALGORITMI DIVIDE ET IMPERA quickSort(0.166 CAPITOLUL 12. ¸ a class MergeSort { static int x[]={3.) }//class Aceasta metoda are complexitatea n log n. Faza de asamblare se face ˆ timpul ın ın O(m1 + m2 ) unde n1 ¸i n2 sunt lungimile celor doi vectori care se interclaseaz˘. a[i]=a[j].dr.length-1. } } while(i<=j)..5. i++. if(st<j) quickSort(st.x. do { while (a[i]<x) i++.2.1. ¸ ¸ Pasul de divizare se face ˆ timpul O(1).out.print(x[i]+" ").3.length. public static void main(String[]args) .2 Sortare prin interclasare . ın ¸ ˆ urma rezolv˘rii recursive a subproblemelor rezult˘ vectori ordonati ¸i prin In a a ¸ s interclasarea lor obtinem vectorul initial sortat. j--. j=dr.5. x=a[(st+dr)/2].int dr. }// quickSort(.i++) System. if(i<dr) quickSort(i.a).x).6.3}.

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

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

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

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

. EXEMPLE 171 System. public static void main(String[] args) throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System. c=new int[MAX_NR_DISCURI]. etc.tijaDestinatie).tijaDestinatie. b=new int[MAX_NR_DISCURI].tijaIntermediara). .nc.nb.tijaIntermediara. else { solutiaHanoi(nrDiscuri-1.io.tijaSursa.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. System. class HanoiDisc { static int nrMutare=0. static int discMutat.*. solutiaHanoi(nrDiscuri-1. discuri pe tija A.tijaSursa. . static final int MAX_NR_DISCURI=9.out. static int na.3. static int[] a=new int[MAX_NR_DISCURI].println(++nrMutare + " Disc 1 " + tijaSursa + " ==> "+ tijaDestinatie). // na = nr.out.12..in)).

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

3.print(c[i]).out.<=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) 12..out.print(b[i]). EXEMPLE 173 switch (tijaDestinatie) { case ’A’: a[(++na)-1]=discMutat.i<na.print(") ").out. break.print(") ").print(a[i]).out. System.12. for(int i=0.i++) System..i++) System.5 ˆ Injum˘t˘¸ire repetat˘ a at a .out. } }// mutaDiscul() static void afisareDiscuri() { System. System.out. for(int i=0. System.i<nc. for(int i=0. break. case ’C’: c[(++nc)-1]=discMutat.out.out.print(" B(").i<nb.i++) System. }// afisareDiscuri() }// class Numar discuri[1<=.print(") "). System.out.print(" A("). case ’B’: b[(++nb)-1]=discMutat. System.3.print(" C(").

. ¸irul r˘mas ˆ a ¸ s a ın acel moment se ˆ ımparte ˆ dou˘ sub¸iruri cu num˘r egal elemente (dac˘ num˘rul de ın a s a a a elemente este impar.. ALGORITMI DIVIDE ET IMPERA Se consider˘ un ¸ir de numere naturale x1 . // nu folosesc d_0 . una dup˘ alta.dr=n.. . class Injumatatire1 { static int n=10.’S’.println(st+" . 10 a ramas! 6 . m=(st+dr)/2. if((dr-st+1)%2==1) impar=true. unde di ∈ {S. d2 . boolean impar.’S’.out.’D’. 10 elimin D . x2 . D} au urm˘toarea semnificatie: la pasul i... } }//main }//class 1 . if(impar) dr--. elementul situat la mijloc se elimin˘) ¸i se elimin˘ jum˘tatea a s a a din partea stˆng˘ a ¸irului dac˘ di = S (dac˘ di = D se elimin˘ jum˘tatea din a a s a a a a partea drept˘). else impar=false.. if(d[i]==’S’) // elimin jumatatea stanga { st=m+1.. while(st<dr) { System.m. "+dr+" elimin "+d[i])..println(st+" . 10 elimin S 6 . "+dr+" a ramas! \n").i=1. } else // elimin jumatatea dreapta { dr=m... Se cere acest element. static char[] d={’X’.. System.. xn ¸i o succesiune de decizii a s s d1 .’S’}. ! public static void main(String[]args) { int st=1..174 CAPITOLUL 12.. pˆn˘ cˆnd r˘mˆne un singur a ın a a a a a a element.out.... a Deciziile se aplic˘ ˆ ordine. } i++. .

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

ALGORITMI DIVIDE ET IMPERA .176 SSS SSD SDS SDD DSS DSD DDS DDD --> --> --> --> --> --> --> --> 10 9 7 6 5 4 2 1 CAPITOLUL 12.

// matricea de adiacenta static int[] color. // infinit static final int WHITE=0. muchii public static void main(String[] args) throws IOException { int i. // predecesor static int[] d. GRAY=1. // inceput coada = prima pozitie ocupata din care scot ! static int sc.nodd.in"))). static int[][] a.nods. // sfarsit coada = prima pozitie libera pe care voi pune ! static int n. // nod_sursa.*. nod_destinatie StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("bfs. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("bfs.io. // distante catre sursa static int[] q. 177 .m.out"))).Capitolul 13 Algoritmi BFS-Lee 13.1 Prezentare general˘ a import java. // varfuri. // pentru bfs static int[] p. class BFS // nodurile sunt de la 1 la n { // distanta minima dintre nod "sursa" si nod "dest" static final int oo=0x7fffffff. // coada static int ic. BLACK=2.k.j.

} bfs(nods. System.nval.nval. st.nodd)..nextToken(). sc=0. d=new int[n+1].println(). a[j][i]=1. color[v]=GRAY.nextToken().k<=m. st.println("Distanta("+nods+".print("drum : ").nextToken(). drum(nodd). m=(int)st.nextToken().nextToken(). CAPITOLUL 13. // coada : scot <-. st.icoada.. out. } static int dincoada() . } static void incoada(int v) { q[sc++]=v.nval. System.close(). for(k=1.out. a[i][j]=1. ALGORITMI BFS-LEE n=(int)st. j=(int)st. p=new int[n+1].nextToken().nval. st.178 st. a=new int[n+1][n+1].out. i=(int)st. }//main static void videzcoada() { ic=0.out. nods=(int)st.nval. nodd=(int)st. System.nval.k++) { st. color=new int[n+1]."+nodd+") = "+d[nodd]).introduc } static boolean coadaEsteVida() { return (sc==ic).scoada <-. q=new int[m+1].

u<=n. for(u=1. }//while }//bfs // am ajuns la nod_destinatie . 179 // Cauta nodurile albe v adiacente cu nodul u si pun v in coada for(v=1. } } }//for color[u]=BLACK. if(color[fin]!=WHITE) break. } static void bfs(int start. while(!coadaEsteVida()) { u=dincoada(). v<=n. v++) { if(a[u][v]==1) // v in Ad[u] { if(color[v]==WHITE) // neparcurs deja { color[v]=GRAY. } color[start]=GRAY. return v. d[start]=0. videzcoada().˘ 13. ies din for incoada(v). incoada(start). d[u]=oo. PREZENTARE GENERALA { int v=q[ic++]. p[v]=u. if(color[fin]!=WHITE) break. // optimizare. color[v]=BLACK. u++) { color[u]=WHITE.1. d[v]=d[u]+1. int fin) { int u. v.

ˆ matrice au marcat cu R locul ˆ care se afl˘ locuinta lui Romeo. vertical˘ sau dia a agonale). ıntˆ Ei au analizat harta ora¸ului ¸i au reprezentat-o sub forma unei matrice cu s s n linii ¸i m coloane. } }//class /* 9 12 2 8 2 5 1 2 2 3 3 1 3 4 4 5 5 6 6 4 7 8 8 9 9 7 7 4 */ Distanta(2.2 Probleme rezolvate 13. System. ın a ¸ Ei se pot deplasa numai prin zonele care sunt marcate cu spatiu. De a s asemenea. din pozitia ¸ ¸ curent˘ ˆ oricare dintre cele 8 pozitii ˆ a ın ¸ ınvecinate (pe orizontal˘.2. iar ın ın a ¸ cu J locul ˆ care se afl˘ locuinta Julietei. .OJI2004 clasa a X-a s ˆ ultima ecranizare a celebrei piese shakespeariene Romeo ¸i Julieta tr˘iesc In s a ˆ ıntr-un ora¸ modern.1 Romeo ¸i Julieta . ALGORITMI BFS-LEE static void drum(int u) // nod_sursa ---> nod_destinatie { if(p[u]!=0) drum(p[u]).out.8) = 4 drum : 2 3 4 7 8 13. ˆ s a s ınvata a Intro secvent˘ tulbur˘toare sunt prezentate fr˘mˆt˘rile interioare ale celor doi eroi ¸a a a aa ˆ ıncercˆnd f˘r˘ succes s˘ scrie un program care s˘ determine un punct optim de a aa a a ˆ alnire.180 CAPITOLUL 13. comunic˘ prin e-mail ¸i chiar ˆ ¸˘ s˘ programeze.print(u+" "). ˆ matrice fiind marcate cu spatiu zonele prin care se poate s ın ¸ trece (str˘zi lipsite de pericole) ¸i cu X zonele prin care nu se poate trece.

num˘rul liniei. Dac˘ exist˘ mai multe solutii. ei au hot˘rˆt c˘ trebuie s˘ aleag˘ un punct de ˆ alnire ˆ care atˆt Romeo. a • Pentru datele de test exist˘ ˆ a ıntotdeauna solutie. ¸ a Datele de ie¸ire s Fi¸ierul de ie¸ire rj. M < 101 • Liniile ¸i coloanele matricei sunt numerotate ˆ s ıncepˆnd cu 1. PROBLEME REZOLVATE 181 Cum lui Romeo nu ˆ place s˘ a¸tepte ¸i nici s˘ se lase a¸teptat n-ar fi tocmai ıi a s s a s bine. separate prin spatiu. programul a a ın s a a ¸ trebuie s˘ determine o solutie pentru care timpul este minim.out 4 4 4 . avˆnd semnificatia: a ¸ a ¸ − x y reprezint˘ punctul de ˆ alnire (x . J. Si cum probabil exist˘ mai multe puncte de ˆ alnire a a ıntˆ ¸ a ıntˆ posibile. plecˆnd de acas˘. care reprezint˘ num˘rul de linii ¸i a a s respectiv de coloane ale matricei. ei vor s˘ ˆ aleag˘ pe cel ˆ care timpul necesar pentru a ajunge la punctul a ıl a ın de ˆ alnire este minim. aa a a a ıntˆ ın a cˆt ¸i Julieta s˘ poat˘ ajunge ˆ acela¸i timp. X sau spatiu) reprezentˆnd matricea. ¸ − pe fiecare dintre urm˘toarele N linii se afl˘ M caractere (care pot fi doar a a R.13. Fiindc˘ la ˆ alniri a s a a ın s a a a ıntˆ amˆndoi vin ˆ a ıntr-un suflet.num˘rul coloanei). ei estimeaz˘ timpul necesar pentru a ajunge la ˆ alnire a ıntˆ prin num˘rul de elemente din matrice care constituie drumul cel mai scurt de acas˘ a a pˆn˘ la punctul de ˆ alnire.in 5 8 XXR XXX X X X J X X X XX XXX XXXX rj. ıntˆ Cerint˘ ¸a Scrieti un program care s˘ determine o pozitie pe hart˘ la care Romeo ¸i ¸ a ¸ a s Julieta pot s˘ ajung˘ ˆ acela¸i timp. a ¸ Datele de intrare Fi¸ierul de intrare rj. ¸ Exemple rj. ıntˆ Restrictii ¸i preciz˘ri ¸ s a • 1 < N.2.in contine: s ¸ − pe prima linie numerele naturale N M . y . a ıntˆ a a − tmin este timpul minim ˆ care Romeo (respectiv Julieta) ajunge la punctul ın de ˆ alnire.out va contine o singur˘ linie pe care sunt scrise trei s s ¸ a numere naturale separate prin cˆte un spatiu tmin x y.

(4. Dac˘ o a a pozitie are valoare 3. a ın a s iar cele marcate cu 2 reprezint˘ toate locurile ˆ care se poate afla Julieta dup˘ a ın a cel mult k pa¸i. dac˘ acestea au valoarea 0 sau 3. atunci a ¸ a ne vom opri ¸i k reprezint˘ momentul minim de timp dup˘ care cei doi se ˆ alnesc. valoarea 3 ˆ pozitia ın ¸ ın a ¸ ın ¸ ˆ care se afl˘ Julieta initial ¸i valoarea 0 ˆ rest. ın ıntˆ Ordinul de complexitate al operatiei de scriere a rezultatului este O(1). In a Timp maxim de executare: 1 secund˘/test a Indicatii de rezolvare * ¸ Mihai Stroe.182 CAPITOLUL 13. (4. are valoarea 2. ALGORITMI BFS-LEE Explicatie: ¸ Traseul lui Romeo poate fi: (1. (3.4). (4. ¸ a ıntˆ La prima vedere s-ar p˘rea c˘ num˘rul k nu reprezint˘ momentul de timp a a a a minim la care cei doi se ˆ alnesc.5). 14/4 aprilie 2004 Problema se rezolv˘ folosind algoritmul lui Lee. a ıntˆ ˆ plus 4. ¸ ˆ concluzie. ordinul de complexitate al algoritmului de rezolvare a acestei In probleme este O(kM N ).2).4). Timpul necesar Julietei pentru a ajunge de acas˘ la punctul de ˆ alnire este deasemenea 4. Dac˘ k nu reprezint˘ momentul de timp minim la care cei doi se s a a ˆ alnesc ˆ ıntˆ ınseamn˘ c˘ acesta a fost determinat mai devreme ¸i algoritmul s-a oprit a a s deja.4). Analiza complexit˘¸ii at Ordinul de complexitate al operatiei de citire a datelor de intrare este O(M N ). a ıntˆ Traseul Julietei poate fi: (3. (2.1). (4. ¸ Vom folosi o matrice D ˆ care vom pune valoarea 1 peste tot pe unde nu se ın poate trece.3). ¸ Ordinul de complexitate al acestui algoritm este O(kM N ). La acela¸i pas k vom mai parcurge matricea o dat˘ ¸i ın ¸ a s as ˆ pozitiile vecine celor care au valoarea 3 vom pune valoarea 3. a Se aplic˘ acest algoritm folosind ca puncte de start pozitia lui Romeo ¸i a ¸ s pozitia Julietei.3). unde k reprezint˘ a momentul ˆ care cei doi se ˆ alnesc. valoarea 2 ˆ pozitia ˆ care se afl˘ Romeo initial. Dac˘ la pasul k pozitia vecin˘ uneia care are valoarea 3. ˆ ¸ ınseamn˘ c˘ la un moment de timp anterior Julieta se putea a a afla ˆ pozitia respectiv˘. Vom demonstra c˘ algoritmul este corect prin ıntˆ a metoda reducerii la absurd. s a a ıntˆ iar pozitia care are valoare 2 reprezint˘ locul ˆ alnirii. . Ginfo nr. Timpul necesar lui Romeo pentru a ajunge de acas˘ la punctul de ˆ alnire este 4. este punctul cel mai apropiat de ei cu aceast˘ proprietate. dac˘ acestea au ın ¸ a valoarea 0. Pentru aceasta avem ˆ vedere c˘ pozitiile marcate ın a ¸ cu 2 reprezint˘ toate locurile ˆ care se poate afla Romeo dup˘ cel mult k pa¸i. ın a ¸ s ın La fiecare pas k vom parcurge aceast˘ matrice ¸i ˆ pozitiile vecine celor care a s ın ¸ au valoarea 2 vom pune valoarea 2.

static int[] qi=new int[5000]. st. static int[] qj=new int[5000].t2. xr=new int[m+2][n+2].j<=n.j++) if(s. int i.ij=0.i++) { s=br.jr=0.jj=0.i<=m. st. class RJ { static final int zid=10000. BufferedReader br=new BufferedReader(new FileReader("rj.io.13. static int[][] xr. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("rj.j.xj. static int m.charAt(j-1)==’R’) {ir=i.nval. // coada sau coada circulara mai bine ! // coada // ic=inceput coada public static void main(String[] args) throws IOException { long t1. // matrice bordata cu zid ! // matrice bordata cu zid ! br. xr[i][j]=1.charAt(j-1)==’X’) xr[i][j]=xj[i][j]=zid.readLine(). t1=System.nval.n. jr=j. String s. xj=new int[m+2][n+2].nextToken(). for(j=1. // citeste CRLF !!! for(i=1. jmin. n=(int)st.*. sc.out"))). imin.in")).currentTimeMillis().nextToken().} // zid! . tmin. StreamTokenizer st=new StreamTokenizer(br). m=(int)st. else if(s. static int ic.2.readLine().ir=0. PROBLEME REZOLVATE Rezolvare detaliat˘ a 183 Codul surs˘ * a import java.

xj[i][j]=1. // coada vida. ic++.} // S .println(tmin+" "+imin+" "+jmin). imin=jmin=0.} // NE if(x[i-1][j-1]==0) {x[i-1][j-1]=t+1. }// main() static void fill(int[][] x. qj[sc]=jj.i<=m.j). // timp if(x[i-1][j]==0) {x[i-1][j]=t+1. out. t2=System.} // NV if(x[i+1][j]==0) {x[i+1][j]=t+1. sc++. qi[sc]=ir. jmin=j. sc++. // scot din coada fill(xj. int j) { int t=x[i][j].i. qj[sc]=j. qi[sc]=i-1. // (ij.currentTimeMillis(). j=qj[ic].println("Timp = "+(t2-t1)).j<=n. } tmin=10000. jj=j. fill(xr. for(i=1.charAt(j-1)==’J’) {ij=i.j<=n+1.jr) --> coada // scot din coada ic=sc=0.j++) if(xr[i][j]==xj[i][j]) if(xj[i][j]!=0) // pot exista pozitii ramase izolate ! if(xr[i][j]<tmin) {tmin=xr[i][j].184 CAPITOLUL 13.} } for(i=0. qj[sc]=j+1. System. sc++. qi[sc]=ij.j).int i.close(). } // coada vida.} // N if(x[i-1][j+1]==0) {x[i-1][j+1]=t+1. j=qj[ic]. qj[sc]=jr. // E si V for(j=0.i++) xr[i][0]=xr[i][n+1]=xj[i][0]=xj[i][n+1]=zid. // (ir.i. ic++.} out. qi[sc]=i-1.i++) for(j=1. sc++. while(ic!=sc) { i=qi[ic].i<=m+1. qj[sc]=j. qi[sc]=i+1. qi[sc]=i-1.out. // N si S ic=sc=0. qj[sc]=j-1. sc++. sc++. imin=i.j++) xr[0][j]=xr[m+1][j]=xj[0][j]=xj[m+1][j]=zid. ALGORITMI BFS-LEE else if(s.jj) --> coada while(ic!=sc) { i=qi[ic].

a ¸ reprezentˆnd num˘rul de pa¸i pe care trebuie s˘-i efectueze robotul la fiecare a a s a comand˘. • Pe urm˘toarele N linii se afl˘ cˆte N numere naturale. directia de deplasare. de coordonate (1.} // V 13. qi[sc]=i+1. pe care cultiv˘ cartofi.} // SV {x[i][j+1]=t+1.. qj[sc]=j-1. N ).} // SE {x[i+1][j-1]=t+1. qi[sc]=i. Fiecare comand˘ specific˘ directia de deplasare (sud sau est) ¸i num˘rul a a a ¸ s a de p˘trate pe care le parcurge ˆ directia respectiv˘. a a a ¸ Cerint˘ ¸a Scrieti un program care s˘ determine cantitatea maxim˘ de cartofi pe care ¸ a a o poate culege robotul. cartela pe care se afl˘ programul s-a deteriorat ¸i unitatea de a a s citire a robotului nu mai poate distinge directia de deplasare.OJI2006 clasa a X-a Fermierul Ion detine un teren de form˘ p˘trat˘. ˆ artit ˆ N × N p˘trate ¸ a a a ımp˘ ¸ ın a de latur˘ unitate.} // E qj[sc]=j-1.) }// class 185 {x[i+1][j+1]=t+1.. ˆ ipoteza ˆ care Ion specific˘ manual.2. Se va determina ¸i traseul pe care se obtine a ¸ a s ¸ recolta maxim˘. a • Pe linia N + 3 se afl˘ K numerele naturale C1 . a Din p˘cate.in are urm˘toarea structur˘: s a a • Pe linia 1 se afl˘ num˘rul natural N . {x[i][j-1]=t+1. PROBLEME REZOLVATE if(x[i+1][j+1]==0) if(x[i+1][j-1]==0) if(x[i][j+1]==0) if(x[i][j-1]==0) }// fil(. qj[sc]=j+1. a Datele de intrare Fi¸ierul de intrare sudest. . pentru fiecare ın ın a comand˘. qi[sc]=i+1. sc++. Robotul strˆnge recolta doar a ın ¸ a a din p˘tratele ˆ care se opre¸te ˆ a ın s ıntre dou˘ comenzi. sc++..2. qj[sc]=j+1.2 Sudest .CK .13. Pentru recoltarea cartofilor fermierul a a folose¸te un robot special proiectat ˆ acest scop. Fermierul Ion trebuie s a a a s˘ introduc˘ manual. directia urmat˘ de robot. a .. reprezentˆnd dimensiunea parcelei de a a a teren. separate prin spatii. pentru fiecare comand˘. de coordonate (N. qi[sc]=i. separate prin spatii. a a a ¸ reprezentˆnd cantitatea de cartofi din fiecare p˘trat unitate. 1) ¸i trebuie s˘ ajung˘ ˆ p˘tratul din dreapta a s a a ın a jos. ci numai num˘rul ¸ a de pa¸i pe care trebuie s˘-i fac˘ robotul la fiecare comand˘. Robotul porne¸te din p˘tratul s ın s a din stˆnga sus. a a • Pe linia N + 2 se afl˘ un num˘r natural K reprezentˆnd num˘rul de comenzi a a a a aflate pe cartela magnetic˘. Traseul robotului este programat prin memorarea unor comenzi pe o cartel˘ a magnetic˘.. sc++. sc++.

a Exemplu sudest. • Pentru fiecare set de date de intrare se garanteaz˘ c˘ exist˘ cel putin un a a a ¸ traseu.. ˆ ordine. ALGORITMI BFS-LEE Datele de ie¸ire s Fi¸ierul de iesire sudest. cˆte un p˘trat unitate pe o linie. iar ultimul va avea coordonatele N N . Coordonatele scrise pe a a a aceea¸i linie vor fi separate printr-un spatiu. 1) ¸i din a a a s a s cel de sosire (N. a a ın coordonatele p˘tratelor unitate ce constituie traseul pentru care se obtine cantia ¸ tate maxim˘ de cartofi. ... pentru cantitate maxim˘ recoltat˘ ¸i a as traseu corect se acord˘ 100%.out 29 11 31 51 61 65 66 Explicatii ¸ Un alt traseu posibil este: 11 13 15 25 65 66 dar costul s˘u este 1 + 1 + 4 + 1 + 5 + 10 = 22 a Timp maxim de executie/test: 1 secund˘ ¸ a . N ). Restrictii ¸i preciz˘ri ¸ s a • 5 ≤ N ≤ 100 • 2≤K ≤2∗N −2 • 1 ≤ C1 . Primul p˘trat de pe traseu va avea s ¸ a coordonatele 11. • Se consider˘ c˘ robotul strˆnge recolta ¸i din p˘tratul de plecare (1.in 6 121041 133511 2 2 1 2 1 10 453926 113201 10 2 4 6 5 10 5 22141 sudest.out va contine pe prima linie cantitatea maxim˘ s ¸ a de cartofi recoltat˘ de robot. Dac˘ sunt mai multe trasee a pe care se obtine o cantitate maxim˘ de cartofi recoltat˘ se va afi¸a unul dintre ¸ a a s acestea. • Pentru determinarea corect˘ a cantit˘¸ii maxime recoltate se acord˘ 50% a at a din punctajul alocat testului respectiv. CK ≤ 10 • Cantitatea de cartofi dintr-un p˘trat de teren este num˘r natural ˆ a a ıntre 0 ¸i s 100. Pe urm˘toarele K + 1 linii vor fi scrise.186 CAPITOLUL 13.

C[i][j] = cantitatea maxim˘ de cartofi culeas˘ pe un a a traseu ce porne¸te din (1. static int[][] C=new int[Nmax][Nmax]. .2. a Mai exact. static int[] Move=new int[2*Nmax]. pas comenzile . . static int N..*. . a ¸ a a a a ¸ ¸i marchez ˆ matricea P pozitia ˆ care am ajuns cu indicele mut˘rii curente). static int[][] A=new int[Nmax][Nmax]. PROBLEME REZOLVATE Indicatii de rezolvare * ¸ solutia comisiei ¸ Reprezentarea informatiilor ¸ • N . In Codul surs˘ * a Variant˘ dup˘ solutia oficial˘: a a ¸ a import java. s ın ¸ ın a ˆ mod similar procedez pentru o mutare spre est. static int[][] P=new int[Nmax][Nmax]. j). static PrintWriter out.memoreaz˘ cantitatea de produs a 187 • C[N max][N max]. class Sudest1 { static StreamTokenizer st. retin noua cantitate. K.num˘rul de linii a • K .13. La fiecare mutare marchez pozitiile ˆ care pot s a ¸ ın ajunge la mutarea respectiv˘. 1) ¸i se termin˘ ˆ (i. . respectˆnd conditiile s s a ın a ¸ problemei • P [N max][N max].P [i][j] = pasul la care am ajuns ˆ pozitia i.num˘rul de comenzi a • A[N max][N max]. ¸ a a ˆ caz afirmativ.memoreaz˘ cele K comenzi a Parcurg ¸irul celor k mut˘ri.. verific dac˘ ˆ acest caz obtin o cantitate de cartofi mai In a ın ¸ mare decˆt cea obtinut˘ pˆn˘ la momentul curent (dac˘ da. // // // // A[i][j]=cantitatea de cartofi C[i][j]=cantitatea maxima . j culegˆnd ın ¸ a o cantitate maxim˘ de cartofi a • M ove[2 ∗ N max].io. static final int Nmax=101. parcurg toate pozitiile ˆ care am putut ajunge la pasul precedent ¸ ın (cele marcate ˆ matricea P corespunz˘tor cu num˘rul pasului precedent) ¸i pentru ın a a s fiecare pozitie verific dac˘ la pasul curent pot s˘ execut mutarea la sud.

j.} st. P[1][1]=0. i<=N.i++) for(j=1.i<=N. i++) for(j=1.cmd.} }// read_data() static void init() { int i. C[1][1]=A[1][1]. solve(). cmd++) for(i=1. init()..) static void read_data() throws IOException { int i.} static void solve() { int i.cmd<=K..j)) // SUD if(C[i][j]+A[i+Move[cmd]][j]>C[i+Move[cmd]][j]) { P[i+Move[cmd]][j]=cmd. j<=N. out=new PrintWriter(new BufferedWriter(new FileWriter("sudest. cmd<=K. st. C[i+Move[cmd]][j]=C[i][j]+A[i+Move[cmd]][j]. read_data().int y) {return 1<=x && 1<=y && x<=N && y<=N.nval. for(i=1.nextToken(). } .j++) { st. }// main(.out"))).cmd++) { st. Move[cmd]=(int)st.j<=N. ALGORITMI BFS-LEE public static void main(String[] args) throws IOException { st=new StreamTokenizer( new BufferedReader(new FileReader("sudest.nval.} }// init() static boolean posibil(int x.i++) for(j=1.nval. A[i][j]=(int)st.nextToken().j. for(i=1.188 CAPITOLUL 13. N=(int)st.nval.j++) {C[i][j]=-1. P[i][j]=255.i<=N. j++) if(P[i][j]==cmd-1) { if(posibil(i+Move[cmd]. K=(int)st.nextToken().j. for(cmd=1.j<=N.nextToken(). for(cmd=1.in"))). cmd.

println(x+" "+y).13. out. static PrintWriter out. else { gasit=false. } }// if out. out. k. C[i][j+Move[cmd]]=C[i][j]+A[i][j+Move[cmd]].N..println(C[N][N]).y.y-Move[pas].2. PROBLEME REZOLVATE if(posibil(i. static int n.*.pas-1).close(). }// solve() 189 static void drum(int x.) }// class Variant˘ folosind coad˘: a a import java..K).pas-1). boolean gasit.nn --> 1. int y. int pas) { int i.n*n pozitii in matrice ! class Sudest2 { static StreamTokenizer st.....y)) if(C[x-Move[pas]][y]==C[x][y]-A[x][y] && P[x-Move[pas]][y]==pas-1) { drum(x-Move[pas]... // 11. } if(!gasit) if(posibil(x-Move[pas]. . drum(N. out.io.println(x+" "+y).y-Move[pas])) if(C[x][y-Move[pas]]==C[x][y]-A[x][y] && P[x][y-Move[pas]]==pas-1) { drum(x. if(x==1 && y==1) out..j+Move[cmd])) // EST if(C[i][j]+A[i][j+Move[cmd]]>C[i][j+Move[cmd]]) { P[i][j+Move[cmd]]=cmd. if(posibil(x. } }// else }// drum(. .println("1 1"). static final int nmax=101. gasit=true.

k=(int)st.cmd. sc=1. j=(q[ic]-1)%n+1. st.j<=n. p[1][1]=0.i<=n.jj. int[] q=new int[qmax].cmd.j++) {c[i][j]=-1.nextToken().nval.. // int[][] p=new int[nmax][nmax].sc.cmd<=k.} }// read_data() static void init() { int i. init(). q[ic]=1.nextToken(). out=new PrintWriter(new BufferedWriter(new FileWriter("sudest.i<=n. // ultimul din coada la distanta 1 de [1][1] for(cmd=1. // int ic. for(cmd=1.} st.nval.) static void read_data() throws IOException { int i.j.i++) for(j=1. pozitia anterioara optima comenzile public static void main(String[] args) throws IOException { st=new StreamTokenizer(new BufferedReader(new FileReader("sudest.nextToken().j++) { st.nval. // scot din coada // propag catre Sud . read_data(). // coada a[i][j]=cantitatea de cartofi c[i][j]=cantitatea maxima .nval. for(i=1.in"))). cmd++) { while(ic!=scv) { i=(q[ic]-1)/n+1. move[cmd]=(int)st.out"))).i++) for(j=1. // pozitia [1][1] --> q scv=1.} }// init() static void solve() { int i. // int[][] c=new int[nmax][nmax].nextToken().cmd++) { st. c[1][1]=a[1][1]. a[i][j]=(int)st. }// main(. n=(int)st. ic=0. for(i=1. // int[] move=new int[2*nmax].scv..190 static static static static static static CAPITOLUL 13. ALGORITMI BFS-LEE int[][] a=new int[nmax][nmax].j. p[i][j]=255.qmax=100.j. solve(). cmd<=k.ii... ic=(ic+1)%qmax.j<=n.

Acesta are o structur˘ de matrice dreptunghiular˘ cu M a a linii ¸i N coloane.(p[i][j]-1)%n+1). out. j + 1). spre est ˆ (i. j) deplasarea s a a ¸ spre nord presupune trecerea ˆ pozitia (i − 1.println(c[n][n]).) }// class 191 13. int j) { if(i*j==0) return. }// solve() static void drum(int i. q[sc]=(ii-1)*n+jj. out.println(i+" "+j). q[sc]=(ii-1)*n+jj.close(). sud ¸i vest (dac˘ aceste camere exist˘). PROBLEME REZOLVATE ii=i+move[cmd]. sc=(sc+1)%qmax. spre sud ˆ ın ¸ ın ın . if((jj>=1)&&(jj<=n)) if(c[i][j]+a[ii][jj]>c[ii][jj]) { c[ii][jj]=c[i][j]+a[ii][jj].. p[ii][jj]=(i-1)*n+j. Pentru pozitia (i.ONI2003 clasa a X-a Sunteti un participant la Olimpiada National˘ de Informatic˘. sc=(sc+1)%qmax.13. }// for out. jj=j. if((ii>=1)&&(ii<=n)) if(c[i][j]+a[ii][jj]>c[ii][jj]) { c[ii][jj]=c[i][j]+a[ii][jj]. }// drum(. drum((p[i][j]-1)/n+1. j). // pun in coada } // propag catre Est jj=j+move[cmd]. drum(n..n). p[ii][jj]=(i-1)*n+j. Una dintre ele este as a at vizitarea unui muzeu.2. din orice camer˘ se poate ajunge ˆ camerele vecine pe directiile s a ın ¸ nord. ˆ pro¸ ¸ a a In gramul olimpiadei intr˘ ¸i cˆteva activit˘¸ii de divertisment.3 Muzeu . ii=i.2. // pun in coada } }// while scv=sc. est.

. Restrictii ¸i preciz˘ri ¸ s a Exemplu: 2 ≤ N ≤ 50 . V reprezentˆnd deplas˘ri a a spre Nord. 1) ˆ (M. al matricei s ¸ a care reprezint˘ muzeul. ˆ a Intr-o camer˘ marcat˘ a a cu num˘rul i (i > 0) se poate intra gratuit. 1) a a ¸ ın ¸ ¸ matricei) ¸i doriti s˘ ajungeti la ie¸irea situat˘ ˆ coltul de Sud-Est (pozitia (M. E. ALGORITMI BFS-LEE Acest muzeu are cˆteva reguli speciale. orice camer˘ a a ¸ a a cu num˘rul i (i > 0) ofer˘ spre vˆnzare un bilet cu num˘rul i. separate printr-un spatiu. astfel ˆ at s˘ ajungeti ˆ camera (M. reprezentˆnd costurile biletelor s a 1. N ) sunt marcate cu num˘rul 0. separate prin spatii. num˘rul de linii. el este valabil ˆ toate camerele marcate cu num˘rul respectiv. determinati o strategie de parcurgere a a ¸ camerelor. Est. j) ¸i spre vest ˆ (i. Dac˘ ıncˆ a ¸ ın a a ¸ a exist˘ mai multe variante. 3. a a ın pe a doua linie num˘rul minim de mut˘ri L efectuate dintr-o camer˘ ˆ a a a ıntr-o camer˘ vecin˘. dar nu se poate ie¸i din ea decˆt a s a dac˘ ar˘tati supraveghetorului un bilet cu num˘rul i.out veti afi¸a: In s ¸ s pe prima linie suma minim˘ necesar˘ pentru a ajunge din (1.10 ˆ aceast˘ ordine. o dat˘ cump˘rat a a a a a a acest bilet. ın a Date de ie¸ire s ˆ fi¸ierul muzeu. Linia M + 2 s ¸ contine 10 numere ˆ ¸ ıntregi ˆ ıntre 0 ¸i 10000 inclusiv.192 (i + 1. . Biletele ın a pot avea preturi diferite. alegeti una ˆ care este parcurs un num˘r minim de a ¸ ın a camere (pentru a cˆ?tiga timp ¸i pentru a avea mai mult timp pentru vizitarea a s integral˘ a muzeului). ¸ s a Cerint˘ ¸a Cunoscˆndu-se structura muzeului. s ın CAPITOLUL 13. dar un bilet cu num˘rul i va avea acela¸i pret ˆ toate ¸ a s ¸ ın camerele ˆ care este oferit spre vˆnzare. a a ın pe a treia linie L caractere din multimea N . 1) ¸i (M. ın a Dumneavoastr˘ intrati ˆ muzeu prin coltul de Nord-Vest (pozitia (1. respectiv de coloane. Pozitiile (1. Urm˘toarele M linii contin structura muzeului. Fiecare camer˘ este marcat˘ cu un a a a num˘r ˆ a ıntre 0 ¸i 10 inclusiv.in contine dou˘ numere ˆ s ¸ a ıntregi M ¸i N . 2. N ) pl˘tind cˆt mai putin. 1) ˆ (M. O dat˘ ajuns acolo primiti un bilet gratuit care v˘ permite s˘ vizitati a ¸ a a ¸ tot muzeul. fiecare a a ¸ contine N numere ˆ ¸ ıntregi ˆ ıntre 0 ¸i 10 inclusiv. Din fericire. Mai multe camere pot fi marcate cu acela¸i num˘r.. Sud sau Vest. N ). j − 1). a Date de intrare Prima linie a fi¸ierului de intrare muzeu. s s a Camerele marcate cu num˘rul 0 pot fi vizitate gratuit. pentru a ajunge din (1. S. N ) s ¸ a ¸ s a ın ¸ ¸ a matricei). N ).

. 1) la (M. ordinul de complexitate devine a a O(M · N ). problema determin˘rii celui mai scurt drum de la a (1. am folosit initial conventia s ¸ ¸ respectiv˘ pentru a u¸ura ˆ a s ınelegerea. ˆ care 0 s ın reprezint˘ o camer˘ pentru care se pl˘te¸te biletul (sau nu e nevoie de bilet) ¸i 1 a a a s s reprezint˘ o camer˘ pentru care nu se cump˘ra bilet (deci ˆ care nu se intr˘). biletul 10) este 210 = 1024. ¸ Fie C num˘rul de numere de marcaj diferite din matrice. a ¸ a aceasta nu mai este abordat˘. a Indicatii de rezolvare * ¸ 193 Mihai Stroe..octombrie 2003 Se observ˘ c˘ num˘rul variantelor de cump˘rare a biletelor (cump˘r / nu a a a a a cump˘r biletul 1. g˘sirea drumului minim cu algoritmul lui Lee are complexitatea a O(M · N ).2. a a a ın a Pentru o astfel de matrice. Se alege solutia de cost minim ¸i.. Eventual. a Se genereaz˘ fiecare din aceste variante. . problema determin˘rii celui mai scurt drum se poate rezolva ¸i pe a s matricea initial˘. Ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O(M · N · 2C ).out 56 12 000002 9 011143 EEEEESSSS 010000 015100 000100 1000 5 7 100 12 1000 1000 1000 1000 1000 Timp maxim de executare: 1 secund˘/test. cum algoritmul ¸ a t a a lui Lee este folosit de obicei pentru o matrice cu 0 ¸i 1. Analiza complexit˘¸ii at Operatia de citire a datelor are ordinul de complexitate O(M · N ). dac˘ a a a a la un moment dat exist˘ o solutie cu un cost mai bun decˆt al variantei curente.in muzeu. ˆ caz de egalitate. GInfo nr. cea cu num˘r minim de ¸ s ın a camere. N ) se rezolv˘ cu algoritmul lui Lee. Considerˆnd 2C o constant˘. Pentru o astfel de variant˘. a Se rezolv˘ aceast˘ problem˘ pentru toate cele 1024 variante. a Evident. . Algoritmul functioneaz˘ datorit˘ restrictiei impuse pentru C (cel mult 10 ¸ a a ¸ numere de marcaj). ¸inˆnd cont la fiecare pas de biletele cump˘rate. biletul 2. Operatia de scriere a solutiei are ordinul de complexitate O(M · N ) pentru ¸ ¸ cazul cel mai defavorabil. se cala a culeaz˘ costul ¸i se transform˘ matricea initial˘ ˆ a s a ¸ a ıntr-o matrice cu 0 ¸i 1. Pentru fiecare dintre a cele 2C variante.13. PROBLEME REZOLVATE muzeu. 13/6 .

j) d=new int[51][51]. sc.nextToken(). . jos=3.j<=n.i++) { bilete(i).1) --> (i. static int[] qi=new int[qdim].i++) for(j=1.io. } for(i=1.nval.nval. static int ic. m=(int)st. dreapta=2. class Muzeu { static final int qdim=200.nval. for(i=1.j++) { st.i++) {st. static int[] cb=new int[11]. // dimensiuni muzeu int ncmin.i<=1023.cmin=50*50*10000/2.n.194 Codul surs˘ a CAPITOLUL 13. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("10-muzeu. n=(int)st. cb[i]=(int)st. // directii pentru "intoarcere" dsol=new int[51][51]. // costul (1. int[][] int[][] int[][] int[][] x=new int[51][51]. // cost curent/minim boolean amAjuns.nval.j. } amBilet[0]=true. // muzeu c=new int[51][51].i<=10. // costuri bilete static boolean[] amBilet=new boolean[11].j) // ic=inceput coada public static void main(String[] args) throws IOException { int i. st.nextToken(). static static static static static static static static int m.nextToken(). static int[] qj=new int[qdim]. stanga=4.in"))).i<=m.j) // coada pentru j din pozitia (i. x[i][j]=(int)st. // coada pentru i din pozitia (i. st.*. // 0 ==> gratuit for(i=0.nextToken(). // nr camere minim int cc. ALGORITMI BFS-LEE import java. // dimensiune coada circulara static final int sus=1.

. if(!amAjuns) continue. // curat "numai" traseul ! ic=sc=0. // schimbare "adresa" . ic=(ic+1)%qdim. ncmin=c[m][n]. for(j=1. amAjuns=false. afisSolutia(). if(cc>cmin) continue. } else // costuri egale if(c[m][n]<ncmin) { ncmin=c[m][n]. sc=(sc+1)%qdim. PROBLEME REZOLVATE cc=0.j<=n. } } d=dsol.. j=qj[ic]. if(cc<cmin) { cmin=cc. for(j=1. else amBilet[j]=false..13.i<=m.j<=10.j++) if(amBilet[j]) cc+=cb[j].1) --> coada circulara ! c[1][1]=1. if(cc>cmin) continue... qj[sc]=1.) 195 static void matriceCosturi() { int i. copieDirectii().j<=10.j++) c[i][j]=0..j. if(amAjuns) break. // cost 1 pentru pozitia (1.2. i/=2. vecini(i.i++) for(j=1. // coada vida qi[sc]=1. copieDirectii().j++) { if(i%2==1) amBilet[j]=true. for(i=1.1) (pentru marcaj!) while(ic!=sc) // coada nevida { i=qi[ic]. } }//matriceCosturi() static void copieDirectii() . } }// bilete(. matriceCosturi(). ("pointer"!) . // (1. }// main() static void bilete(int i) { int j.j).

// "timp" = nr camere parcurse if((i-1>=1)&&(c[i-1][j]==0)&&amBilet[x[i-1][j]]) // N { c[i-1][j]=t+1. return. return.) static void afisSolutia() throws IOException { int i. qi[sc]=i. qj[sc]=j. d[i][j-1]=dreapta. d[i+1][j]=sus. qj[sc]=j-1. if((i+1==m)&&(j==n)) {amAjuns=true. if((i==m)&&(j+1==n)) {amAjuns=true.j. return.} } if((i+1<=m)&&(c[i+1][j]==0)&&amBilet[x[i+1][j]])// S { c[i+1][j]=t+1. int j) { int t=c[i][j]. d[i][j+1]=stanga.j.. }// copieDirectii() static void vecini(int i.196 { CAPITOLUL 13.} } if((j-1>=1)&&(c[i][j-1]==0)&&amBilet[x[i][j-1]]) // V { c[i][j-1]=t+1. sc=(sc+1)%qdim.out"))).j++) dsol[i][j]=d[i][j]. PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter("muzeu.} } }// vecini(. . ALGORITMI BFS-LEE int i. qi[sc]=i+1.. sc=(sc+1)%qdim. for(i=1. qi[sc]=i.i<=m.} } if((j+1<=n)&&(c[i][j+1]==0)&&amBilet[x[i][j+1]]) // E { c[i][j+1]=t+1. sc=(sc+1)%qdim. qi[sc]=i-1. if((i==m)&&(j-1==n)) {amAjuns=true.i++) for(j=1. qj[sc]=j+1.j<=n. if((i-1==m)&&(j==n)) {amAjuns=true. sc=(sc+1)%qdim. return. qj[sc]=j. d[i-1][j]=jos.

j=1. j++. // (m.1) while((i!=1)||(j!=1)) // folosesc "c" care nu mai e necesara ! if(d[i][j]==sus) { c[i-1][j]=jos.. i=1.} else if(c[i][j]==stanga) {out.println("Eroare la traseu .} else if(c[i][j]==dreapta) {out. out.. s a ın .2. la un moment dat.1)!").close(). (1. } out.println("Eroare la traseu . Pe plas˘. 197 i=m. } else System.} else if(c[i][j]==jos) {out. } else if(d[i][j]==jos) { c[i+1][j]=sus..out. i++.4 P˘ianjen ONI2005 clasa a X-a a Un p˘ianjen a ¸esut o plas˘.println(ncmin-1). PROBLEME REZOLVATE out.print("E").n)-->(1.13. ˆ noduri de coordonate cunoscute. (m. j=n.n) while((i!=m)||(j!=n)) { if(c[i][j]==sus) {out. oricare dou˘ noduri vecine (pe orizontal˘ sau vertical˘) ın a ¸ a a a erau unite prin segmente de plas˘ de lungime 1. // (1.print("V"). j--. j++. } else if(d[i][j]==stanga) { c[i][j-1]=dreapta.print("N").println(cmin).print("S"). i--.1)--(m. i--. i++. }//afisSolutia() }// class 13. j--.out. ˆ care nodurile sunt dispuse sub forma unui a t a ın caroiaj cu m linii (numerotate de la 0 la m − 1) ¸i n coloane (numerotate de la 0 la s n − 1) ca ˆ figur˘. ˆ timp unele portiuni ale plasei sa In ¸ au deteriorat.} else System..} else if(d[i][j]==dreapta) { c[i][j+1]=stanga.1) --> (m.n)!"). devenind nesigure.n) --> (1.2. Initial. se g˘sesc p˘ianjenul a a a ¸i o musc˘.

n ≤ 140 • 1 ≤ k ≤ 2 ∗ (m ∗ n − m − n + 1) • Lungimea drumului minim este cel mult 15000 • Pentru datele de test exist˘ ˆ a ıntotdeauna solutie. a ¸ reprezentnd linia ¸i respectiv coloana nodului ˆ care se afl˘ initial p˘ianjenul. s ın a ¸ a − pe linia a treia dou˘ numere naturale lm cm separate printr-un spatiu. Pe urm˘toarele min + 1 linii sunt scrise nodurile a a prin care trece p˘ianjenul. Dac˘ problema are mai ¸ a multe solutii. se va afi¸a una singur˘. a ¸ reprezentˆnd num˘rul de linii ¸i respectiv num˘rul de coloane ale plasei. separate printr-un spatiu. un num˘r natural k. Datele de intrare Fi¸ierul de intrare paianjen.out va contine pe prima linie un num˘r natural s s ¸ a min reprezentˆnd lungimea drumului minim parcurs de p˘ianjen. cˆte un nod pe o linie. Pentru fiecare nod sunt scrise a a linia ¸i coloana pe care se afl˘. a ¸ reprezentˆnd linia ¸i respectiv coloana pe care se afl˘ initial musca. cˆte patru valori naturale l1 c1 l2 c2. separate printr-un spatu. De a ¸ a asemenea.198 0 1 2 3 4 5 6 CAPITOLUL 13. ALGORITMI BFS-LEE 0 1 2 3 4 5 6 7 8 pozitie paianzen pozitie musca Cerint˘ ¸a S˘ se determine lungimea celui mai scurt traseu pe care trebuie s˘-l parcurg˘ a a a p˘ianjenul. reprezentˆnd coordonatele capetelor celor k portiuni a ¸ a ¸ de plas˘ deteriorate (linia ¸i apoi coloana pentru fiecare cap˘t). a − pe fiecare dintre urm˘toarele k linii. separate printr-un spatiu. pentru a ajunge la musc˘.in contine: s ¸ − pe prima linie dou˘ numere naturale m n. s a ¸ Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ m. a s a Datele de ie¸ire s Fi¸ierul de ie¸ire paianjen. folosind doar portiunile sigure ale plasei. a a separate prin cˆte un spatiu. exprimat ˆ a a ın num˘r de segmente de lungime 1. a a s a − pe a doua linie dou˘ numere naturale lp cp. ¸ s a . reprezentˆnd num˘rul de portiuni a a a ¸ de plas˘ deteriorate. se cere un astfel de traseu. a s a ¸ − pe linia a patra.

bitul 3 . cu oprirea c˘ut˘rii ˆ jurul nodului curent ˆ a a ın ın momentul detect˘rii unui nod de pas mai mic cu o unitate decˆt cel al nodului a a curent. s fiecare element reprezentˆnd un nod al plasei. Traseul optim este desenat cu linie groas˘. PROBLEME REZOLVATE 199 • Portiunile nesigure sunt specificate ˆ fi¸ierul de intrare ˆ ¸ ın s ıntr-o ordine oarecare.algoritm Lee).1 secunde ¸ a s pentru Linux. j] este a 0 dac˘ nodul (i. j) nu este atins. respectiv o valoare pozitiv˘ reprezentˆnd pasul a a a la care a ajuns paianjenul ˆ drumul lui spre musc˘.2.13. bitul 1 este 1 dac˘ se poate deplasa la dreapta. ¸ s a de asemenea un algoritm de tip BF. j] va codifica pe patru biti a ¸ directiile ˆ care se poate face deplasarea din punctul (i. cm] va ın a contine lungimea drumului minim.out 8 23 22 32 42 52 62 63 73 74 Explicatie ¸ Problema corespunde figurii de mai sus. unde B[i. De asemenea. j): bitul 0 este 1 dac˘ ¸ ın a p˘ianjenul se poate deplasa ˆ sus. a a Drumul minim al p˘ianjenului se retine ˆ a ¸ ıntr-o alt˘ matrice B. utilizˆnd. oricare dou˘ portiuni nesigure verticale se pot intersecta cel a a ¸ mult ˆ ıntr-un cap˘t.ˆ jos. Deci elementul B[lm. Oricare dou˘ portiuni nesigure orizontale se pot intersecta cel mult ˆ a ¸ ıntr-un cap˘t. a ın a bitul 2 . . ”Gh. s ¸ Exemplu paianjen. a • Se acord˘ 30% din punctaj pentru determinarea lungimii drumului minim a ¸i 100% pentru rezolvarea ambelor cerinte. Carmen Popescu. a iar portiunile nesigure sunt desenate punctat. A[i.la stˆnga. C. ın a Rezolvarea se bazeaz˘ pe parcurgerea matriciei ¸i retinerea nodurilor parcurse a s ¸ ˆ ıntr-o structur˘ de date de tip coad˘ (parcurgere BF .in 97 23 74 8 2425 2333 3031 3335 4454 6465 6575 7273 paianjen. ¸ Timp maxim de executie/test: 1 secund˘ pentru Windows ¸i 0. Laz˘r” Sibiu a Plasa de p˘ianjen se memoreaz˘ ˆ a a ıntr-o matrice A cu M linii ¸i N coloane. Indicatii de rezolvare * ¸ prof. N. ¸ Reconstituirea drumului minim se face pornind de la pozitia mu¸tei.

afisSolutia(). foarte multe fire rupte ¸ a caz particular. // directii pentru intoarcere de la musca! static static static static static static int m. iar ˆ ¸ ¸ ımbun˘t˘¸it a at maxim 30 de puncte. static int[][] p=new int[140][140]. ALGORITMI BFS-LEE TESTE Obs dimensiune mic˘. dimensiune mare traseu ˆ spiral˘ solutie unic˘ ın a a traseu scurt. fire putine rupte a ¸ solutie unic˘. citescDate(). traseul pe frontier˘ a o ”barier˘” pe mijlocul tablei. t1=System. // dimensiune coada circulara static final int sus=1. // dimensiunile plasei int lp. dreapta=2. // costul ajungerii in (i. // pozitie musca(lin. cu o ”fant˘” a a # 0 1 2 3 4 5 6 7 8 9 m 10 10 2 140 131 100 100 138 140 138 n 8 8 2 140 131 130 7 138 140 138 k 11 46 0 20 2000 12771 23 9381 38365 273 min 16 74 2 278 103 12999 15 9050 555 274 O solutie backtracking simplu obtine maxim 20 de puncte.col) int[] qi=new int[qdim].j) static int[][] d=new int[140][140].col) int lm.j) int[] qj=new int[qdim].t2.currentTimeMillis().currentTimeMillis().io. t2=System.*.n. sc. Codul surs˘ a import java. stanga=8. greu de g˘sit cu backtracking a multe fire rupte.cp. // coada pentru i din pozitia (i. nici un fir rupt fire putine rupte. drum ˆ ”serpentine” ın firele interioare rupte. dimensiune mare ¸ multe fire rupte. jos=4. . // inceput/sfarsit coada public static void main(String[] args) throws IOException { long t1. // pozitie paianjen(lin.j) int ic. matriceCosturi(). // plasa static int[][] c=new int[140][140]. // coada pentru j din pozitia (i.200 CAPITOLUL 13.cm. // test 3 eroare date lm=144 ??? class Paianjen4 // coada circulara (altfel trebuie dimensiune mare !) { // traseu fara recursivitate (altfel depaseste stiva !) static final int qdim=200.

c2). lp=(int)st.nval.i<m..nval.j2. n=(int)st.i++) { p[i][j1]^=jos. st.l1. k. cp=(int)st. i1=min(l1. // 0 pe directia dreapta for(j=j1+1.c1.in"))). i2=max(l1..2. st. j2=max(c1.nextToken().nval. for(i=0.i<=i2-1. // 0 pe directia jos p[i][j1]^=sus. .l1=(int)st.nextToken().out.c1=(int)st.nextToken().nextToken(). st.j<=j2-1. st.c2=(int)st.nextToken().nval.nval.k++) { st.nextToken(). // 0 pe directia sus } p[i2][j1]^=sus.nval.k<=nrSegmenteDeteriorate. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("paianjen. } else if(i1==i2) // ruptura orizontala { p[i1][j1]^=dreapta. st.l2). st.nextToken().. // sau .i.l2=(int)st. lm=(int)st.c2).j++) p[i][j]=0xF.nval. !!! for(i=i1+1. st.i2.nval. st.l2.nextToken().j++) . st. p[i1][j1]-=jos. nrSegmenteDeteriorate=(int)st.i++) for(j=0.13.c2.j.nval. }// main() static void citescDate() throws IOException { int nrSegmenteDeteriorate. cm=(int)st.nval. PROBLEME REZOLVATE System. int i1.nval.. st. m=(int)st. j1=min(c1. 201 // 1111=toate firele ! if(j1==j2) // ruptura verticala { p[i1][j1]^=jos.j<n.l2).println("TIME = "+(t2-t1)+" millisec ").j1.nextToken(). for(k=1.nextToken().nextToken().

ALGORITMI BFS-LEE // 0 pe directia stanga } else System. int j) { int t=c[i][j]. sc=(sc+1)%qdim.. sc=(sc+1)%qdim. p[i1][j]^=stanga.dreapta)) // E { c[i][j+1]=t+1. qj[sc]=cp.out. if((j+1<=n-1)&&(c[i][j+1]==0)&&ok(i. qi[sc]=i-1. sc=(sc+1)%qdim. } // N } p[i1][j2]^=stanga. // (lp. ic=sc=0.. ic=(ic+1)%qdim. // a ajuns deja la musca ! }// while }//matriceCosturi() static void fill(int i.println("Date de intrare . if(c[lm][cm]!=0) break. . eronate !"). d[i-1][j]=jos. // timp ! if((i-1>=0)&&(c[i-1][j]==0)&&ok(i. }// for k }//citescDate() static void matriceCosturi() { int i. j=qj[ic]. d[i][j+1]=stanga.cp) --> coada ! c[lp][cp]=1.j.j. qj[sc]=j. // cost 1 pentru pozitie paianjen (pentru marcaj!) while(ic!=sc) // coada nevida { i=qi[ic]. // coada vida qi[sc]=lp. qj[sc]=j+1.j). CAPITOLUL 13. fill(i. qi[sc]=i.sus)) { c[i-1][j]=t+1.202 { p[i1][j]^=dreapta.j.

13. } static int max(int a. out. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("paianjen. PROBLEME REZOLVATE } if((i+1<=m-1)&&(c[i+1][j]==0)&&ok(i. int dir) { if((p[i][j]&dir)!=0) return true. qi[sc]=i+1. }// ok(.) static int min(int a. else return b.out"))). else return b. i=lm. sc=(sc+1)%qdim. } if((j-1>=0)&&(c[i][j-1]==0)&&ok(i.j. else return false. int b) { if(a>b) return a.2. 203 .jos)) // S { c[i+1][j]=t+1.stanga)) // V { c[i][j-1]=t+1. } }// fill(.. } static void afisSolutia() throws IOException { int i. qj[sc]=j-1. qj[sc]=j.j.println(c[lm][cm]-1)..) static boolean ok(int i.. sc=(sc+1)%qdim.j.. qi[sc]=i. int b) { if(a<b) return a. d[i+1][j]=sus. d[i][j-1]=dreapta. int j.

fluxm. else if(d[i][j]==stanga) { c[i][j-1]=dreapta. sc. // nr noduri. while((i!=lm)||(j!=cm)) { out.i. nr arce static int[][] c=new int[MAX_NODES+1][MAX_NODES+1]. else if(c[i][j]==dreapta) j++. else System.println("Eroare la traseu . static final int MAX_NODES=10. j=cp. i++.println(i+" "+j). BLACK=2. // coada public static void main(String[] args) throws IOException { int s.println(i+" "+j). // pentru bfs static int[] p=new int[MAX_NODES+1]. drum crestere) static int ic. } else if(d[i][j]==jos) { c[i+1][j]=sus. }//afisSolutia() }// class 13. else if(c[i][j]==jos) i++. // pozitia pentru musca ! out.5 Algoritmul Edmonds-Karp import java.. i--. while((i!=lp)||(j!=cp)) // folosesc matricea if(d[i][j]==sus) { c[i-1][j]=jos. GRAY=1. ALGORITMI BFS-LEE j=cm. } j--.. // flux static int[] color=new int[MAX_NODES+1].out.2.close(). // fluxm=flux_maxim StreamTokenizer st=new StreamTokenizer( . class FluxMaxim { static final int WHITE=0.j.io. // capacitati static int[][] f=new int [MAX_NODES+1][MAX_NODES+1]..} else if(d[i][j]==dreapta) { c[i][j+1]=stanga. else if(c[i][j]==stanga) j--. sfarsit coada static int[] q=new int[MAX_NODES+2]..println("Eroare la traseu . static final int oo=0x7fffffff.k. c care nu mai e necesara ! j++. m. // inceput coada. i=lp. // predecesor (ptr.*.204 CAPITOLUL 13. } out. } !"). if(c[i][j]==sus) i--.out. else System. !").t. static int n.

maxf = 0.nval.0)+"\t").close(). p[u]!=-1. i++) for(j=1. u=p[u]) { f[p[u]][u]+=min. j=(int)st. st. i=(int)st. for(k=1.i++) { for(j=1.nval. j++) f[i][j]=0.nval. f[u][p[u]]-=min. s=(int)st. for(i=1.print(maxim(f[i][j].nextToken(). n=(int)st.in"))). . p[u]!=-1.nextToken(). st. st.t)) { // Determina cantitatea cu care se mareste fluxul min=oo. // mareste fluxul pe drumul gasit while(bfs(s.nval. t=(int)st.k++) { st. } fluxm=fluxMax(s.println().c[p[u]][u]-f[p[u]][u]). j.nval.nextToken().nval. // Cat timp exista drum de crestere a fluxului (in graful rezidual).out. st.k<=m. } out.13.t).j++) System.out"))).nextToken(). u.nextToken().out. st.2. }// main() static int fluxMax(int s. int t) { int i. min. System.nval.println("\nfluxMax("+s+". st. j<=n.i<=n. i<=n.out. // sau f[u][p[u]]=-f[p[u]][u]. for(i=1. out.nextToken(). c[i][j]=(int)st.nextToken().j<=n. m=(int)st.print(fluxm)."+t+") = "+fluxm+" :"). System. for(u=t. u=p[u]) min=minim(min. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("fluxMaxim. PROBLEME REZOLVATE 205 new BufferedReader(new FileReader("fluxMaxim. // Mareste fluxul pe drumul gasit for(u=t.

}// fluxMax(. u<=n.print("drum : ").. v++) if(color[v]==WHITE && ((c[u][v]-f[u][v])>0)) { incoada(v). boolean gasitt=false.} } if(gasitt) break... }//while return gasitt.) // Nu mai exista drum de crestere a fluxului ==> Gata !!! System. v<=n.. break.println("Nu mai exista drum de crestere a fluxului !!!"). System. int u.) . p[v]=u. } ic=sc=0.206 } CAPITOLUL 13.out. System.. // Cauta nodurile albe v adiacente cu nodul u si pune v in coada // cand capacitatea reziduala a arcului (u. // afism(f). p[s]=-1. }// while(. v. while(ic!=sc) { u=dincoada(). int t) // s=sursa t=destinatie { // System.out.out.println("bfs "+s+" "+t+" flux curent :"). drum(t).out.println(" min="+min+" maxf="+maxf+"\n"). if(v==t) { gasitt=true. return maxf. ALGORITMI BFS-LEE maxf += min. p[u]=-1. u++) { color[u]=WHITE.. // coada vida incoada(s).v) este pozitiva for(v=1.) static boolean bfs(int s. for(u=1. }// bfs(.

j++) System. int y) { return (x>y) ? x : y.j<=n. PROBLEME REZOLVATE 207 static void drum(int u) { if(p[u]!=-1) drum(p[u]).print(a[i][j]+"\t").i<=n.6) = 23 : 0 12 11 0 0 0 0 0 0 12 0 0 .out..j.print(u+" ").2.) static int minim(int x.out. }// afism(.out. } static void incoada(int u) { q[sc++]=u. for(i=1. System. } static int maxim(int x. return u... color[u]=GRAY. color[u]=BLACK. int y) { return (x<y) ? x : y. }// drum(.13.println().. } }// class /* 6 10 1 6 1 2 16 1 3 13 2 3 4 2 4 12 3 2 10 3 5 14 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.println(). System.i++) { for(j=1. } // System.) static void afism(int[][] a) { int i. } static int dincoada() { int u=q[ic++].out.

2. daca exista mai multe solutii trebuie determinata doar una dintre acestea. respectiv cerc. ALGORITMI BFS-LEE 11 0 0 0 0 19 4 0 13. numarul benzilor colorate de pe cerculete si patratele este cuprins intre 1 si 10.TXT 3 2 1 1 1 1 1 . reprezentand numerele de ordine ale unui patratel. Au decis ca un cerculet poate fi amplasat pe un patratel daca exista cel putin o culoare care apare pe ambele. cerculetele sunt identificate prin numere cuprinse intre 1 si m. Ei doresc sa formeze perechi din care fac parte un cerculet si un patratel astfel incat sa se obtina cat mai multe perechi. iar urmatoarele b numere reprezinta codurile celor b culori. Urmatoarea linie contine numarul m al cerculetelor. Pe fiecare dintre urmatoarele n linii sunt descrise benzile corespunzatoare unui patratel. Numerele de pe o linie vor fi separate prin cte un spatiu. patratelele sunt identificate prin numere cuprinse intre 1 si n. Patratelele si cerculetele vor fi descrise in ordinea data de numarul lor de ordine. separate printr-un spatiu. iar urmatoarele b numere reprezinta codurile celor b culori. Restrictii si precizari: numarul patratelelor este cuprins intre 1 si 100.TXT OUTPUT.Descrierea problemei: Doi elfi au pus pe o masa n patratele si m cerculete. Date de iesire: Fisierul de iesire trebuie sa contina pe prima linie numarul k al perechilor formate. Date de intrare: Fisierul de intrare de intrare contine pe prima linie numarul n al patratelelor. care formeaza o pereche. Primul numar de pe o astfel de linie este numarul b al benzilor. Fiecare dintre urmatoarele k va contine cate doua numere. Unul a ales patratelele si celalalt cerculetele si au desenat pe ele mai multe benzi colorate. Pe fiecare dintre urmatoarele m linii sunt descrise benzile corespunzatoare unui cerculet. \ .6 Cuplaj maxim /* Culori . 1 \ 1 2 3 2 / . numarul cerculetelor este cuprins intre 1 si 100. Apoi au inceput sa se joace cu patratelele si cerculetele. Primul numar de pe o astfel de linie este numarul b al benzilor. Exemplu INPUT.208 4 3 9 4 6 20 5 4 7 5 6 4 */ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0 CAPITOLUL 13. un patratel sau un cerc nu poate face parte din mai mult decat o pereche.

nval.nextToken().. .nval. ic. // u=0 ==> v=1. f.nextToken().io.13.. scrie(fluxMax(0. cc = culoare cerc static int[] color. p. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("CuplajMaximCulori.n+m+1=t / / / / / / secunde/test 209 */ import java.nextToken(). st.i<=n. }// main() static void citire() throws IOException { int i. static final int oo=0x7fffffff. . n=(int)st..n class CuplajMaximCulori // u=1.. // predecesor.j. static int[][] c. // capacitati.n sau n+m+1(=t) static final int WHITE=0..2. 2 ..nval. sc. nc=(int)st. m. cc=new boolean[m+1][11].nval... for(k=1. GRAY=1. flux static boolean[][] cp. coada public static void main(String[] args) throws IOException { citire(). cp=new boolean[n+1][11].5 s=0 . static int n.nextToken(). nc=(int)st.i++) { st.. BLACK=2. q. for(i=1. 4 Timp de executie: 0.in")))..n ==> v=n+1. 3 .k<=nc. PROBLEME REZOLVATE 1 4 2 1 2 1 3 1 2 3 3 4 4 ..i++) { st. m=(int)st.nc. cc.n+m { // u=n+1. for(k=1. cp[i][j]=true..2. } } st.k++) { st.2.n+m ==> v=1. j=(int)st.k++) .n+m+1)). // cp = culoare patratel. for(i=1.k.i<=m.nval.k<=nc...nextToken(). capacitati().2 \ 3 --.*..

color=new int[n+m+2].j.j++) c[j+n][n+m+1]=1. while(bfs(s. } } }// citire() static void capacitati() { int i. } . q=new int[n+m+2].j++) if(cc[j][ic]) c[i][j+n]=1.ic++) if(cp[i][ic]) for(j=1.min. // sau f[u][p[u]]=-f[p[u]][u].210 { CAPITOLUL 13. j=(int)st.c[p[u]][u]-f[p[u]][u]).p[u]!=-1.j<=m. for(i=0.nextToken().u=p[u]) { f[p[u]][u]+=min. for(u=t.maxf=0. cc[i][j]=true.i<=n. } for(j=1.j<=m.t)) { min=oo.i++) { c[0][i]=1.j.u. }// capacitati() static int fluxMax(int s. int t) { int i.jc. c=new int[n+m+2][n+m+2]. for(i=1.j<=n+m+1. p=new int[n+m+2].p[u]!=-1. ALGORITMI BFS-LEE st. f[u][p[u]]-=min.ic<=10.u=p[u]) min=minim(min. for(u=t.i++) for(j=0. for(ic=1.nval.ic.i<=n+m+1. f=new int[n+m+2][n+m+2].j++) f[i][j]=0.

PROBLEME REZOLVATE maxf+=min. // incoada(v).u++) {color[u]=WHITE.v++) if((color[v]==WHITE)&&((c[u][v]-f[u][v])>0)) { q[sc++]=v.) return maxf. }// fluxMax(.u<=n+m+1. if(u==0) { for(v=1. int t) { int u.2.v++) if((color[v]==WHITE)&&((c[u][v]-f[u][v])>0)) { q[sc++]=v.v<=n+m. p[u]=-1.. p[v]=u. } } else if(u<=n) { for(v=n+1. // incoada(v). // s --> coada p[s]=-1. while(ic!=sc) { u=q[ic++]. for(u=0. color[s]=GRAY.. if(v==t) {gasitt=true. boolean gasitt=false. // incoada(v).13. color[u]=BLACK.v<=n.v--) if((color[v]==WHITE)&&((c[u][v]-f[u][v])>0)) { q[sc++]=v. v. color[v]=GRAY. q[sc++]=s. break. p[v]=u.} ic=sc=0. color[v]=GRAY.. }// while(. color[v]=GRAY.v>=1. p[v]=u.. } } else { for(v=n+m+1.} } 211 .) static boolean bfs(int s.

212 CAPITOLUL 13.j++) if(f[i][j+n]>0) { out.j<=m. }// bfs() static int minim(int x. for (i=1. }// scrie(. out. // din while ! } }// while() return gasitt.. ALGORITMI BFS-LEE if(gasitt) break. } out. } static int maxim(int x. int y) { return (x<y) ? x : y.out"))).println(i+" "+j). int y) { return (x>y) ? x : y..) }// class . break. } static void scrie(int fluxm) throws IOException { int i.j.close(). PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("CuplajMaximCulori.i++) if(f[0][i]>0) for(j=1.i<=n.println(fluxm).

Se ordoneaz˘ elementele multimii C ¸i se verific˘ dac˘ un element ˆ a ¸ s a a ındepline¸te s conditia de apartenent˘ la multimea S. atunci ¸i S ′ este o solutie. ¸ ¸a ¸ 213 . a a ¸ O problem˘ poate fi rezolvat˘ prin tehnica (metoda) Greedy dac˘ ˆ a a a ındepline¸te s proprietatea: dac˘ S este o solutie. el este introdus ˆ multimea S.1 Metoda greedy Metoda Greedy are ˆ vedere rezolvarea unor probleme de optim ˆ care ın ın optimul global se determin˘ din estim˘ri succesive ale optimului local. optimul global. Se pleac˘ de la solutia vid˘ pentru multimea S ¸i se ia pe rˆnd cˆte un a ¸ a s a a element din multimea C. Dac˘ se demonstreaz˘ c˘ a ın a a a succesiunea de optimuri locale conduce la optimul global. se cere s˘ se determine ¸ ¸ a o submultime S. ın ¸ 2. initial se presupune c˘ S este multimea vid˘ a ¸ ¸ a ¸ a ¸i se adaug˘ succesiv elemente din C ˆ S. (solutia problemei) care ˆ ¸ ¸ ındepline¸te anumite conditii. a a Metoda Greedy se aplic˘ urm˘torului tip de problem˘: dintr-o multime de a a a ¸ elemente C (candidati la construirea solutiei problemei). a ¸ a ın s ¸ Pornind de la aceast˘ conditie. ˆ general. Deoarece s ¸ este posibil s˘ existe mai multe solutii se va alege solutia care maximizeaz˘ sau a ¸ ¸ a minimizeaz˘ o anumit˘ functie obiectiv. Succesiunea s a ın a de optimuri locale nu asigur˘. atunci metoda Greedy este aplicabil˘ cu succes.Capitolul 14 Metoda optimului local greedy 14. ajungˆnd la un optim local. iar S ′ este inclus˘ ˆ S. Dac˘ elementul ales ˆ ¸ a ındepline¸te conditia de optim s ¸ local. a Exist˘ urm˘toarele variante ale metodei Greedy: a a 1.

) ¸ ¸ a a • o functie care verific˘ dac˘ o anumit˘ multime de candidati constituie o ¸ a a a ¸ ¸ solutie posibil˘. c˘ut˘m o solutie posibil˘ care s˘ a a ¸ a a optimizeze valoarea functiei obiectiv. METODA OPTIMULUI LOCAL . multimea de a a a a ¸ candidati selectati este fezabil˘. a problemei ¸ a a a • o functie care verific˘ dac˘ o multime de candidati este fezabil˘. Dac˘ algoritmul a a ¸ ¸ a a greedy functioneaz˘ corect. De fiecare dat˘ cˆnd l˘rgim multimea candidatilor selectati. Dac˘. ultimul candidat ad˘ugat va r˘mˆne de acum ¸ ¸ a a a a ˆ ıncolo ˆ ea. etc.214 CAPITOLUL 14. multimea candidatilor selectati este vid˘. prima solutie g˘sit˘ va fi considerat˘ solutie optim˘ a ¸ a ¸ a a a ¸ a problemei. conform functiei de selectie. a problemei a a a • o functie de selectie care indic˘ la orice moment care este cel mai promit˘tor ¸ ¸ a ¸a dintre candidatii ˆ a nefolositi ¸ ınc˘ ¸ • o functie obiectiv care d˘ valoarea unei solutii (timpul necesar execut˘rii ¸ a ¸ a tuturor lucr˘rilor ˆ a ıntr-o anumit˘ ordine. ¸ ¸ ¸ ¸ a La fiecare pas. ˆ cele mai multe situatii de acest fel avem: In ¸ • o multime de candidati (lucr˘ri de executat. ¸ Un algoritm greedy construie¸te solutia pas cu pas. elimin˘m ultimul candidat ad˘ugat. dup˘ ad˘ugare.GREEDY 14. lungimea drumului pe care l-am a g˘sit.2 Algoritmi greedy Algoritmii greedy sunt ˆ general simpli ¸i sunt folositi la rezolvarea unor ın s ¸ probleme de optimizare. nu neap˘rat optim˘. nu neap˘rat optim˘. ˆ ıncerc˘m s˘ ad˘ug˘m la aceast˘ multime pe cel mai promitator a a a a a ¸ ¸˘ candidat. adic˘ dac˘ ¸ a a ¸ ¸ a a a este posibil s˘ complet˘m aceast˘ multime astfel ˆ at s˘ obtinem o solutie a a a ¸ ıncˆ a ¸ ¸ posibil˘. ¸ ¸ a a a acesta nu va mai fi niciodat˘ considerat. vˆrfuri ale grafului. dup˘ o astfel de ad˘ugare. Dac˘. verific˘m ın a a a ¸ ¸ ¸ a dac˘ aceast˘ multime constituie o solutie posibil˘ a problemei. Solutia optim˘ nu este ˆ mod necesar unic˘: se poate ca functia obiectiv s˘ ¸ a ın a ¸ a aib˘ aceea¸i valoare optim˘ pentru mai multe solutii posibile. s ¸ Initial. multimea ¸ ¸ a a a ¸ de candidati selectati nu mai este fezabil˘. etc) ¸i pe care urm˘rim s˘ o optimiz˘m (minimiz˘m/maximiz˘m) a s a a a a a Pentru a rezolva problema de optimizare. a s a ¸ Descrierea formal˘ a unui algoritm greedy general este: a function greedy(C) // C este multimea candidatilor ¸ ¸ S←∅ // S este multimea ˆ care construim solutia ¸ ın ¸ while not solutie(S) and C = ∅ do x ← un element din C care maximizeaz˘/minimizeaz˘ select(x) a a C ← C − {x} if f ezabil(S ∪ {x}) then S ← S ∪ {x} if solutie(S) then return S else return ”nu exist˘ solutie” a ¸ .

g1 g2 gn Vectorul x = (x1 . 14. Practic. EXEMPLE 215 14. n) ¸i de a le ˆ arca ˆ at s ınc˘ ıntregi ˆ rucsac ın g pˆn˘ cˆnd acesta se umple. ≥ .. ∀i = 1. a ¸ determinarea celor mai scurte drumuri care pleac˘ din acela¸i punct (algoritmul lui a s Dijkstra). problema rucsacului. codificarea Huff¸ a man... O modalitate de a ajunge la solutia optim˘ este de a considera obiectele ˆ ordinea descresc˘toare a valorilor ¸ a ın a i utilit˘¸ilor lor date de raportul vi (i = 1. Fie G greutatea maxim˘ suportat˘ de rucsac. etc. .14. sortare prin selectie. 1]. ¸ a ¸ a a a ¸ Vom nota Gp greutatea permis˘ de a se ˆ arca ˆ rucsac la un moment dat.. procedura de rezolvare a problemei este urm˘toarea: a procedure rucsac() Gp ← G for i = 1. problema color˘rii intervalelor.1 Problema continu˘ a rucsacului a Se consider˘ n obiecte..3. xn ) se nume¸te solutie posibil˘ dac˘ s ¸ a a xi ∈ [0. . . determinarea arborelui de cost minim (algoritmii lui Prim ¸i Kruskal). Pentru rezolvare vom folosi metoda greedy. x2 .. a ınc˘ ın Conform strategiei greedy. n n i=1 xi gi ≤ G iar o solutie posibil˘ este solutie optim˘ dac˘ maximizeaz˘ functia f . a s O persoan˘ are un rucsac. ¸ ¸ a Not˘m cu xi ∈ [0. n if Gp > gi then Gp ← Gp − gi . 1] partea din obiectul i care a fost pus˘ ˆ rucsac. a a ın trebuie s˘ maximiz˘m functia a a ¸ n f (x) = i=1 xi ci . Se permite fractionarea obiectelor (valoarea p˘rtii din obiectul a ¸ a¸ fractionat fiind direct proportional˘ cu greutatea ei!)..3.. Obiectul i are greutatea gi ¸i valoarea vi (1 ≤ i ≤ n). Din aceast˘ cauz˘ presupunem ˆ continuare c˘ a a a a a ın a v2 vn v1 ≥ ≥ . Persoana a a a ˆ cauz˘ dore¸te s˘ pun˘ ˆ rucsac obiecte astfel ˆ at valoarea celor din rucsac s˘ ın a s a a ın ıncˆ a fie cˆt mai mare. s determinarea multimii dominante.3 Exemple Dintre problemele clasice care se pot rezolva prin metoda greedy mention˘m: ¸ a plata restului cu num˘r minim de monezi... 2..

3. Atunci cˆnd este necesar˘ a a a a a citirea unui text sunt citite toate textele situate ˆ ınaintea lui pe band˘. ¸ s a ın ˆ Intr-o astfel de aranjare a textelor pe band˘. timpul mediu de citire a unui a text este: k n 1 f (p) = Lp(i) n i=1 k=1 Se dore¸te determinarea unei permut˘ri care s˘ asigure o valoare minim˘ a s a a a timpului mediu de citire... de exemplu. a Modalitatea de plasare a celor n texte pe band˘ corespunde unei permut˘ri p a a a multimii {1... n}. s Propozitia 1 Procedura rucsac() furnizeaz˘ o solutie optim˘.. a a a a a f (p′ ) − f (p) = (Lp(j) − Lp(i) )(j − i) ≤ 0. Aplicˆnd de un num˘r finit de ori acest rationament. a Demonstratie: Fie p = (p1 . ¸ a ¸ a Demonstratia se g˘se¸te. ≤ Ln atunci plasarea textelor corespunz˘toare ¸ a a permutarii identice este optim˘.. Rezult˘ c˘ permutarea identic˘ corespunde unei plas˘ri optime. a a ın a Urm˘toarea propozitie ([25] pagina 99) ne permite s˘ fim siguri c˘ ˆ acest a ¸ a a ın mod ajungem la o plasare optim˘. pe o singur˘ band˘ suficient de lung˘. . 0) cu xi ∈ (0. . .... . T2 .GREEDY else Gp xi ← gi for j = i + 1.. 2. 0. L2 . xi . . Rezolvarea problemei este foarte simpl˘: a • se sorteaz˘ cresc˘tor textele ˆ functie de lungimea lor ¸i a a ın ¸ s • se plaseaz˘ pe band˘ ˆ ordinea dat˘ de sortare. textele fiind a¸ezate pe band˘ ˆ ordinea Tp(1) Tp(2) . a Propozitia 2 Dac˘ L1 ≤ L2 ≤ ... ˆ [25] la pagina 226. pn ) o plasare optim˘. n xj ← 0 Vectorul x are forma x = (1. Tn . Ln . trecem a a a ¸ de la o permutare optim˘ la alt˘ permutare optim˘ pˆn˘ ajungem la permutarea a a a a a identic˘. 1.Tp(n) ... Dac˘ exist˘ i < j cu ¸ a a a Lp(i) ≥ Lp(j) atunci consider˘m permutarea p′ obtinut˘ din p prin permutarea a ¸ a elementelor de pe pozitiile i ¸i j.2 Problema plas˘rii textelor pe o band˘ a a S˘ presupunem c˘ trebuie s˘ plas˘m n texte T1 . METODA OPTIMULUI LOCAL . Atunci ¸ s f (p′ ) − f (p) = (n − j + 1)(Lp(i) − Lp(j) ) + (n − i + 1)(Lp(j) − Lp(i) ) deci Cum p este o plasare optim˘ ¸i f (p′ ) ≤ f (p) rezult˘ c˘ f (p′ ) = f (p) deci ¸i p′ as a a s este o plasare optim˘. p2 ....216 CAPITOLUL 14. 1] ¸i 1 ≤ i ≤ n.. de lungimi date a a a a L1 . .. ¸ a s ın 14... ..

x2 . S˘ se determine o submultime B ′ = {x1 . Putem presupune acum a a ¸ c˘ a1 ≤ a2 ≤ ... s unde n ≤ m. EXEMPLE 217 14.. . < an .. de lungimi date a a a a L1 . L2 . b2 . an } ¸i B = {b1 . Tn . i + 2m. ¸ ın m 14.. a2 . ¸i a¸a a s s mai departe.. x2 = b2 ...4 Maximizarea unei sume de produse Se dau multimile de numere ˆ ¸ ıntregi A = {a1 .. . s xn1 = bn1 ¸i xn = bm . s s Dac˘ toate elementele din A sunt negative vom lua x1 = b1 . Rezolvarea problemei este a foarte simpl˘: a • se sorteaz˘ cresc˘tor textele ˆ functie de lungimea lor ¸i a a ın ¸ s • plasarea textelor pe benzi se face ˆ ordinea dat˘ de sortare.14.. xn−n2 +1 = bm−n2 +1 .. vor fi plasate textele i + m. Ln . Presupunˆnd c˘ L1 ≤ L2 ≤ . . xn } a lui B astfel a ¸ ˆ at valoarea expresiei E = a1 · x1 + a2 · x2 + an · xn s˘ fie maxim˘. .. an } care reprezint˘ a ¸ a coordonatele a n statii pe axa real˘. pe aceea¸i band˘ cu el.... a Se dore¸te determinarea unei plas˘ri a celor n texte pe cele m benzi care s˘ s a a asigure o valoare minim˘ a timpului mediu de citire. ≤ Ln . se poate observa c˘ textul Ti va fi plasat a a a ın a a a pe banda i − i−1 · m ˆ continuarea celor aflate deja pe aceast˘ band˘ iar dup˘ m textul Ti . . ıncˆ a a Vom sorta cresc˘tor elementele celor dou˘ multimi.. ≤ bm . pe m benzi suficient de lungi...3. a s Dac˘ toate elementele din A sunt pozitive vom lua xn = bm .... T2 . 14. ≤ an ¸i b1 ≤ b2 ≤ .. xn−1 = bm−1 .....3. .3. iar mai departe a a • fiecare text se plaseaz˘ pe banda urm˘toare celei pe care a a a fost plasat textul anterior. deci ˆ a s a ınc˘ n−i texte (demonstratiile se pot consulta.. de exemplu. a ¸i a¸a mai departe. ˆ [25])... x2 = b2 . Atunci cˆnd este necesar˘ citirea unui a a text sunt citite toate textele situate ˆ ınaintea lui pe banda respectiv˘. . . ın a ˆ ıncepˆnd cu primul text (cu cea mai mic˘ lungime) care se a a plaseaz˘ pe prima band˘. Dac˘ primele n1 elemente din A sunt strict negative ¸i ultimele n2 elemente a s din A sunt pozitive sau nule (n1 + n2 = n) atunci vom lua x1 = b1 . . xn−1 = bm−1 . a2 ...3. S˘ se ¸ a a .. Vom presupune a1 < a2 < ...3 Problema plas˘rii textelor pe m benzi a S˘ presupunem c˘ trebuie s˘ plas˘m n texte T1 . bm }.5 Problema statiilor ¸ Se consider˘ o multime de numere naturale A = {a1 ..

coincid. ˆ cele dou˘ configuratii. ¸ a ¸ a ¸ ¸ ¸ ¸ Demonstratie: Fie B = {ai1 .GREEDY determine o submultime cu num˘r maxim de statii cu proprietatea c˘ distanta ¸ a ¸ a ¸ dintre dou˘ statii al˘turate este cel putin d (o distant˘ dat˘). a ¸ a ¸a a Rezolvarea problemei este urm˘toarea: a • se alege statia 1. O mutare se poate face dintr-o anumit˘ s ın a a pozitie numai ˆ pozitia liber˘. cutia goal˘ se afl˘ pe pozitia 1. a a a ¸ Propozitia 3 Exist˘ o solutie optim˘ care contine statia 1.218 CAPITOLUL 14. se repet˘ acest ¸a ¸ ¸a ¸ a a pas pˆn˘ cˆnd s-au verificat toate statiile. a a ¸ a 14. . se afl˘ num˘rul 3. c˘ut˘m num˘rul 3 a ¸ ın ¸ a a a a a a din configuratia initial˘ (ˆ g˘sim pe pozitia 1) ¸i ˆ mut˘m pe pozitia cutiei goale.. Evident |ai2 − ai1 | ≥ d. ¸ • se parcurge ¸irul statiilor ¸i se alege prima statie ˆ alnit˘ care este s ¸ s ¸ ıntˆ a la distant˘ cel putin d fat˘ de statia aleas˘ anterior. ¸ ın a ¸ 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 → × × .. ¸ ın ¸ a Prezent˘m algoritmul de rezolvare pe baza urm˘torului exemplu: a a 1 2 3 4 5 6 7 initial → 3 1 ¸ 2 6 5 4 final → 1 2 3 4 5 6 rezolvat → × Vom proceda astfel: cutia goal˘ din configuratia initial˘ se afl˘ pe pozitia 3 a ¸ ¸ a a ¸ dar pe aceast˘ pozitie. METODA OPTIMULUI LOCAL . aim } o solutie a problemei care nu contine ¸ ¸ ınlocui cu statia ¸ statia 1 (deci ai1 > a1 ). a Dup˘ selectarea statie 1 se pot selecta (pentru obtinerea unei solutii optime) a ¸ ¸ ¸ numai statii situate la distante cel putin d fat˘ de statia 1.. Statia i1 se poate ˆ ¸ 1 pentru c˘ |ai2 − a1 | = |ai2 − ai1 + ai1 − a1 | = |ai2 − ai1 | + |ai1 − a1 | > d.3. ¸ ¸ a ıl a ¸ s ıl a ¸ acum. vom repeta acest rationament pˆn˘ cˆnd a a ¸ ¸ a a a pozitiile cutiilor goale. Pentru aceste statii ¸ ¸ ¸ a ¸ ¸ repet˘m strategia sugerat˘ de propozitia anterioar˘. ai2 . ˆ configuratia final˘.6 Problema cutiilor Se dore¸te obtinerea unei configuratii de n numere plasate pe n + 1 pozitii (o s ¸ ¸ ¸ pozitie fiind liber˘) dintr-o configuratie initial˘ dat˘ ˆ care exist˘ o pozitie liber˘ ¸ a ¸ ¸ a a ın a ¸ a ¸i cele n numere plasate ˆ alt˘ ordine.

Acest fapt permite utilizarea s a s a c˘ut˘rii binare pentru determinarea sub¸irului potrivit pentru elementul xi a a s atunci cˆnd prelucr˘m acest element. s s a s a Metota de rezolvare este urm˘toarea: se parcurg elementele ¸irului initial unul a s ¸ dup˘ altul ¸i pentru fiecare element xi se caut˘ un sub¸ir existent la care xi se a s a s poate ad˘uga la sfˆr¸it. dac˘ nu exist˘ un astfel de sub¸ir se creeaz˘ un sub¸ir nou a as a a s a s care va contine ca prim element pe xi .. bn ].8 Problema intervalelor disjuncte Se consider˘ n intervale ˆ a ınchise pe axa real˘ [a1 . a 1 2 modificat → 1 2 final → 1 2 rezolvat → × × 3 4 5 3 6 3 4 × nerezolvat˘ a 3 3 3 × 4 6 5 4 219 6 7 5 4 5 6 × ¸i vom muta num˘rul din acea cutie ˆ s a ın 6 5 5 × 7 4 6 Repet˘m rationamentul prezentat la ˆ a ¸ ınceput ¸i vom continua pˆn˘ cˆnd toate s a a a numerele ajung pe pozitiile din configuratia final˘. EXEMPLE 1 2 modificat → 1 2 final → 1 2 rezolvat → × × Acum vom c˘uta o cutie a cutia goal˘. se parcurg intervalele.. unul a a . [a2 .3.. . b2 ]. Se a cere selectarea unor intervale disjuncte astfel ˆ at num˘rul acestora s˘ fie maxim.14. a Observatie: Ultimele elemente din fiecare sub¸ir (care sunt ¸i elemente maxime ¸ s s din aceste sub¸iruri) formeaz˘ un ¸ir descresc˘tor. [an .3. ıncˆ a a Metoda de rezolvare este urm˘toarea: se sorteaz˘ intervalele cresc˘tor dup˘ a a a a cap˘tul din dreapta. se selecteaz˘ primul interval. dac˘ exist˘ mai multe sub¸iruri la care se ¸ a a s poate ad˘uga xi . ¸ ¸ a 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 → × × × × × × 14.7 Problema sub¸irurilor s S˘ se descompun˘ un ¸ir de n numere ˆ a a s ıntregi ˆ sub¸iruri strict cresc˘toare ın s a astfel ˆ at numerele lor de ordine din ¸irul initial s˘ fie ordonate cresc˘tor ˆ ıncˆ s ¸ a a ın sub¸irurile formate ¸i num˘rul sub¸irurilor s˘ fie minim. se va alege acela care are cel mai mare ultim element. b1 ].3. a a 14.

. bk ] dac˘ ci ∈ [ak .220 CAPITOLUL 14.. y2n ) reprezentˆnd taxe. y2 . S˘ se determine o a multime cu num˘r minim de alemente C = {c1 . acest a ¸ procedeu continu˘ pˆn˘ cˆnd nu mai r˘mˆn intervale de analizat. S˘ se determine ¸irul z = (z1 .. x = (x1 .m. ¸ a ¸ a ¸ a 14. .9 Problema alegerii taxelor Se dau dou˘ ¸iruri cu cˆte 2n numere ˆ as a ıntregi fiecare... a a a a a a Propozitia 4 Exist˘ o solutie optim˘ care contine primul interval dup˘ sortare. [an . a a s unde zi ∈ {xi . c2 . pˆn˘ se g˘se¸te un interval [ai . bj ] cu aj > c2 ¸i alegem c3 = bj . [a2 . s s 14. .. static int n. z2 .. . bi ] disjunct cu ultimul interval ales ¸i a a a a s s se adaug˘ acest interval la solutie. static boolean[] esteInArbore. METODA OPTIMULUI LOCAL . x2 . Repet˘m acest procedeu pˆn˘ cˆnd termin˘m s a a a a a de parcurs toate intervalele.. // arbore minim de acoperire: algoritmul lui Prim class Prim // O(n^3) { static final int oo=0x7fffffff.io. static int[][] cost.. ... astfel ˆ at suma tuturor elementelor din ¸irul z ıncˆ s s˘ fie minim˘ ¸i acest ¸ir s˘ contin˘ exact n elemente din ¸irul x ¸i n elemente din a as s a ¸ a s s ¸irul y. cm } care s˘ ”acopere” toate ¸ a a cele n intervale date (spunem c˘ ci ”acoper˘” intervalul [ak . z2n ).3. s Metoda de rezolvare este urm˘toarea: se construie¸te ¸irul t = x − y (ˆ care a s s ın ti = xi − yi ) ¸i se sorteaz˘ cresc˘tor. 14. el devenind astfel ”ultimul interval ales”. . bi ] cu ai > c1 ¸i a a a s alegem c2 = bi . Parcurgem intervalele pˆn˘ cand g˘sim un interval [ai .. Parcurgem mai departe intervalele pˆn˘ cand g˘sim un interval a a a [aj . ≤ bn .. se aleg din ¸irul x elementele corespunz˘toare s a a s a primelor n elemente din ¸irul t sortat iar celelalte n elemente se iau din ¸irul y. Presupunem c˘ b1 ≤ b2 ≤ .*.10 Problema acoperirii intervalelor Se consider˘ n intervale ˆ a ınchise [a1 . Primul punct ales este a a c1 = b1 . x2n ) ¸i s y = (y1 . . Alg prim de ordinul O(n3 ) import java. bn ].. a a a Metoda de rezolvare este urm˘toarea: se sorteaz˘ intervalele cresc˘tor dup˘ a a a a cap˘tul din dreapta..11 Algoritmul lui Prim Determinarea arborelui minim de acoperire pentru un graf neorientat.3. bk ]). b2 ].GREEDY dup˘ altul. b1 ]. yi } (1 ≤ i ≤ 2n)...3.

j<=n. } for(j=1. jmin=j. for(k=1.nextToken().nval.jmin=0. esteInArbore[nods]=true. esteInArbore=new boolean [n+1]. p[jmin]=imin. if(min>cost[i][j]) { min=cost[i][j].k<=m. k.min. p=new int[n+1].out"))). costArbore+=min.i<=n. j.nval. imin=i.j<=n. st.nextToken(). st. j=(int)st.nextToken().3. m=(int)st.nextToken(). for(j=1. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("prim.imin=0.nextToken(). for(i=1.j++) cost[i][j]=oo.nval.in"))). cost[i][j]=cost[j][i]=(int)st. st.i++) for(k=1.j++) // O(n) { if(esteInArbore[j]) continue. } }//for j }//for i esteInArbore[jmin]=true. // nod start int i. . n=(int)st. costArbore=0. EXEMPLE static int[] p.nval.k<=n-1.i<=n. for(i=1.k++) // sunt exact n-1 muchii in arbore !!! O(n) { min=oo.14. cost=new int[n+1][n+1]. st. // predecesor in arbore 221 public static void main(String[]args) throws IOException { int nods=3. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("prim.i++) // O(n) { if(!esteInArbore[i]) continue. i=(int)st.nval.k++) { st.

.*.222 }//for k CAPITOLUL 14.nextToken(). n=(int)st. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("prim. st. k.k++) if(p[k]!=0) out.m. out. // arbore minim de acoperire: algoritmul lui Prim class PrimDist // O(n^2) { static final int oo=0x7fffffff. out. // distante de la nod catre arbore public static void main(String[]args) throws IOException { int nodStart=3. }//main }//class /* 6 7 1 2 1 3 2 3 3 4 4 5 5 6 4 6 */ 3 1 2 1 1 3 2 1 3 2 3 4 3 5 4 6 4 cost=7 Alg Prim cu distante ¸ import java. static int[] p. // nod start int i.min.close(). costArbore=0.k<=n. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("prim.nval.in"))).println(k+" "+p[k]). static int n.nextToken(). st.jmin=0.GREEDY for(k=1.println("cost="+costArbore).nval. static int[][] cost. static boolean[] esteInArbore. METODA OPTIMULUI LOCAL .out"))).io. // predecesor in arbore static int[] d. j. m=(int)st.

for(k=1.k<=n. st.k++) { st. } }//for j esteInArbore[jmin]=true.j) if(d[jmin]+cost[jmin][j]<d[j]) { d[j]=d[jmin]+cost[jmin][j].nval.i++) d[i]=oo. EXEMPLE cost=new int[n+1][n+1].j++) cost[i][j]=oo. } } }//for k . p[j]=jmin. for(k=1.nextToken().nextToken().nextToken().i++) for(j=1.3.k++) // O(n) { min=oo. 223 for(j=1. d=new int[n+1]. for(i=1.j<=n. i=(int)st. d[jmin]=0. for(j=1.k<=m. jmin=j. st. j=(int)st. p=new int[n+1].i<=n.i<=n.j<=n. costArbore+=min.j++) // actualizez distantele nodurilor O(n) { if(esteInArbore[j]) continue. } d[nodStart]=0.14. for(i=1. esteInArbore=new boolean [n+1].j<=n. cost[i][j]=cost[j][i]=(int)st.nval. // j este deja in arbore if(cost[jmin][j]<oo) // am muchia (jmin.nval. if(min>d[j]) { min=d[j].j++) // O(n) { if(esteInArbore[j]) continue.

*.in"))).m.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. static int[][] w. n=(int)st.cost.224 CAPITOLUL 14.nval.nval. m=(int)st. // hd[i]=distanta din pozitia i din heap static int[] hnod. // hnod[i]= nodul din pozitia i din heap static int[] pozh. st.k++) if(p[k]!=0) out.GREEDY for(k=1. out. // distante de la nod catre arbore static int[] p. st. out.out"))).println(k+" "+p[k]).nval. w=new int[n+1][n+1].io. st. .costa=0.println("cost="+costArbore).close().j. int i. // predecesorul nodului in arbore static int[] hd.k<=n. // matricea costurilor static int[] d. METODA OPTIMULUI LOCAL . PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("prim.k.nextToken(). // 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.nextToken(). }//main }//class /* 6 7 1 2 1 3 2 3 3 4 4 5 5 6 4 6 */ 3 1 2 1 1 3 2 1 3 2 3 4 3 5 4 6 4 cost=7 Alg Prim cu heap import java. static int n.nods. nods=(int)st.

hd[pozh[nods]]=0.println(p[i]+" "+i). p=new int [n+1].i<=n. while(q>0) // la fiecare pas adaug un varf in arbore { u=extragMin(q). st.i++) for(j=1. for(k=1. break.out.println("graf neconex !!!").aux.costa+=w[p[i]][i].nval.i++) // afisez muchiile din arbore if(i!=nods) {out. }//main static void prim(int nods) { int u.u++) { hnod[u]=pozh[u]=u. // q = noduri nefinalizate = dimensiune heap d[nods]=0.k++) { st. i=(int)st.close(). w[j][i]=w[i][j]=cost. for(u=1.q.} out.nextToken().3.j++) w[i][j]=oo. urcInHeap(pozh[nods]).println("costa="+costa).nval. out. if(u==-1) { System. st. p[u]=0.nextToken(). hd=new int[n+1]. } 225 q=n. hd[u]=d[u]=oo. for(i=1.nextToken().u<=n.i<=n.k<=m. pozh=new int[n+1]. d=new int [n+1].v. } . EXEMPLE for(i=1. } prim(nods). j=(int)st.nval. hnod=new int[n+1]. cost=(int)st.j<=n.14.

p[v]=u..tata.v) // relax si refac heap } }//prim(.fiu. pozh[hnod[1]]=1. hnod[q]=aux.v<=q... fiu1=2*tata.fiu1.int v) { if(w[u][v]<d[v]) { d[v]=w[u][v].hnod[v]). // noduri nefinalizate = in heap 1. hd[pozh[v]]=d[v]. METODA OPTIMULUI LOCAL . } }//relax(. fiu2=2*tata+1.fiu2. if(fiu2<=q-1) if(hd[fiu2]<hd[fiu1]) fiu=fiu2. tata=1. aux=hnod[1]. hd[1]=hd[q]. pozh[hnod[q]]=q.226 CAPITOLUL 14. hnod[1]=hnod[q]..nod. aux=hd[1].q { // returnez valoarea minima (din varf!) si refac heap in jos // aducand ultimul in varf si coborand ! int aux. if(hd[tata]<=hd[fiu]) // 1 <---> q . urcInHeap(pozh[v])..) static void relax(int u.q // cost finit ==> exista arc (u. while(fiu1<=q-1) //refac heap de sus in jos pana la q-1 { fiu=fiu1.v++) if(w[u][hnod[v]]<oo) relax(u.) static int extragMin(int q) // in heap 1.GREEDY q--. for(v=1.. hd[q]=aux.

fiu2=2*tata+1. hnod[tata]=aux. EXEMPLE break. aux=hnod[fiu]. fiu=tata. pozh[hnod[tata]]=fiu. hnod[fiu]=hnod[tata]. fiu1=2*tata..) // hnod[1] a ajuns deja (!) in hnod[q] 227 static void urcInHeap(int nodh) { int aux. hnod[fiu]=hnod[tata]. hd[tata]=aux. } }//class /* 6 7 3 1 2 3 1 3 1 3 1 3 2 . tata=fiu/2. while((tata>0)&&(hd[fiu]<hd[tata])) { pozh[hnod[tata]]=fiu.tata. aux=hd[fiu].. hd[fiu]=hd[tata].3. } return hnod[q]. nod=hnod[nodh]. aux=hd[fiu].nod. hd[tata]=aux. fiu=nodh. } pozh[nod]=fiu. }// extragMin(. hd[fiu]=hd[tata]. tata=fiu. pozh[hnod[fiu]]=tata. hnod[fiu]=nod. tata=fiu/2.fiu.14.

for(k=1. static int[] x. st.nval.in"))).*.m.io.out.k++) { st. }//main . z[k]=(int)st.nextToken(). st. st. y=new int[m+1].out"))). x=new int[m+1].k<=m. m=(int)st. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("kruskal.GREEDY 3 4 4 5 4 6 costa=7 14.12 Algoritmul lui Kruskal import java.y. public static void main(String[] args) throws IOException { int k.nextToken().close().z. z=new int[m+1]. METODA OPTIMULUI LOCAL .nextToken().nextToken().3. et=new int[n+1].println("cost="+cost).nextToken(). y[k]=(int)st.println(cost). StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("kruskal. // Arbore minim de acoperire : Kruskal class Kruskal { static int n. System. out.et.nval. n=(int)st. } kruskal(). x[k]=(int)st.nval.nval. out. st.nval.cost=0.228 2 3 3 4 4 5 5 6 4 6 */ 2 1 1 3 2 CAPITOLUL 14.

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

for(k=1.j.3. // nod start int i.j<=n. for(i=1. st. k. ˆ graf neorientat ¸ ın import java.y). while((z[i]<=z[j])&&(i<j)) j--. esteFinalizat=new boolean [n+1]. d=new int[n+1].GREEDY while((z[i]<=z[j])&&(i<j)) i++. j. METODA OPTIMULUI LOCAL .j.j++) cost[i][j]=oo. static int[][] cost. // predecesor in drum static int[] d.in"))).k<=m. min. if(i<j) { invers(i.i++) d[i]=oo.out"))). } } return i. static boolean[] esteFinalizat.i<=n. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dijkstraNeorientat. n=(int)st.jmin=0. static int n.io. m=(int)st. // drumuri minime de la nodSursa class Dijkstra // O(n^2) { static final int oo=0x7fffffff.*.m.nval.i++) for(j=1. invers(i.x). PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dijkstraNeorientat.j. //i==j }//poz }//class 14. for(i=1.13 Algoritmul lui Dijkstra Alg Dijkstra cu distante.230 CAPITOLUL 14.k++) .nextToken().nextToken(). p=new int[n+1].i<=n. cost=new int[n+1][n+1].z). st. // distante de la nod catre nodSursa public static void main(String[]args) throws IOException { int nodSursa=1. static int[] p.nval. invers(i.

i=(int)st.out.print(nodSursa+"-->"+k+" dist="+d[k]+" drum: "). } d[nodSursa]=0. } }//class /* 6 7 1-->1 dist=0 drum: 1 .nval.print(k+" "). // j este deja in arbore if(cost[jmin][j]<oo) // am muchia (jmin. } } }//for k for(k=1.nval. System. drum(k).nextToken(). --> k { if(p[k]!=0) drum(p[k]). p[j]=jmin.k<=n.j<=n. 231 for(k=1.k++) { System.3.nextToken().nval. System. for(j=1. st.out.k<=n. }//main static void drum(int k) // s --> . st.j++) // actualizez distantele nodurilor // O(n) { if(esteFinalizat[j]) continue.nextToken().j) if(d[jmin]+cost[jmin][j]<d[j]) { d[j]=d[jmin]+cost[jmin][j]. } out.. for(j=1. } }//for j esteFinalizat[jmin]=true.. j=(int)st. if(min>d[j]) { min=d[j].j<=n.14. jmin=j. EXEMPLE { st.println().out.k++) // O(n) { min=oo. cost[i][j]=cost[j][i]=(int)st.j++) // O(n) { if(esteFinalizat[j]) continue.close().

.i++) for(j=1.nval. w[j][i]=w[i][j]=cost.in"))). w=new int[n+1][n+1]. static int n. dijkstra(nodSursa).nextToken(). METODA OPTIMULUI LOCAL .cost. static int[][]w.232 1 2 1 3 2 3 3 4 5 4 5 6 4 6 */ 4 1 2 1 1 3 2 CAPITOLUL 14.k<=m.nval.nval. st. i=(int)st. j=(int)st.GREEDY 1-->2 1-->3 1-->4 1-->5 1-->6 dist=3 dist=1 dist=2 dist=3 dist=4 drum: drum: drum: drum: drum: 1 1 1 1 1 3 3 3 3 3 2 4 4 5 4 6 Alg Dijkstra cu heap.nextToken().k.out"))). for(i=1.nval.nextToken(). n=(int)st. st.j++) w[i][j]=oo. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dijkstraNeorientat.m. // hnod[i]= nodul din pozitia i din heap static int[] pozh. // hd[i]=distanta din pozitia i din heap static int[] hnod.j.io. cost=(int)st.costa=0. m=(int)st. // distante minime de la nodSursa class DijkstraNeorientatHeap // pastrate in MinHeap ==> O(n log n) ==> OK !!! { static final int oo=0x7fffffff.*.k++) { st. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dijkstraNeorientat. for(k=1. // distante de la nod catre arbore static int[]p. // predecesorul nodului in arbore static int[] hd. ˆ graf neorientat ın import java.nval. } nodSursa=1.nextToken(). st.j<=n. // pozh[i]=pozitia din heap a nodului i public static void main(String[]args) throws IOException { int i. st.nodSursa.nextToken().i<=n. // matricea costurilor static int[]d.

d=new int [n+1]. p[u]=0. hd=new int[n+1].out.println().k<=n. System.out.3.v<=q.14.aux. pozh=new int[n+1]. while(q>0) // la fiecare pas adaug un varf in arbore { u=extragMin(q). // q = noduri nefinalizate = dimensiune heap d[nods]=0. drum(k). if(u==-1) { System. } q=n. break. }//main static void dijkstra(int nods) { int u.k++) { System. p=new int [n+1]. urcInHeap(pozh[nods]).q.q if(w[u][hnod[v]]<oo) // cost finit ==> exista arc (u.u++) { hnod[u]=pozh[u]=u.println("graf neconex !!!").v++) // noduri nefinalizate = in heap 1. EXEMPLE 233 for(k=1.int v) . for(v=1.close(). } out..out. // relax si refac heap } }// dijkstra() static void relax(int u. hd[pozh[nods]]=0. for(u=1.v. } q--.u<=n. hd[u]=d[u]=oo.hnod[v]).v) relax(u. hnod=new int[n+1].print(nodSursa+"-->"+k+" dist="+d[k]+" drum: ").

} }// relax(. hnod[1]=hnod[q].fiu1.. // 1 <---> q aux=hnod[1]... // hnod[1] a ajuns deja (!) in hnod[q] } // extragMin(.nod.fiu2.fiu. fiu1=2*tata. hd[1]=hd[q]. hd[q]=aux. } return hnod[q]. tata=fiu.. fiu2=2*tata+1. aux=hnod[fiu]. if(fiu2<=q-1) if(hd[fiu2]<hd[fiu1]) fiu=fiu2. fiu2=2*tata+1. if(hd[tata]<=hd[fiu]) break. pozh[hnod[tata]]=fiu. hd[tata]=aux.) static int extragMin(int q) // in heap 1.) .. hd[fiu]=hd[tata]. hnod[q]=aux. while(fiu1<=q-1) //refac heap de sus in jos pana la q-1 { fiu=fiu1. METODA OPTIMULUI LOCAL . pozh[hnod[1]]=1.q { // returnez valoarea minima (din varf!) si refac heap in jos int aux.tata. hnod[fiu]=hnod[tata]. tata=1. pozh[hnod[q]]=q. fiu1=2*tata. hd[pozh[v]]=d[v]. aux=hd[1].234 { CAPITOLUL 14. p[v]=u. urcInHeap(pozh[v]). pozh[hnod[fiu]]=tata. hnod[tata]=aux. aux=hd[fiu].GREEDY if(d[u]+w[u][v]<d[v]) { d[v]=d[u]+w[u][v].

while((tata>0)&&(hd[fiu]<hd[tata])) { pozh[hnod[tata]]=fiu. aux=hd[fiu].*. } }//class /* 6 7 1 2 1 3 2 3 3 4 5 4 5 6 4 6 */ 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 Alg Dijkstra cu distante.fiu.tata. fiu=tata. tata=fiu/2. hd[tata]=aux.3. EXEMPLE 235 static void urcInHeap(int nodh) { int aux. fiu=nodh... --> k { if(p[k]!=0) drum(p[k]). hnod[fiu]=hnod[tata]. hd[fiu]=hd[tata]. System. hnod[fiu]=nod. // drumuri minime de la nodSursa class Dijkstra // O(n^2) { ..14. ˆ graf orientat ¸ ın import java. tata=fiu/2.) static void drum(int k) // s --> . } // urcInHeap(. } pozh[nod]=fiu.print(k+" ").io.nod. nod=hnod[nodh].out..

StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dijkstraOrientat. k.in"))). // predecesor in drum int[] d. p=new int[n+1].nextToken().jmin=0. for(k=1.j++) // O(n) { .nval. n=(int)st.nval. m=(int)st. boolean[] esteFinalizat. int[] p. METODA OPTIMULUI LOCAL .k++) { st.j<=n. int n. for(j=1.j<=n. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dijkstraOrientat. i=(int)st.i<=n.j++) cost[i][j]=oo.nval. st.nextToken(). for(i=1.nextToken(). // distante de la nod catre nodSursa public static void main(String[]args) throws IOException { int nodSursa=1.i<=n. d=new int[n+1]. st. j=(int)st. for(k=1. cost=new int[n+1][n+1].k<=m.nval.i++) d[i]=oo.236 static static static static static static CAPITOLUL 14.out"))). for(i=1.GREEDY final int oo=0x7fffffff. st. cost[i][j]=(int)st. j. min.nextToken().nextToken().k++) // O(n) { min=oo.nval.k<=n.i++) for(j=1.m. // nod start int i. } d[nodSursa]=0. st. esteFinalizat=new boolean [n+1]. int[][] cost.

}//main static void drum(int k) // s --> . EXEMPLE 237 if(esteFinalizat[j]) continue. jmin=j.print(nodSursa+"-->"+k+" dist="+d[k]+" drum: ").k<=n.k++) { System. for(j=1.j<=n. System.println(). } out.close().j++) // actualizez distantele nodurilor // O(n) { if(esteFinalizat[j]) continue.14. System.out.print("Nu exista drum!"). } } }//for k for(k=1. class DijkstraOrientatHeap // distante minime de la nodSursa // pastrate in MinHeap ==> O(n log n) ==> OK !!! . // j este deja in arbore if(cost[jmin][j]<oo) // am muchia (jmin.out.3. if(d[k]<oo) drum(k).io.out. --> k { if(p[k]!=0) drum(p[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 */ Alg Dijkstra cu heap. ˆ graf orientat ın import java. else System..print(k+" ").. if(min>d[j]) { min=d[j]. p[j]=jmin.j) if(d[jmin]+cost[jmin][j]<d[j]) { d[j]=d[jmin]+cost[jmin][j].*.out. } }//for j esteFinalizat[jmin]=true.

st. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dijkstraOrientat.j++) w[i][j]=oo.k<=n. static int n. static int[]p.nval. // 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. w[i][j]=cost.m.in"))). for(i=1. st. st. i=(int)st. j=(int)st.k++) { if(d[k]<oo) { .k++) { st.nodSursa.nval.j<=n.nextToken(). n=(int)st. static int[] pozh. static int[] hnod. m=(int)st.nextToken().nextToken().nval.i++) for(j=1. w=new int[n+1][n+1].nval.i<=n. st.j.nval.nextToken().GREEDY static final int oo=0x7fffffff. static int[][]w. for(k=1.k. } nodSursa=1. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dijkstraOrientat.nextToken().cost.out"))). METODA OPTIMULUI LOCAL .costa=0.k<=m. static int[]d.238 { CAPITOLUL 14. for(k=1. cost=(int)st. dijkstra(nodSursa). static int[] hd.

println().v<=q. } q--.v) // relax si refac heap .out.u<=n. }//main static void dijkstra(int nods) { int u. hd[pozh[nods]]=0. EXEMPLE 239 System. urcInHeap(pozh[nods]). } q=n.hnod[v]). while(q>0) // la fiecare pas adaug un varf in arbore { u=extragMin(q)..print(nodSursa+"-->"+k+" Nu exista drum! "). } out. // q = noduri nefinalizate = dimensiune heap d[nods]=0.int v) { if(d[u]+w[u][v]<d[v]) { // noduri nefinalizate = in heap 1.close(). for(u=1.q.u++) { hnod[u]=pozh[u]=u. p=new int [n+1].print(nodSursa+"-->"+k+" dist="+d[k]+" drum: ").out. } else System..14.println("graf neconex !!!").) static void relax(int u. hnod=new int[n+1]. break.aux.3.q // cost finit ==> exista arc (u.. for(v=1. drum(k). System. if(u==-1) { System.out.v++) if(w[u][hnod[v]]<oo) relax(u.out. } }//dijkstra(.v. d=new int [n+1]. hd=new int[n+1]. hd[u]=d[u]=oo. pozh=new int[n+1]. p[u]=0.

METODA OPTIMULUI LOCAL . urcInHeap(pozh[v]). pozh[hnod[tata]]=fiu. hd[tata]=aux. hd[fiu]=hd[tata]. fiu1=2*tata.. tata=1. pozh[hnod[fiu]]=tata. . fiu2=2*tata+1. aux=hd[fiu].tata. hd[q]=aux.fiu.fiu1. aux=hnod[fiu]. while(fiu1<=q-1) //refac heap de sus in jos pana la q-1 { fiu=fiu1. if(hd[tata]<=hd[fiu]) break. if(fiu2<=q-1) if(hd[fiu2]<hd[fiu1]) fiu=fiu2.fiu. } }// relax(.) static void urcInHeap(int nodh) { int aux. pozh[hnod[q]]=q... tata=fiu. // 1 <---> q aux=hnod[1].nod.fiu2. hnod[tata]=aux.tata. hd[pozh[v]]=d[v].q { // returnez valoarea minima (din varf!) si refac heap in jos int aux. aux=hd[1].) static int extragMin(int q) // in heap 1. fiu1=2*tata.GREEDY d[v]=d[u]+w[u][v]. hnod[q]=aux. // hnod[1] a ajuns deja (!) in hnod[q] } // extragMin(. pozh[hnod[1]]=1.nod.240 CAPITOLUL 14. hd[1]=hd[q]. hnod[1]=hnod[q]. fiu2=2*tata+1. } return hnod[q]. p[v]=u.. hnod[fiu]=hnod[tata]..

EXEMPLE 241 nod=hnod[nodh].OJI2002 cls 11 ¸ Autorit˘¸ile dintr-o zon˘ de munte intentioneaz˘ s˘ stabileasc˘ un plan de at a ¸ a a a urgent˘ pentru a reactiona mai eficient la frecventele calamit˘¸i naturale din zon˘.) static void drum(int k) // s --> . while((tata>0)&&(hd[fiu]<hd[tata])) { pozh[hnod[tata]]=fiu. } }//class /* 6 7 1 2 1 3 2 3 3 4 5 4 5 6 4 6 */ 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 14. --> k { if(p[k]!=0) drum(p[k]). fiu=tata. } // urcInHeap(.print(k+" "). aux=hd[fiu]..3. } pozh[nod]=fiu. System...14 Urgenta . tata=fiu/2. tata=fiu/2. ¸a ¸ at a ˆ acest scop au identificat N puncte de interes strategic ¸i le-au numerotat distinct In s de la 1 la N . hd[tata]=aux.. Punctele de interes strategic sunt conectate prin M c˘i de acces avˆnd a a . hnod[fiu]=hnod[tata]. fiu=nodh.14. hd[fiu]=hd[tata].out. hnod[fiu]=nod.3.

ˆ ıntre punctele k2 ¸i h2 a fost ˆ s ıntrerupt˘ calea de acces a . programul va determina una singur˘. ˆ care punctele de interes strategic s˘ fie ˆ artite ˆ a ın a ımp˘ ¸ ıntr-un num˘r de K a grupuri.. Ca urmare pot rezulta mai a a a multe grupuri de puncte ˆ a¸a fel ˆ at ˆ ın s ıncˆ ıntre oricare dou˘ puncte din acela¸i grup a s s˘ existe m˘car un drum ¸i ˆ a a s ıntre oricare dou˘ puncte din grupuri diferite s˘ nu a a existe drum.gravitatea maxim˘ a C .ˆ ıntre punctele iM ¸i jM exist˘ o cale de acces de prioritate pM s a Date de ie¸ire s Fi¸ierul de ie¸ire URGENTA.ˆ ıntre punctele i2 ¸i j2 exist˘ o cale de acces de prioritate p2 s a . kC hC .. ˆ at ın ¸ ¸a Intre oricare dou˘ puncte de interes strategic a exist˘ cel mult o cale de acces ce poate fi parcurs˘ ˆ ambele sensuri ¸i cel putin a a ın s ¸ un drum (format din una sau mai multe c˘i de acces) ce le conecteaz˘.OUT va avea urm˘torul format: s s a gravmax . a a ¸ a Exemplu .num˘rul de c˘i de acces ˆ a a ıntrerupte de calamitate k1 h1 .ˆ ıntre punctele kC ¸i hC a fost ˆ s ıntrerupt˘ calea de acces a Restrictii ¸i preciz˘ri ¸ s a 0 < N < 256 N − 2 < M < 32385 0<K <N +1 Priorit˘¸ile c˘ilor de acces sunt ˆ at a ıntregi strict pozitivi mai mici decˆt 256. Date de intrare Fi¸ierul de intrare URGENTA.ˆ ıntre punctele k1 ¸i h1 a fost ˆ s ıntrerupt˘ calea de acces a k2 h2 . Autorit˘¸ile estimeaz˘ gravitatea unei calamit˘¸i ca fiind suma priorit˘¸ilor at a at at c˘ilor de acces distruse de aceasta ¸i doresc s˘ determine un scenariu de gravitate a s a maxim˘. METODA OPTIMULUI LOCAL ..ˆ ıntre punctele i1 ¸i j1 exist˘ o cale de acces de prioritate p1 s a i2 j2 p2 . a a ˆ cazul unei calamit˘¸i unele c˘i de acces pot fi temporar ˆ In at a ıntrerupte ¸i astfel s ˆ ıntre anumite puncte de interes nu mai exist˘ leg˘tur˘. s Dac˘ exist˘ mai multe solutii.242 CAPITOLUL 14.IN are urm˘torul format: s a N M K i1 j1 p1 . iM jM pM . a Un grup de puncte poate contine ˆ ¸ ıntre 1 ¸i N puncte inclusiv..GREEDY priorit˘¸i ˆ functie de important˘.

nrm.min. ncc=(int)st. n=(int)st.out"))). static boolean[] esteInArbore.io. j.*.gravmax.nval. si una slaba merge! { static final int oo=0x7fffffff.nextToken(). // predecesor in arbore static int[] d. st. k.nextToken().jmin=0.3. // a1[k]=varful 1 al muchiei k din arbore static int[] a2. StreamTokenizer st= new StreamTokenizer( new BufferedReader(new FileReader("urgenta.. m=(int)st. static int n.OUT 27 8 13 17 24 34 37 45 56 67 243 Timp maxim de executare: 1 secund˘ / test a Codul surs˘ a import java.m.14.IN 7 11 4 121 132 173 243 342 351 361 375 455 564 673 URGENTA. st.nval.aux.nextToken(). // distante de la nod catre arbore static int[] a1. // a1[k]=costul muchiei k din arbore public static void main(String[]args) throws IOException { int nodStart=3. . // a2[k]=varful 2 al muchiei k din arbore static int[] ac. costArbore=0. EXEMPLE URGENTA. PrintWriter out= new PrintWriter( new BufferedWriter(new FileWriter("urgenta. // nod start int i.in"))).. // ncc = nr componente conexe static int[][] cost.nval.ncc. static int[] p. // arbore minim de acoperire: algoritmul lui Prim O(n^2) class Urgenta // sortare O(n^2) .costmax. st.

for(j=1.k<=n. // j este deja in arbore if(cost[jmin][j]<oo) // am muchia (jmin.i++) for(j=1.244 CAPITOLUL 14. for(i=1. costArbore+=min.j<=n. st.j<=n.j++) // O(n) { if(esteInArbore[j]) continue. O(n) . } // alg Prim d[nodStart]=0.j) if(d[jmin]+cost[jmin][j]<d[j]) { d[j]=d[jmin]+cost[jmin][j].k++) { st. jmin=j.j<=n.k++) // O(n) { min=oo.nval.GREEDY cost=new int[n+1][n+1]. for(j=1. for(k=1. i=(int)st. st.i++) d[i]=oo. a1=new int[n].nextToken().j++) // actualizez distantele nodurilor // { if(esteInArbore[j]) continue.nval. p=new int[n+1]. esteInArbore=new boolean [n+1]. METODA OPTIMULUI LOCAL . if(min>d[j]) { min=d[j]. d[jmin]=0. } }//for j esteInArbore[jmin]=true.nextToken(). for(i=1. cost[i][j]=cost[j][i]=(int)st. for(k=1. a2=new int[n]. costmax+=cost[i][j]. ac=new int[n]. j=(int)st. costmax=0.i<=n.k<=m.i<=n.j++) cost[i][j]=oo.nextToken().nval. d=new int[n+1].

. //System.i<=n-2. ac[k]=cost[i][p[i]]. aux=a1[i]. ac[i]=ac[i+1]. . // deocamdata. a1[k]=i..out.i<=n.i++) { cost[a1[i]][a2[i]]=cost[a2[i]][a1[i]]=oo. a2[i]=a2[i+1]. a2[i+1]=aux.println(i+" "+p[i]+" --> "+cost[i][p[i]]). a1[i+1]=aux.println("cost="+costArbore).14. for(i=1. a1[i]=a1[i+1].i++) if(ac[i]<ac[i+1]) { aux=ac[i].. //sterg . gravmax=costmax-costArbore. for(i=ncc. 245 // trebuie sa adaug la gravmax primele ncc-1 costuri mari (sort!) // din arborele minim de acoperire // sortez descrescator ac (pastrand corect a1 si a2) // care are n-1 elemente for(k=1. } } // primele ncc-1 .3.k<=n-1. aux=a2[i]. for(i=1..out.i<=ncc-1. // sterg muchiile ramase in arbore .i<=n-1.i++) gravmax+=ac[i]. a2[k]=p[i].k++) // de n-1 ori (bule) { for(i=1. } } }//for k k=0.out. } //System.i++) if(p[i]!=0) { //System. ac[i+1]=aux.. EXEMPLE p[j]=jmin.. k++.println("gravmax ="+gravmax).

i++) for(j=i+1.15 Reactivi .246 } CAPITOLUL 14.in contine: s ¸ − pe prima linie num˘rul natural N . pentru fiecare reactiv x. Mai exact. respectiv temperatura maxim˘ de stocare a reactivului x.i<=n-1. care reprezint˘ num˘rul de reactivi..j<=n... }//main }//class 14. ¸ ın Orice frigider are un dispozitiv cu ajutorul c˘ruia putem stabili temperatura a (constant˘) care va fi ˆ interiorul acelui frigider (exprimat˘ ˆ a ın a ıntr-un num˘r ˆ a ıntreg de grade Celsius).GREEDY out.j++) if(cost[i][j] < oo) out. METODA OPTIMULUI LOCAL . pentru a evita accidentele sau deprecierea reactivilor. // afisez muchiile .OJI2004 cls 9 ˆ Intr-un laborator de analize chimice se utilizeaz˘ N reactivi. maxx ] ˆ care trebuie s˘ se ˆ a a ın a ıncadreze temperatura de stocare a acestuia. a a . out.3.i<=n-1. // determin numarul muchiilor . a Se ¸tie c˘.println(nrm). Reactivii vor fi plasati ˆ frigidere.println(i+" "+j). for(i=1. for(i=1. a ¸ ın ¸ se precizeaz˘ intervalul de temperatur˘ [minx . a a a − pe fiecare dintre urm˘toarele N linii se afl˘ min max (dou˘ numere ˆ a a a ıntregi separate printr-un spatiu). numerele de pe linia x + 1 reprezint˘ temperatura ¸ a minim˘. out..close(). nrm=0.j++) if(cost[i][j] < oo) nrm++. ace¸tia tres a s buie s˘ fie stocati ˆ conditii de mediu speciale.i++) for(j=i+1.j<=n.println(gravmax). Cerint˘ ¸a Scrieti un program care s˘ determine num˘rul minim de frigidere necesare ¸ a a pentru stocarea reactivilor chimici. Date de intrare Fi¸ierul de intrare react.

a Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ N ≤ 8000 • −100 ≤ minx ≤ maxx ≤ 100 (numere ˆ ıntregi. ˆ sensul c˘ multimea intervalelor care contin punctul este ın ın a ¸ ¸ mai mare (conform relatiei de incluziune). Se a a observ˘ c˘ aceast˘ alegere este cea mai bun˘.out va contine o singur˘ linie pe care este scris s s ¸ a num˘rul minim de frigidere necesar. a pentru orice x de la 1 la N • un frigider poate contine un num˘r nelimitat de reactivi ¸ a Exemple react.3. ıl ¸ a a ¸ a ¸ ¸ ˆ continuare ne vom concentra pe g˘sirea unei solutii de acest tip.out 3 react. alegem ultimul punct al s˘u ca temperatur˘ a unui frigider. pˆn˘ cˆnd ar ¸ a a a a ajunge la sfˆr¸itul intervalului care se termin˘ cel mai repede. S˘ se aleag˘ un num˘r minim de puncte a a a a a astfel ˆ at fiecare interval s˘ contin˘ cel putin unul dintre punctele alese. Intervalele care contin punctul respectiv sunt eliminate (reactivii corespunz˘tori ¸ a pot fi plasati ˆ ¸ ıntr-un frigider).in 5 -10 10 10 12 -20 10 7 10 78 react. decˆt multimea corespunz˘toare oric˘rei ¸ a ¸ a a alte alegeri. In a ¸ Sort˘m reactivii dup˘ sfˆr¸itul intervalului. EXEMPLE 247 Date de ie¸ire s Fi¸ierul de ie¸ire react. 14/4 ¸ a Problema se poate rezolva prin metoda greedy.out 2 Timp maxim de executie: 1 secund˘/test ¸ a Indicatii de rezolvare . Se observ˘ c˘ noua solutie respect˘ restrictiile din enunt.14. GInfo nr.in 3 -10 10 -25 20 50 react. Aceasta se poate obtine mutˆnd fiecare punct spre dreapta. dintre toate alegerile unui punct a a a a ˆ intervalul respectiv. dintre intervalele as a care ˆ contin. iar procesul continu˘ cu intervalele r˘mase. ˆ care fiecare punct s˘ fie sfˆr¸itul unui ins a ın a as terval.” ıncˆ a ¸ a ¸ Facem o prim˘ observatie: pentru orice solutie optim˘ exist˘ o solutie cu a ¸ ¸ a a ¸ acela¸i num˘r de puncte (frigidere). Pentru intervalul care se termin˘ a a as a cel mai repede.descriere solutie * ¸ ¸ Solutie prezentat˘ de Mihai Stroe. O variant˘ mai explicit˘ a enuntului este urm˘toarea: a a ¸ a ”Se consider˘ N intervale pe o ax˘.in 4 25 57 10 20 30 40 react. ˆ acela¸i a a ın s . deoarece mutarea punctului mai la stˆnga a a nu duce la selectarea unor noi intervale. Acest fapt este adev˘rat.out 2 react. reprezentˆnd grade Celsius).

StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("Reactivi. METODA OPTIMULUI LOCAL . n=(int)st. static int nf=0. ordinul de complexitate al algoritmului de rezolvare a acestei In probleme este O(N · D + N · logN ). unde F este num˘rul de frigidere selectate.248 mod.i<=n. CAPITOLUL 14.*. x1=new int[n+1]. Codul surs˘ a import java.i++) { . PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("Reactivi. In a pe baza vectorului sortat. ¸ a a Citirea datelor de intrare are ordinul de complexitate O(N ). Deoarece fiecare a s a frigider este setat la o temperatur˘ ˆ a ıntreag˘. static int[] x1. Eliminarea intervalelor care contin un anumit punct (sfˆr¸itul intervalului care se termin˘ cel mai repede) ¸ as a are ordinul de complexitate O(N ). F ≤ D. static int ni=0.x2. Se observ˘ c˘ D este cel mult 201. Afi¸area rezultatului are ordinul de complexitate O(1). static int[] ngf. x2=new int[n+1]. are ordinul de complexitate O(1). Urmeaz˘ F pa¸i.GREEDY Analiza complexit˘¸ii at Not˘m cu D num˘rul de temperaturi ˆ a a ıntregi din intervalul care contine tem¸ peraturile din enunt. deoarece ˆ general D > logN .nextToken().j. ngf=new int[n+1]. consider˘m ın a ordinul de complexitate ca fiind O(N · D).in"))). for(i=1. a ˆ cadrul unui pas. class Reactivi { static int n.io. // // // // // n=nr reactivi ni=nr interschimbari in quickSort n=nr frigidere ng=nr grade in frigider limite inferioara/superioara public static void main(String[] args) throws IOException { int i.out"))). Sortarea intervalelor dup˘ cap˘tul din dreapta are ordinul de complexitate a a O(N · logN ). st. determinarea intervalului care se termin˘ cel mai repede. s ˆ concluzie.nval.

nf=1. while(i<n) { while((i<=n)&&(x1[i]<=ngf[nf])) i++. EXEMPLE st.i-1). if(i+1<u) quickSort(i+1. 249 . }// main static void sol() { int i. aux=x2[i]. x2[i]=x2[j]. quickSort(1. if(i<n) ngf[++nf]=x2[i++].nextToken(). if(i!=j) { aux=x1[i]. ngf[nf]=x2[i]. j=u. x2[i]=(int)st.nval. aux=x2[i]. x1[i]=x1[j]. x2[j]=aux.j.u).nval.nextToken(). i=1. } while((i<j)&&((x2[i]<x2[j])|| ((x2[i]==x2[j])&&(x1[i]>=x1[j])))) j--.println(nf). out. x1[i]=x1[j]. x1[j]=aux.aux. } sol(). x1[i]=(int)st. i++.n). out.3.14. x2[i]=x2[j]. int u) { int i. x2[j]=aux. i=p. } } if(p<i-1) quickSort(p. } } static void quickSort(int p. if(i!=j) { aux=x1[i]. x1[j]=aux.close(). st. while(i<j) { while((i<j)&&((x2[i]<x2[j])|| ((x2[i]==x2[j])&&(x1[i]>=x1[j])))) i++.

ONI2005 cls 9 Autor: Silviu G˘nceanu a Printul Algorel este ˆ ˆ ¸ ın ıncurc˘tur˘ din nou: a fost prins de Spˆnul cel a a a Negru ˆ ˆ ın ıncercarea sa de a o salva pe printes˘ ¸i acum este ˆ ¸ as ınchis ˆ Turnul cel ın Mare. ın Prin noul super-telefon al s˘u. iar aceast˘ sum˘ este combinatia magic˘ ce va ıncˆ a a a a ¸ a deschide u¸a.250 } } CAPITOLUL 14.3. Algorel poate evada dac˘ g˘se¸te combinatia magic˘ cu care poate deschide a a s ¸ a poarta turnului. Date de ie¸ire s Prima linie a fi¸ierului de ie¸ire pal. METODA OPTIMULUI LOCAL . iar a a primul num˘r s˘ aib˘ cel putin L cifre a a a ¸ .16 Pal . s ¸ a astfel ˆ at suma lor s˘ fie minim˘. iar s s a a dumneavoastr˘ trebuie s˘-i trimiteti cˆt mai repede numerele cu care poate obtine a a ¸ a ¸ combinatia magic˘. aflati dou˘ numere palindroame cu care se poate a ¸ a obtine combinatia magic˘.GREEDY 14. Numerele palindroame formate nu pot a ˆ ıncepe cu cifra 0. pentru i cu valori de la a a ¸ 0 la 9. ¸ Restrictii ¸i preciz˘ri ¸ s a • ˆ total vor fi cel mult 100 de cifre In • 1 ≤ L < 100 ¸i L va fi mai mic decˆt num˘rul total de cifre s a a • Pentru datele de test va exista ˆ ıntotdeauna solutie: se vor putea forma din ¸ cifrele scrise pe u¸a turnului dou˘ numere care ˆ s a ıncep cu o cifr˘ diferit˘ de 0.in contine un num˘r ˆ s ¸ a ıntreg L reprezentˆnd luna gimea minim˘ a primului num˘r. Dac˘ exist˘ mai ¸ a a a multe solutii se va scrie doar una dintre ele. ¸ ¸ a Date de intrare Prima linie a fi¸ierului pal. ¸ a Cerint˘ ¸a Avˆnd datele necesare. Acum interveniti dumneavoastr˘ ˆ poveste. Urmeaz˘ 10 linii: pe linia i + 2 se va afla un a a a num˘r ˆ a ıntreg reprezentˆnd num˘rul de aparitii ale cifrei i. printul transmite num˘rul de aparitii a fiec˘rei a ¸ a ¸ a cifre de pe u¸a turnului precum ¸i lungimea minim˘ L a primului num˘r. Printul ¸tie cum se formeaz˘ aceast˘ combinatie magic˘: trebuie s˘ utilizeze ¸ s a a ¸ a a toate cifrele scrise pe u¸a turnului pentru a obtine dou˘ numere palindroame. fiind prietenul s˘u ¸ a ın a cel mai priceput ˆ algoritmi. iar cel de-al doilea a a a ¸ poate avea orice lungime diferit˘ de 0.out contine primul num˘r palidrom. s Primul num˘r palindrom trebuie s˘ aib˘ cel putin L cifre. iar s s ¸ a cea de-a doua linie contine cel de-al doilea num˘r palindrom.

out 5 10001 3 222 2 3 0 0 0 0 0 0 0 Explicatie ¸ Pentru acest exemplu avem L = 5. De exemplu a a a a 12321 ¸i 7007 sunt numere palindroame. a a a a a a A¸adar. pentru ˆ s ınceput. pentru ca suma celor dou˘ numere palindroame s˘ fie a a a a minim˘. 3 cifre de 0.descriere solutie ¸ ¸ Solutia oficial˘. s Cele dou˘ palindroame cu care a se genereaz˘ combinatia magic˘ a ¸ a sunt 10001 ¸i 222. Not˘m num˘rul total de cifre a a a a cu N C. a • Fiecare linie din fi¸ierul de intrare ¸i din fi¸ierul de ie¸ire se termin˘ cu s s s s a marcaj de sfˆr¸it de linie. s Combinatia magic˘ va fi suma acestora ¸ a ¸i anume 10223 (care este suma minim˘ s a pe care o putem obtine). ˆ functie de L ˆ modul urm˘tor: ın ¸ ın a 1. dac˘ L < N C/2. s ın s • Pentru 30% dintre teste.3. as Exemplu pal. De asemenea. Silviu G˘nceanu ¸ a a Problema se rezolv˘ utilizˆnd tehnica greedy. pentru alte a 40% din teste num˘rul total de cifre va fi cel mult 18. dac˘ L >= N C/2 apar cazuri particulare ¸i se stabile¸te dac˘ lungimea a s s a primului palindrom cre¸te sau nu cu o unitate. ¸ Timp maxim de executie/test: 1 sec sub Windows ¸i 1 sec sub Linux ¸ s Indicatii de rezolvare . ˆ timp ce 109 ¸i 35672 nu sunt. EXEMPLE 251 • Un num˘r este palindrom dac˘ el coincide cu r˘sturnatul s˘u. s Cifrele de la 3 la 9 lipsesc de pe u¸a turnului. ın ¸ a a . ˆ functie de optimiz˘rile efectuate asupra implement˘rii. 2 cifre de 1¸i 3 cifre de 2. atunci lungimea primului palindrom va fi aleas˘ astfel a a ˆ at cele dou˘ numere s˘ fie cˆt mai apropiate ca lungime ıncˆ a a a 2. observˆnd c˘ cifrele mai mici trebuie s˘ ocupe pozitii cˆt mai semnia a a ¸ a ficative. iar pentru restul de 30% din a teste num˘rul total de cifre va fi mai mare sau egal cu 30. trebuie avut ˆ vedere ca niciunul din cele dou˘ ¸ ın a numere s˘ nu ˆ a ınceap˘ cu cifra 0. Pentru a realiza acest lucru se vor completa ˆ paralel cele dou˘ numere ın a cu cifrele parcurse ˆ ordine cresc˘toare stabilind ˆ care din cele dou˘ numere ın a ın a se vor pozitiona.14.in pal. num˘rul total de cifre va fi cel mult 7. a Datele de test au fost construite astfel ˆ at ¸i solutii neoptime s˘ obtin˘ ıncˆ s ¸ a ¸ a puncte. trebuie ca lungimea maxim˘ a celor dou˘ numere s˘ fie cˆt mai mic˘. gradat. s Avˆnd lungimile numerelor stabilite putem merge mai departe utilzˆnd stratea a gia greedy. Se observ˘ c˘. se stabile¸te lungimea exact˘ a primului num˘r (care va s a a fi cel mai lung).

i++) ncfi=ncfi+(fc[i]%2).nval.L=(int)st. // la inceput cifre mici in p1 class Pal // apoi in ambele in paralel! { static int L. .out"))). PrintWriter out = new PrintWriter ( new BufferedWriter( new FileWriter("pal. // nr total cifre for(i=0. } corectez(p1). break. static int nc1.*.i++) { st. fc[i]=(int)st. while(nc1<nc2) {nc1++. st. case 2: impare2().in"))). static int NC=0.nextToken().} if((ncfi==2)&&(nc1%2==0)) {nc1++. nc2=NC-nc1.out.} p1=new int[nc1]. static int ncfi=0. // nr cifre cu frecventa impara nc1=L. METODA OPTIMULUI LOCAL .i<=9. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("pal. corectez(p2).GREEDY import java. switch(ncfi) { case 0: impare0().nextToken(). nc2--. // palindromul 2 public static void main(String []args) throws IOException { int i.nval.nc2.i<=9.} for(i=0. p2=new int[nc2].println("Date de intrare eronate!").i<=9. // nr cifre cu frecventa impara static int[] fc=new int[10].i++) NC+=fc[i]. case 1: impare1(). // frecventa cifrelor static int[] p1. break.io. nc2--. // palindromul 1 static int[] p2. default: System. for(i=0.252 Codul surs˘ a CAPITOLUL 14. break.

14. out.} } } static void impare1() { int i. int imp1=nc1/2. break. fc[i]--. out.close().} . int imp1=nc1/2.p1).k2. fc[i]--.i<=9.// pentru ca in teste rezultat = suma! for(i=p12. k1++. imp2=nc2/2.i>=0.length-1. while(k1<nc1-nc2) {incarcPozitia(k1. fc[i]--. break. k2=0.print(p2[i]).println().i++) if(fc[i]%2==1) { p1[imp1]=i.println().i>=0.i<p1. k1++.i++) out.k2.print(p1[i]).3. int[] p12=suma(p1.}// incarc numai p1 while((k1<imp1)||(k2<imp2)) { if(k1<imp1) {incarcPozitia(k1.p2). break.p2). for(i=0.i<p2. p2[imp2]=i. out. fc[i]--.println().length.length.} if(k2<imp2) {incarcPozitia(k2.i++) if(fc[i]%2==1) { p2[imp2]=i.k1. }//main static void impare0() // cea mai mare cifra la mijloc { int i.i++) out.i--) if(fc[i]>0) { p1[imp1]=i. if(nc1%2==1) // numai daca nc1 si nc2 sunt impare ! for(i=9. for(i=0. k2++. } k1=0.i<=9. EXEMPLE 253 for(i=0.k1. imp2=nc2/2. out.p1).print(p12[i]).} for(i=0.i--) out.

p1). k1++.254 CAPITOLUL 14. k1++. imp2=nc2/2. fc[i]--.} k1=0. } if(k2<imp2) { incarcPozitia(k2. for(i=0.val. n=x.} // incarc numai p1 while((k1<imp1)||(k2<imp2)) { if(k1<imp1) {incarcPozitia(k1.} if(k2<imp2) {incarcPozitia(k2. if(pozdif0>0) { val=x[pozdif0].} } } static void impare2() { int i. break. } } } static void corectez(int[] x) { int pozdif0. x[pozdif0]=x[n-pozdif0-1]=0.k1.p2).k2. x[0]=x[n-1]=val. METODA OPTIMULUI LOCAL .i++) if(fc[i]%2==1) { p1[imp1]=i.}// incarc numai p1 while((k1<imp1)||(k2<imp2)) { if(k1<imp1) { incarcPozitia(k1.} for(i=0. while(k1<nc1-nc2) {incarcPozitia(k1. k1++. fc[i]--. k2=0.length. } } static void incarcPozitia(int k. while(k1<nc1-nc2) {incarcPozitia(k1.i<=9.i++) if(fc[i]%2==1) { p2[imp2]=i. pozdif0=0.p1). int[] x) . k2++. k2=0.p1).i<=9. k2++.p2). int imp1=nc1/2. k1++. break. while(x[pozdif0]==0) pozdif0++.p1).GREEDY k1=0.

length.length+1) : (y. trebuie s˘ sape un ¸ a ¸ant dispus ˆ linie dreapt˘ ˆ s ¸ ın a ıntre dou˘ puncte A ¸i B.k++) zz[k]=z[k].3. int n=x.i<=9.ONI2006 cls 9 ¸ ¸ Cei n detinuti ai unei ˆ ¸ ¸ ınchisori. } z[z. int k. if(k<x. if(z[z. for(i=0.length-1]!=0) return z.3. return zz. if(k<y. for(k=0. break.i++) if(fc[i]>0) { x[k]=i. for(k=0. situate la distanta de 250 a s ¸ km unul de cel˘lalt.length-1].length-2.length>y. } } 255 static int[] suma(int[] x.length) z[k]+=y[k]. nu?”: el dore¸te s˘ ¸ ıns˘ ¸ ¸ s a sape doar undeva ˆ zona dintre borna x ¸i borna y. ”e doar democratie. EXEMPLE { int i. } } }//class 14. Pe lˆng˘ aceste pretentii ın s a a ¸ ˆ ınchisoarea se confrunt˘ ¸i cu o alt˘ problem˘: nu are suficienti paznici angajati. t=z[k]/10.17 Sant . fc[i]--.k++) { z[k]=t. else { int[] zz=new int[z.length.length-1]=t. numerotati de la 1 la n.length+1)]. Fiecare detinut are ˆ a o pretentie. fc[i]--.k<=z.length) z[k]+=x[k]. int[] y) { int[] z=new int[(x.14. z[k]=z[k]%10. pe care exist˘ 251 borne kilometrice numerotate de la 0 la a a 250.k<zz.t=0. x[n-k-1]=i. as a a ¸ ¸ .length) ? (x.

pentru orice i. ai bi . iar pe linia s a 2j + 1 vor fi scrise numerele de ordine ale detinutilor care sap˘ ˆ aceast˘ zon˘.in are pe prima linie num˘rul n de detinuti. 1 ≤ i ≤ n • 0 ≤ xj ≤ yj ≤ 250. respectˆndu-se a a pretentiile lor.out va contine pe prima linie num˘rul natural k s s ¸ a reprezentˆnd num˘rul minim de paznici necesari pentru paza celor n detinuti sco¸i a a ¸ ¸ s la lucru. Mai exact pe linia i + 1 ¸ a ¸ ¸ (1 ≤ i ≤ n) este descris˘ pretentia detinutului cu num˘rul de ordine i. separate prin cˆte un spatiu reprezentˆnd num˘rul de ordine al a ¸ a a paznicului ¸i bornele kilometrice xj c si yj unde va p˘zi paznicul p.256 CAPITOLUL 14. pentru orice j. 1 ≤ j ≤ k • un detinut poate s˘ sape ¸i ˆ ¸ a s ıntr-un singur punct (”ˆ dreptul bornei kilomeın trice numerotat˘ cu x”) a • ˆ cazul ˆ care exist˘ mai multe solutii se va afi¸a una singur˘ ın ın a ¸ s a • numerele de ordine ale paznicilor vor fi scrise ˆ fi¸ier ˆ ordine cresc˘toare ın s ın a • numerotarea paznicilor ˆ ıncepe de la 1 Exemplu . Pe fiecare s a ¸ ¸ dintre urm˘toarele n linii exist˘ cˆte dou˘ numere naturale. ordonate cresc˘tor. a ¸ a Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ n ≤ 10000 • 0 ≤ ai ≤ bi ≤ 250. 2j+1) va contine pe linia 2j trei numere ¸ ¸ ¸ naturale p xj yj . astfel ˆ at num˘rul de paznici necesari pentru a p˘zi cei n detinuti ¸ ıncˆ a a ¸ ¸ s˘ fie minim. care reprezint˘ pretentia detinutului. ¸ ¸ ın ¸ Date de intrare Fi¸ierul de intrare sant. s˘ se determine locul a a ¸ ¸ s ¸ a (locurile) unde vor fi pu¸i detinutii s˘ sape ˆ s ¸ ¸ a ıntr-o zi de munc˘. ¸ ¸ a ın a a separate prin cˆte un spatiu. a ¸ ¸ a Date de ie¸ire s Fi¸ierul de ie¸ire sant.GREEDY Cerint˘ ¸a Cunoscˆndu-se num˘rul n de detinuti ¸i pretentiile lor. separate printra a a a un spatiu (ai ≤ bi ). Intervalul ˆ care poate p˘zi un paznic nu poate contine dou˘ sau a ın a ¸ a mai multe zone disjuncte dintre cele exprimate de detinuti ˆ preferintele lor. astfel: fiecare pereche de linii (2j. Pe urm˘toarele 2k linii vor fi descrise locurile unde vor fi pu¸i s˘ sape a s a detinutii. METODA OPTIMULUI LOCAL .

iar detinutii p˘ziti a ¸ ¸ a ¸ sunt 2 ¸i 4. s iar detinutul p˘zit este 5 ¸ a !Solutia nu este unic˘! ¸ a Timp maxim de executie/test: 1 secund˘ (Windows). iar detinutul p˘zit este 3 ¸ a 257 5 10 30 30 32 0 30 27 30 27 28 sunt necesari 2 paznici: paznicul 1 va p˘zi la a borna 30. ¸ ¸ a ¸ s paznicul 2 va p˘zi ˆ a ıntre borna 27 ¸i borna 28. s ¸ ¸ a ¸ s paznicul 2 va p˘zi ˆ a ıntre borna 30 ¸i borna 60. EXEMPLE . iar detinutii p˘ziti sunt 1. de fapt. s ¸ a paznicul 2 va p˘zi la borna 5. 2.in 3 0 20 8 13 30 60 4 10 203 2 53 30 403 5 7 33 . iar detinutul p˘zit este 1.5 secunde (Linux) ¸ a Indicatii de rezolvare .out 2 1 8 13 12 2 30 60 3 3 1 10 20 1 255 24 3 30 40 3 2 1 30 30 1234 2 27 28 5 Explicatie ¸ sunt necesari 2 paznici: paznicul 1 va p˘zi ˆ a ıntre borna 8 ¸i borna 13. paznicul 3 va p˘zi ˆ s a ıntre borna 30 ¸i s borna 40. iar s detinutul p˘zit este 3 ¸ a sunt necesari 3 paznici: paznicul 1 va p˘zi ˆ a ıntre borna 10 ¸i borna 20.descriere solutie ¸ ¸ Solutia comisiei ¸ Problema cere. iar detinutii p˘ziti sunt 1 ¸i 2.14. determinarea num˘rului minim de intersectii ˆ a ¸ ıntre segmentele determinate de kilometrul minim ¸i maxim ˆ s ıntre care sap˘ un detinut. a ¸ Pentru a le determina procedez astfel: • ordonez intervalele cresc˘tor dup˘ kilometrul minim ¸i descresc˘tor dup˘ a a s a a kilometrul maxim • pun primul detinut (deci cel cu intervalul de s˘pare cel mai mare) ˆ grija ¸ a ın primului paznic • pentru toate celelalte – caut un paznic care mai p˘ze¸te detinuti ¸i care poate p˘zi ¸i acest a s ¸ ¸ s a s detinut (adic˘ intersectia segmentelor s˘ fie nevid˘) ¸ a ¸ a a – dac˘ g˘sesc a a – ajustez intervalul de s˘pare ca s˘ poat˘ s˘pa ¸i acest detinut a a a a s ¸ – altfel (dac˘ nu g˘sesc) a a – mai am nevoie de un paznic ¸i ˆ dau ˆ grija acestuia s ıl ın .3. 3 ¸i 4. 0.

METODA OPTIMULUI LOCAL . j=ls.j).) public static void main(String[] args) throws IOException . a Codul surs˘ a import java.i.io. class Sant { static int n. } }// while if(li<j) qsort(li. i++. while(i<j) { while(x2[i]<val) i++. a a a a a ˆ momentul cˆnd un interval nu se mai intersecteaz˘ cu cel deja determinat. if(i<=j) { aux=x1[i]. }// qsort(. x2[j]=aux. o[j]=aux. static int[] x2=new int[10001]. este ın a a sigur c˘ nici un alt interval ce ˆ urmeaz˘ nu se mai intersecteaz˘... i=li. o[i]=o[j]. val=x2[(i+j)/2]. x1[j]=aux.*. while(x2[j]>val) j--.GREEDY Datorit˘ faptului c˘ intervalele sunt sortate. aux=o[i].258 CAPITOLUL 14.int ls) { int val. cresc˘tor dup˘ cap˘tul inferior. lucru ce asigur˘ a ıl a a a determinarea num˘rului minim de paznici. x2[i]=x2[j]. static int[] x1=new int[10001]. static int[] o=new int[10001]. static void qsort(int li. aux=x2[i].j.ls). if(i<ls) qsort(i. j--.aux. x1[i]=x1[j].

. } out.k<=n. out. st.nval. EXEMPLE { int k. st. for(k=2.print(o[1]+" ").println(). np=1.k++) { st. for(k=2.println(np+" "+xp+" "+xp). out.nval. }// for k out.n).k<=n.in"))). } else out.14.! np=1. // inca o data pentru . np++. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("sant.. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("sant.k++) if(x1[k]>xp) { np++. out.xp.close().3. o[k]=k..println(np). x1[k]=(int)st. out. for(k=1..print(o[k]+" "). xp=x2[k].out"))). x2[k]=(int)st.nextToken().) }// class 259 .k++) { if(x1[k]>xp) { out.nextToken(). }// main(. } qsort(1. xp=x2[1]. n=(int)st.nval. xp=x2[1]. xp=x2[k].print(o[k]+" ").println(np+" "+xp+" "+xp).k<=n.nextToken().np.

18 Cezar . Problema este de s a a alege cele k str˘zi ¸i amplasarea sediului s˘lii de ¸edinte a Senatului astfel ˆ at. s˘ poat˘ folosi lectica gratuit˘ f˘r˘ a pl˘ti. Restrictii ¸i preciz˘ri ¸ s a . Edilii a a s a au pavat unele dintre leg˘turile directe dintre dou˘ a¸ez˘ri (numind o astfel de a a s a leg˘tur˘ pavat˘ ”strad˘”). METODA OPTIMULUI LOCAL .in contine s ¸ • pe prima linie dou˘ valori n k separate printr-un spatiu reprezentˆnd a ¸ a num˘rul total de senatori ¸i num˘rul de strazi pe care circul˘ lectica gratuit˘ a s a a a • pe urm˘torele n−1 linii se afl˘ cˆte dou˘ valori i j separate printr-un spatiu. ˆ acest scop. ˆ ıntre oricare dou˘ a¸ez˘ri existˆnd leg˘turi directe sau indirecte. a s a s ¸ ıncˆ prin folosirea transportului gratuit. A¸ez˘rile senatoriale sunt numerotate de la 1 s a la n. ei se ¸ a s ¸ In deplaseaz˘ cu lectica. Cezar a promis s˘ stabileasc˘ sediul s˘lii de ¸edinte a Senatului ˆ In a a a s ¸ ıntruna dintre a¸ez˘rile senatoriale aflate pe traseul lecticii gratuite. a a a a ¸ reprezentˆnd numerele de ordine a dou˘ a¸ez˘ri senatoriale ˆ a a s a ıntre care exist˘ strad˘. Cezar a considerat c˘ fiecare senator va c˘l˘tori exact o dat˘ de la ¸ a aa a a¸ezarea sa pˆn˘ la sala de ¸edinte a Senatului.GREEDY 14.260 CAPITOLUL 14. ˆ plus. metroul sau teleportarea nefiind posibile la acea vreme). ˆ calculul costului total de transport.3. O leg˘tur˘ a s a a a a a este direct˘ dac˘ ea nu mai trece prin alte a¸ez˘ri senatoriale intermediare. ın s ¸ s˘ fac˘ economii cˆt mai ˆ a a a ınsemnate. Cezar a promis c˘ va dota Roma cu o lectic˘ a a gratuit˘ care s˘ circule pe un num˘r de k str˘zi ale Romei astfel ˆ at orice senator a a a a ıncˆ care va circula pe str˘zile respective. senatorii. a s ¸a Date de intrare Fi¸ierul cezar. a a La alegerea sa ca prim consul. a a Date de ie¸ire s Pe prima linie a fi¸ierului cezar. Orice senator care se deplaseaz˘ pe o strad˘ pl˘te¸te 1 ban a a a a s pentru c˘ a fost transportat cu lectica pe acea strad˘. pentru In toti senatorii. ˆ drumul lor spre sala de ¸edinte.out se va scrie costul total minim al transs port˘rii tuturor senatorilor pentru o alegere optim˘ a celor k str˘zi pe care va a a a circula lectica gratuit˘ ¸i a locului unde va fi amplasat˘ sala de ¸edinte a Senatuas a s ¸ lui. a a a a aa a Str˘zile pe care se deplaseaz˘ lectica gratuit˘ trebuie s˘ fie legate ˆ a a a a ıntre ele (zborul.OJI2007 cls 11 ˆ Roma antic˘ exist˘ n a¸ez˘ri senatoriale distincte. astfel ˆ at ˆ a a a a ıncˆ ıntre oricare dou˘ a¸ez˘ri senatoriale s˘ a s a a existe o singur˘ succesiune de str˘zi prin care se poate ajunge de la o a¸ezare a a s senatorial˘ la cealalt˘. a a Toti senatorii trebuie s˘ participe la ¸edintele Senatului. cˆte una pentru fiecare In a a s a a dintre cei n senatori ai Republicii. s a a s ¸ Cerint˘ ¸a Scrieti un program care determin˘ costul minim care se poate obtine prin ¸ a ¸ alegerea adecvat˘ a celor k str˘zi pe care va circula lectica gratuit˘ ¸i a locului de a a as amplasare a s˘lii de ¸edint˘ a Senatului.

7-8. pentru 25% din teste 1000 < n ≤ 3000. 8-10 ¸i a s˘lii de ¸edinte a Senatului s a s ¸ ˆ a¸ezarea 8 (dup˘ cum este evidentiat ın s a ¸ ˆ desen).3.5 secunde ¸ Indicatii de rezolvare . de exemplu.. EXEMPLE 261 • 1 < n ≤ 10000. j ≤ n . O(n ∗ (n − k)) sau a a O((n − k) ∗ log(n)). ın Exist˘ ¸i alte alegeri pentru care se obtine as ¸ solutia 11. a • Pentru 25% din teste n ≤ 30. . i = j • Oricare dou˘ perechi de valori de pe liniile 2. .out 11 Explicatie ¸ Costul minim se obtine. a a a • Perechile din fi¸ierul de intrare sunt date astfel ˆ at respect˘ conditiile din s ıncˆ a ¸ problem˘. Exemplu cezar.descriere solutie * ¸ ¸ O implementare posibil˘ utilizeaz˘ metoda GREEDY. ¸ Timp maxim de executie/test: 0.14. 0 < k < n • 1 ≤ i.in 13 3 12 23 28 78 75 54 56 89 8 10 10 11 10 12 10 13 cezar. pentru 10% din teste 3000 < n ≤ 5000.. pentru 10% din teste 5000 < n ≤ 10000. pentru 25% din teste 30 < n ≤ 1000. pentru ¸ alegerea celor 3 str˘zi ˆ a ıntre a¸ez˘rile s a 5-7. n din fi¸ierul de intrare a s reprezint˘ dou˘ str˘zi distincte.. 3.

GREEDY Se elimin˘ succesiv. i=(int)st. st. ca = new int[10001]. n=(int)st..nextToken().nextToken(). se incre¸ menteaz˘ cu 1 costul tat˘lui acesteia. Validitatea metodei rezult˘ din observatia a a a ¸ c˘.nval.k<=n-1. a Se poate retine arborele cu ajutorul listelor de adiacent˘ (liniare sau organi¸ ¸a zate ca arbori de c˘utare). g = new int[10001].nextToken(). st. frunza de a cost minim. v2 = new int[10001]. v1[k]=i. j=(int)st.j.*. for(i=1. static PrintWriter out. tat˘l acesteia poate deveni frunz˘ la rˆndul a a a a lui..k.262 CAPITOLUL 14. . iar frunzele se pot memora ˆ a ıntr-un minheap de costuri. // gradele // ca[k]=costul acumulat in nodul k !!! // nde[k]=nr "descendenti" eliminati pana in k static void citire() throws IOException { int i. static int n. // curatenie .k++) { st.nextToken(). static static static static static int[] int[] int[] int[] int[] v1 = new int[10001]. class cezar { static StreamTokenizer st. v2[k]=j. ns=(int)st.i<=n. La eliminarea unei frunze.nval. dar cu un cost strict mai mare decˆt al frunzei eliminate. structur˘ care se actualizeaz˘ ˆ timp logaritmic. dintre frunzele existente la un moment dat. ns.nval. la eliminarea unei frunze oarecare.nval. for(k=1. st.i++) g[i]=ca[i]=nde[i]=0.io. Toate nodurile au costul initial 1. METODA OPTIMULUI LOCAL . a a ın Codul surs˘ a Varianta 1: import java. nde = new int[10001].

} }// citire(. !!! timin=tata(imin). st=new StreamTokenizer(new BufferedReader(new FileReader("cezar20..i<=n. . timin.cost=0. out=new PrintWriter(new BufferedWriter(new FileWriter("cezar..} } return t. i=frunza .. t=-1.t.in"))). int min.k<=n-1. mai bine cu heapMIN .. aici este testul CORECT .. for(k=1..MAX_VALUE.. imin=i.. // for(i=1. nde[timin]=nde[timin]+nde[imin]+1.14. } initializare aiurea .3.senat=0. imin=-1... ca[timin]=ca[timin]+ca[imin]+nde[imin]+1.. imin.} else if( (v2[k]==i) && (g[v1[k]] > 0) ) {t=v1[k]. g[timin]--. // frunze(g=1) cu cost=min 263 min=Integer. break. } static void eliminm() { int i.. break. // initializarea aiurea . { int k. // nr descendenti eliminati }// elimin() public static void main(String[] args) throws IOException { int k..i++) // if(g[i]==1) // if(nde[i]+1<min) // { min=nde[i]+1.k++) // n-1 muchii { if( (v1[k]==i) && (g[v2[k]] > 0) ) {t=v2[k].) static int tata(int i) // mai bine cu lista de adiacenta . EXEMPLE g[i]++.out"))). g[j]++.. g[imin]--.

out.k++) eliminm().*. out. int[] ca = new int[10001]. } System.i<=i2. int[] g = new int[10001].. static int n. senat=k.s=0.GREEDY for(k=1. CAPITOLUL 14.k++) if(g[k]>0) { cost+=ca[k]. METODA OPTIMULUI LOCAL . for(i=i1. static PrintWriter out.println(cost+" "+senat). int i2) { int i. }// main }// class Varianta 2: import java.print(v[i]+" ").println().k<=n. } System. //afisv(g.println(). static static static static int[] v1 = new int[10001].io. // gradele // ca[k]=costul acumulat in nodul k !!! static void afisv(int[] v. if(i%50==0) System.1.out.. ns.n).close(). int[] v2 = new int[10001].int i1. } .out. for(k=1.i++) { System.264 citire().k<=n-1-ns. { static StreamTokenizer st.println("\n"+cost+" "+senat). out.out. class cezar // cost initial = 1 pentru toate nodurile .

n=(int)st.k++) // este mai bine cu lista de adiacenta ? { if( (v1[k]==i) && (g[v2[k]]>0)) {t=v2[k].nextToken().n-1)..1.k++) { st.n-1).nval. // cost initial in nodul i for(k=1.nval..} else if( (v2[k]==i) && (g[v1[k]]>)) {t=v1[k]. { int k.k<=n-1.i<=n. //afisv(v2. break..1.j. g[i]++. break. for(i=1. st.i++) { g[i]=0...nextToken(). } static void eliminm() { // frunze(g=1) cu cost=min . v1[k]=i.t.k. j=(int)st. st. ns=(int)st.nval. }// citire(. EXEMPLE 265 static void citire() throws IOException { int i.nval.k<=n-1.14.1.n).nextToken(). for(k=1.. // initializarea aiurea . //afisv(g. st.. t=-1. i=(int)st.nextToken(). } // curatenie . v2[k]=j. ca[i]=1. g[j]++. } //afisv(v1..3.) static int tata(int i) // mai bine cu liste de adiacenta .} } return t.

break. citire(). g[imin]--. out=new PrintWriter(new BufferedWriter(new FileWriter("cezar. imin=-1. METODA OPTIMULUI LOCAL .1..senat=0.GREEDY int i. mai bine cu heapMIN .k<=n-1-ns. } //afisv(c.k++) { eliminm().i<=n.k++) if(g[k]>0) { senat=k. timin. for(k=1. s+=min. ca[timin]=ca[timin]+min. for(i=1.i++) if(g[i]==1) if(ca[i]<min) { min=ca[i].. imin. // // // // initializare aiurea .n).in"))). int min. st=new StreamTokenizer(new BufferedReader(new FileReader("cezar20.k<=n. }// elimin() public static void main(String[] args) throws IOException { int k. i=frunza cost acumulat //System. min=Integer.out"))).. } timin=tata(imin).println(" Elimin nodul "+imin+ // " timin = "+timin+" ca = "+ca[timin]). . for(k=1.266 CAPITOLUL 14.. imin=i. g[timin]--.out.MAX_VALUE.

out.close().println("\n"+s+" "+senat). }// main }// class 267 . out.out. EXEMPLE } System.println(s+" "+senat).14.3.

268 CAPITOLUL 14.GREEDY . METODA OPTIMULUI LOCAL .

¸ ¸ 15. 1. m}n definit˘ astfel: a {(s1 .1.. m} poate fi reformulat˘ ca problema determin˘rii submultimii produsului a a ¸ cartezian {1. sn ) ∈ {1.1 Generarea iterativ˘ a produsului cartezian a Presupunem c˘ multimile Si sunt formate din numere ˆ a ¸ ıntregi consecutive cuprinse ˆ ıntre o valoare min ¸i o valoare max. ∀i = j. 2.Capitolul 15 Metoda backtracking Metoda backtracking se utilizeaz˘ pentru determinarea unei submultimi a a ¸ unui produs cartezian de forma S1 × S2 × . Dorim s˘ gener˘m produsul cartezian S1 × S2 × .. j ≤ n}. 9}. 2..... max = 9 s a atunci Si = {0... dac˘ min = 0. 15. . . . .1 Generarea produsului cartezian Pentru ˆ ıncep˘tori. s2 .. nu metoda backtracking ˆ sine este dificil de ˆ ¸eles ci a ın ınt modalitatea de generare a produsului cartezian. problema determin˘rii ¸ a tuturor combin˘rilor de m elemente luate cˆte n (unde 1 ≤ n ≤ m) din multimea a a ¸ {1. s2 . sn ) al submultimii produsului at ¸ cartezian poate fi interpretat ca solutie a unei probleme concrete. ¸ ˆ majoritatea cazurilor nu oricare element al produsului cartezian este solutie In ¸ ci numai cele care satisfac anumite restrictii.... Metoda se aplic˘ numai atunci cˆnd nu exist˘ nici o alt˘ cale de rezolvare a a a a a problemei propuse. × Sn a a 269 .. 2. m}n |si = sj .. . .. Fiecare element (s1 .. deoarece timpul de executie este de ordin exponential.... 1 ≤ i. De exemplu. De exemplu.. × Sn (cu multimile Sk finite) care ¸ are anumite propriet˘¸i...

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

. n n+1 Aici k = 4 = n ¸i 9 = max... se plaseaz˘ prima valoare valid˘ (deci ın s ¸ a a a valoarea min). ˆ ¸elegem de ce este util s˘ initializ˘m ınt a ¸ a variabila gol cu valoarea min − 1. deci k = k + 1 0 1 2 3 4 5 0 1 0 1 . . Pe pozitia k se poate pune o nou˘ valoare: s ¸ a • se pune gol+1= −1 + 1 = 0 = urm˘toarea valoare pe pozitia k a ¸ .... deci k = k − 1 a 0 1 2 3 4 5 0 0 9 0 1 .... a a ın 0 1 2 3 4 5 0 0 1 0 1 .15. ˆ urma pasului f˘cut spre dreapta!)... . n n+1 Aici k = 3 ¸i gol=-1< max. Pe pozitia k se poate pune o nou˘ valoare ¸i atunci: s ¸ a s • se pune 0 + 1 = 1 = urm˘toarea valoare pe pozitia k a ¸ • se face pasul spre dreapta... se face pasul spre dreapta (k = k + 1). . n n+1 Aici (dac˘ nu am ie¸it ˆ afara vectorului. 0 1 2 3 4 5 0 0 1 0 0 1 . deci ˆ acest caz cu 1. n n+1 Dup˘ plasarea unei valori pe pozitia k... GENERAREA PRODUSULUI CARTEZIAN 271 Pe pozitia k = 3 se af˘ valoarea 0 care se va schimba cu urm˘toarea valoare ¸ a a (dac˘ exist˘ o astfel de valoare ≤ max)... deci k = k − 1 a 0 1 2 3 4 5 0 0 0 1 .. Pe pozitia k nu se mai poate pune o nou˘ valoare ¸i s ¸ a s atunci: • se pune gol pe pozitia k ¸ • se face pasul spre stˆnga... n n+1 Aici k = 3 ¸i 9 = max...1. a ¸ 0 1 2 3 4 5 0 0 1 0 1 . S˘ consider˘m c˘ s-a ajuns la configuratia a a a ¸ 0 1 2 3 4 5 0 0 9 9 0 1 . n n+1 Aici k = 2 ¸i 0 < max... a s ın ın a ˆ cazul nostru k = 4 ¸i pozitia este goal˘. . . ... Pe pozitia k nu se mai poate pune o nou˘ valoare s ¸ a ¸i atunci: s • se pune gol pe pozitia k ¸ • se face pasul spre stˆnga.. . n n+1 Cum trecerea de la o valoare la alta se face prin cre¸terea cu o unitate a valorii s vechi iar acum facem trecerea gol → min..

n n+1 A ap˘rut ˆ mod evident urm˘toarea regul˘: ajungˆnd pe pozitia k ≤ n..... ...... n n+1 iar aici k = n + 1 ¸i am ajuns ˆ afara vectorului! Nu-i nimic! Se afi¸eaz˘ s ın s a solutia 0100 ¸i se face pasul la stˆnga.. a ın a a a ¸ ˆ ıncerc˘m s˘ punem urm˘toarea valoare (gol are ca ”urm˘toarea valoare” pe min) a a a a pe aceast˘ pozitie ¸i a ¸ s • dac˘ se poate: se pune urm˘toarea valoare ¸i se face pasul spre dreapta... . Aici k = 4 ¸i 0 < max..272 CAPITOLUL 15. 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¸te pozitia ¸i ajungem la k = 0 s ¸ s 0 1 ..... n n+1 0 1 2 3 4 5 iar aici k = 0 ¸i am ajuns ˆ afara vectorului! s ın 0 1 . n n+1 0 1 2 3 4 5 9 9 9 se gole¸te pozitia ¸i ajungem la k = 2 s ¸ s 0 1 . .... .. . . a a s • dac˘ nu se poate: se pune gol=min − 1 ¸i se face pasul spre stˆnga.. ... .... Ajungem la k = 4 s a ¸ 0 1 ... deci k = k + 1 0 1 2 3 4 5 0 1 0 1 0 1 .. Pe pozitia k se ¸ s a s ¸ poate pune o nou˘ valoare: a • se pune 0 + 1 = 1 = urm˘toarea valoare pe pozitia k a ¸ • 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! . n n+1 0 1 2 3 4 5 9 9 9 9 se gole¸te pozitia ¸i ajungem la k = 3 s ¸ s 0 1 . n n+1 0 1 2 3 4 5 9 9 se gole¸te pozitia ¸i ajungem la k = 1 s ¸ s 0 1 . deci k = k + 1 0 1 2 3 4 5 0 1 0 0 1 . a s a Presupunem c˘ s-a ajuns la configuratia a ¸ 0 1 2 3 4 5 9 9 9 9 care se afi¸eaz˘ ca solutie.... Pe pozitia k se poate pune o nou˘ valoare: s ¸ a • se pune gol+1= −1 + 1 = 0 = urm˘toarea valoare pe pozitia k a ¸ • se face pasul spre dreapta. n n+1 Aici k = 4 ¸i gol=-1< max. ...

println().. se construie¸te prima solutie s ¸ 2.1. se plaseaz˘ pozitia ˆ afara vectorului (ˆ dreapta) a ¸ ın ın 3. long t1. gol=min-1.println("Timp = "+(t2-t1)+" ms"). dac˘ este deja construit˘ o solutie a a ¸ 5.out. } t2=System. Implementarea acestui algoritm ˆ Java este urm˘toarea: ın a class ProdCartI1 // 134. se afi¸eaz˘ solutia ¸i se face pasul spre stˆnga s a ¸ s a 6. static void afis(int[] a) { for(int i=1. cu conventiile k = 0 precizeaz˘ terminarea ¸ ın ¸ ¸ a gener˘rii. else a[k--]=gol. iar k = n + 1 precizeaz˘ faptul c˘ s-a construit o solutie care poate fi a a a ¸ afi¸at˘. GENERAREA PRODUSULUI CARTEZIAN 273 Putem descrie acum algoritmul pentru generarea produsului cartezian S n .i++) System.out.. max}: • structuri de date: − a[1. while (k>0) if (k==n+1) {/* afis(a).currentTimeMillis(). unde S = {min. } public static void main (String[] args) { int i. revenire la 3. .print(a[i]). for(i=1.out. k=n+1. se m˘re¸te cu 1 valoarea ¸i se face pasul spre dreapta a s s 8. */ --k.t2. max=14. altfel. dac˘ se poate m˘ri valoarea pe pozitia curent˘ a a ¸ a 7. t1=System.000 ms pentru 8 cu 14 { static int n=8.} else { if (a[k]<max) ++a[k++].i++) a[i]=min. min=1. static int[] a=new int[n+1]. System. s a • proces de calcul: 1.currentTimeMillis(). } } . se gole¸te pozitia curent˘ ¸i se face pasul spre stˆnga s ¸ as a 3’. System..i<=n.i<=n.n] vectorul solutie ¸ − k ”pozitia” ˆ vectorul solutie.. min + 1.15. altfel. k. cˆt timp nu s-a terminat generarea a 4.

while (k>0) if (k==n+1) {/* afis(a).i<=n. */ --k.println("Timp = "+(t2-t1)+" ms").print(a[i]). t1=System. System.currentTimeMillis().i<=n. } t2=System. } } 15.i++) System. k. static int[] a=new int[n+1].out.000 ms pentru 8 cu 14 { static int n=8.currentTimeMillis(). else a[k--]=gol.750 ms pentru 8 cu 14 { static int n=8. METODA BACKTRACKING O alt˘ variant˘ ar putea fi initializarea vectorului solutie cu gol ¸i plasarea a a ¸ ¸ s pe prima pozitie ˆ vector. } . System.out. System.out. static void afis(int[] a) { for(int i=1. long t1.out.i++) a[i]=gol. static void afis(int[] a) { for(int i=1.max=14.274 CAPITOLUL 15. min=1.print(a[i]). max=14.println(). gol=min-1. min=1. } public static void main (String[] args) { int i. Nu mai avem ˆ acest caz o solutie gata construit˘ dar ¸ ın ın ¸ a algoritmul este acela¸i. for(i=1.2 Generarea recursiv˘ a produsului cartezian a class ProdCartR1 // 101.out.i++) System.i<=n.println().t2. Implementarea ˆ acest caz este urm˘toarea: s ın a class ProdCartI2 // 134.1. k=1. static int[] a=new int[n+1].} else { if (a[k]<max) ++a[k++].

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

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

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

s • procesul de c˘utare ¸i revenire este continuat pˆn˘ cˆnd este g˘sit˘ o solutie a s a a a a a ¸ (dac˘ este suficient˘ o solutie) sau pˆn˘ cˆnd au foste testate toate configuratiile a a ¸ a a a ¸ posibile (dac˘ sunt necesare toate solutiile). × An . METODA BACKTRACKING Specificul metodei const˘ ˆ maniera de parcurgere a spatiului solutiilor. a ın ¸ ¸ • solutiile sunt construite succesiv.m. 1}4 . ıncˆ a a a ¸ • la completarea componentei k se verific˘ dac˘ solutia partial˘ (x1 . a • alegerea unei valori pentru o component˘ se face ˆ a ıntr-o anumit˘ ordine a astfel ˆ at s˘ fie asigurat˘ o parcurgere sistematic˘ a spatiului A1 × A2 × . • dac˘ au fost ˆ a ıncercate toate valorile corespunz˘toare componentei k ¸i ˆ a a s ınc˘ nu a fost g˘ sit˘ o solutie.. a ¸ ˆ figur˘ este ilustrat modul de parcurgere a spatiului solutiilor ˆ cazul In a ¸ ¸ ın gener˘rii produsului cartezian{0. 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 .. la fiecare etap˘ fiind completat˘ cˆte o ¸ a a a component˘.d.a.. . se revine a a ¸ s s ¸ la componenta anterioar˘ (k−1) ¸i se ˆ a s ıncearc˘ urm˘toarea valoare corespunz˘ toare a a a acesteia. sau dac˘e dore¸te determinarea unei noi solutii. x2 . s˘ nu fie dou˘ In ın ¸ a a componente al˘turate egale) anumite ramuri ale structurii arborescente asociate a sunt abandonate ˆ ınainte de a atinge lungimea n. xk ) a a ¸ ¸ a verific˘ conditiile induse de restrictiile problemei (acestea sunt numite conditii de a ¸ ¸ ¸ continuare)...278 CAPITOLUL 15. ¸. a ˆ cazul ˆ care sunt specificate restrictii (ca de exemplu.

A2 . ˆ caz contrar urmˆnd a se continua a ın a c˘utarea succesorului. a − dac˘ nu avem succesor. a a a a In procedura se autoapeleaz˘ pentru k + 1. ¸ ¸ ¸ ˆ aplicatiile concrete solutia nu este obtinut˘ numai ˆ cazul ˆ care k = n In ¸ ¸ ¸ a ın ın ci este posibil s˘ fie satisf˘cut˘ o conditie de g˘sire a solutiei pentru k < n (pentru a a a ¸ a ¸ anumite probleme nu toate solutiile contin acela¸i num˘r de elemente). initiarizarea vectorului solutie ¸ ¸ k=1. An .2. ın ¸ a s a − cˆnd am g˘sit un succesor. a . ¸ ¸ ¸ x[k] = i ˆ ınseamn˘ c˘ pe pozitia k din solutia x se afl˘ ai din Ak . ¸ • se identific˘ multimile A1 .. –k. o afi¸am ¸i revenim pe nivelul anterior.. Principiul de functionare al functiei a a a a ¸ f (primul apel este f (1)) corespunz˘tor unui nivel k este urm˘torul: a a − ˆ situatia ˆ care avem o solutie.2. A2 .2 Backtracking recursiv Varianta recursiv˘ a algoritmului backtracking poate fi realizat˘ dup˘ o a a a schem˘ asem˘n˘toare cu varianta recursiv˘. ın ¸ ın ¸ s˘ s − ˆ caz contrar. ..1 Bactracking iterativ Structura general˘ a algoritmului este: a for(k=1.2. ¸ ¸ s a 15. METODA BACTRACKING 279 ˆ aplicarea metodei pentru o problem˘ concret˘ se parcurg urm˘toarele In a a a etape: • se alege o reprezentare a solutiei sub forma unui vector cu n componente.k<=n. verific˘m dac˘ este valid.} atunci: afi¸are solutie ¸i pas stˆnga s ¸ s a else altfel: { if(x[k]<max[k]) dac˘ exist˘ elemente de ˆ a a ıncercat if(posibil(1+x[k])) dac˘ 1+x[k] este valid˘ a a ++x[k++]. a a ¸ • pornind de la restrictiile problemei se stabilesc conditiile de validitate ale ¸ ¸ solutiilor partiale (conditiile de continuare). atunci m˘resc ¸i fac pas dreapta a s else ++x[k]. a a ¸ ¸ a 15.15. An ¸i se stabile¸te o ordine ntre elemente a ¸ s s ˆ care s˘ indice modul de parcurgere a fiec˘rei multimi.. se trece la nivelul inferior k − 1 prin ie¸irea din a s procedura recursiv˘ f . ˆ caz afirmativ. se initializeaz˘ nivelul ¸i se caut˘ un succesor. Mai precis.. . pozitionare pe prima component˘ ¸ a while (k>0) cˆt timp exist˘ componente de analizat a a if (k==n+1) dac˘ x este solutie a ¸ {afis(x).k++) x[k]=gol. altfel golesc ¸i fac pas dreapta s } Vectorul solutie x contine indicii din multimile A1 .. altfel m˘resc ¸i r˘mˆn pe pozitie a s a a else x[k–]=gol.

j ∈ {1. .print(a[i]). .. ¸ Restrictiile ¸i conditiile de continuare: Dac˘ x = (x1 ... .1 Generarea aranjamentelor Reprezentarea solutiilor: un vector cu n componente. ¸ ¸ a a a ¸ Prelucrarea solutiilor: Fiecare solutie obtinut˘ este afi¸at˘. m}. gol=min-1. x2 . Un vector cu k a ¸ elemente (x1 . 2. . int pozmax) { for(int i=1.3.. k}.i<=n. } static boolean gasit(int val.i<=n. k=1. METODA BACKTRACKING 15. static void afis(int[] a) { for(int i=1..i++) System. min=1. System.3 Probleme rezolvate 15. unde i.. while (k>0) if (k==n+1) {afis(a). return false. Cˆnd k = n + 1 a fost g˘sit˘ o solutie. k.i++) a[i]=gol.i++) if (a[i]==val) return true. 2.. ¸ Multimile Ak : {1.. static int[] a=new int[n+1]. x2 .out.280 CAPITOLUL 15.. for(i=1...println(). xn ) este o solutie ¸ s ¸ a ¸ ea trebuie s˘ respecte restrictiile: xi = xj pentru oricare i = j. } public static void main (String[] args) { int i.. Conditia de g˘sire a unei solutii: Orice vector cu n componente care respect˘ ¸ a ¸ a restrictiile este o solutie. xk ) poate conduce la o solutie numai dac˘ satisface conditiile ¸ a ¸ de continuare xi = xj pentru orice i = j.i<=pozmax. --k. ¸ ¸ ¸ a s a class GenAranjI1 { static int n=2.max=4.} else .out.

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

282 else a[++k]=0. System.println(). } } public static void main(String[] args) { f(1).out. i<=m. } }// class . i++) { if(k>1) // nu este necesar ! { gasit=false. static int[] a=new int[n+1].out. } } } CAPITOLUL 15. m=4.j<=k-1.print(++nsol+" : "). nsol=0. boolean gasit. // in for j } if(gasit) continue. for(int i=1. break.i++) System. // in for i } a[k]=i.j++) if(i==a[j]) { gasit=true. METODA BACKTRACKING class GenAranjR1 { static int n=2. else afis().j. static void afis() { System.out. } static void f(int k) { int i.print(a[i]+" "). for(j=1. if(k<n) f(k+1). for(i=1.i<=n.

public static void main(String[] args) { n=2. } } static void afisv() { int i. int i.j. for nu se executa ! for(j=1.out. a[k]=i.j++) if(i==a[j]) { ok=false. m=4. i<=n. if(k<n) f(k+1).3.m.i++) { ok=true. // k=1 ==> nu am in stanga . for(i=1.println().out. else afisv(). PROBLEME REZOLVATE class GenAranjR2 { static int[] a.j<k.. System.print(a[i]). f(1). } 283 static void f(int k) { boolean ok.i<=m.15. i++) System. for(i=1. } if(!ok) continue. a=new int[n+1].. static int n. } } . break.

println("Timp = "+(t2-t1)+" ms").max=14.i<=n. // initializat si a[0] care nu se foloseste!! k=1.i++) System. System. System.. static int gol=min-1.out.currentTimeMillis(). System. else ++a[k].284 CAPITOLUL 15. k. min=1.currentTimeMillis().t2.. // maresc si pas dreapta (k>1) .i<=n. METODA BACKTRACKING 15. } public static void main (String[] args) { int i.nv=0.out. } }// class 40 ms cu 8 14 fara afis .i++) a[i]=gol. t1=System. for(i=0.print(a[i]). static int[] a=new int[n+1]. static void afis(int[] a) { int i. while (k>0) if (k==n+1) { afis(a).2 Generarea combin˘rilor a Sunt prezentate mai multe variante (iterative ¸i recursive) cu scopul de a s vedea diferite modalit˘¸i de a cˆ¸tiga timp ˆ executie (mai mult sau mai putin at as ın ¸ ¸ semnificativ!). class GenCombI1a // 4550 ms cu 12 22 // { // 7640 ms cu 8 14 cu afis static int n=8. for(i=1.print(++nv+" : "). } else { if(a[k]<max) if(1+a[k]>a[k-1]) ++a[k++].println(). long t1. --k. // maresc si raman pe pozitie else a[k--]=gol. } t2=System.3.out.out.

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

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

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

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

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

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

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

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

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

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

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

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

int y) { int xu.j++) System.15.yu. ni++..rezultatIncercare.yu). for(int j=1.3. rezultatIncercare=ESEC.out. if((xu>=1)&&(xu<=n)&&(yu>=1)&&(yu<=n)) if(tabla[xu][yu]==LIBER) { tabla[xu][yu]=i. int x.println().i<=n. k=1. PROBLEME REZOLVATE for(int i=1.k. yu=y+b[k].i++) { System. if(rezultatIncercare==ESEC) tabla[xu][yu]=LIBER.out. while ((rezultatIncercare==ESEC)&&(k<=8))// miscari posibile cal { xu=x+a[k]. if (i<np) { rezultatIncercare=incerc(i+1. } else rezultatIncercare=SUCCES.xu. }// while return rezultatIncercare. // afisare(). } k++. } }// afisare() 297 static int incerc(int i. }// incerc() }// class Pe ecran apar urm˘toarele rezultate: a Dimensiunea tablei de sah [3.print("\t"+tabla[i][j]).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 .j<=n.

a s a Conditia de continuare pe care trebuie s˘ o satisfac˘ solutia partial˘ (x1 . .1.// culorile sunt 1. .2.. static int[] culoare=new int[n].// Braila (3) {1. . xn ) cu ta s a xi ∈ {1.2} // Tulcea (4) }..length.2. . METODA BACKTRACKING 15..1. m}.0. Multimile de valor ale a a ta ¸ elementelor sint A1 = A2 = . } // main static void harta() { int k.0.0}.1.1. 2.// Ialomita (2) {1.1. public static void main(String[] args) { harta(). . x2 .298 CAPITOLUL 15.. Relatia de vecin˘tate ıncˆ a ta a ¸ a dintre ¸˘ri este retinut˘ ˆ ta ¸ a ıntr-o matrice n × n ale c˘rei elemente sunt: a vi.println("Nu se poate colora !")...j = 1.5 Problema color˘rii h˘rtilor a a ¸ Se consider˘ o hart˘ cu n ¸˘ri care trebuie colorat˘ folosind m < n culori.k = 1. m} reprezentˆnd culoarea asociat˘ ¸˘rii i.out..2. static int n=harta.. static int nrVar=0. xk ) ¸ a a ¸ ¸ a este: xk = xi pentru orice i < k cu proprietatea c˘ vi..1}. if(nrVar==0) System. a a ta a astfel ˆ at oricare dou˘ ¸˘ri vecine s˘ fie colorate diferit.j = 1 dac˘ i este vecin˘ cu j a a 0 dac˘ i nu este vecin˘ cu j a a Reprezentarea solutiilor: O solutie a problemei este o modalitate de col¸ ¸ orare a ¸˘ rilor ¸i poate fi reprezentat˘ printru-un vector x = (x1 .2.1. = An = {1..1.1. x2 .3.. 2. Restrictii ¸i conditii de continuare: Restrictia ca dou˘ ¸˘ri vecine s˘ fie col¸ s ¸ ¸ a ta a orate diferit se specific˘ prin: xi = xj pentru orice i ¸i j avˆnd proprietatea vi.// Calarasi (1) {1.3 static int[][] harta= {// CoCaIaBrTu {2.. // indicele pentru tara boolean okk..0.// Constanta (0) {1..0.1}. a class ColorareHartiI1 { static int nrCulori=3.0}.

else if (k == (n-1)) afis().println(). okk=posibil(k). if (okk) break. for(int i=0.i<n.out. while(culoare[k] < nrCulori)// selectez o culoare { ++culoare[k].out.print(culoare[i]+" "). // tara k nu este colorata (inca) while (k>-1) // -1 = iesit in stanga !!! { okk=false.15. // prima pozitie culoare[k]=0. } if (!okk) k--. } } // harta static boolean posibil(int k) { for(int i=0.out. } // 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 299 . PROBLEME REZOLVATE k=0. System. } // posibil static void afis() { System. else culoare[++k]=0.i++) System.3.i<=k-1. return true.i++) if((culoare[k]==culoare[i])&&(harta[i][k]==1)) return false.print(++nrVar+" : ").

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

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

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

a s a O camer˘ are ie¸irea numai spre N dac˘ ¸i numai dac˘ a[i][j]&&8 = 0. if (ok) { x[k]=i. avˆnd semnificatie faptul c˘ acea camer˘ are ¸ a ¸ a a sau nu ie¸iri pe directiile considerate. a s as a Drumul parcurs la un moment dat se retine ˆ ¸ ıntr-o matrice cu dou˘ linii. deplasarea se poate face ˆ patru ¸ ın directii: Nord.i<n. for(i=0. s a a ın Prin urmare. Est.i++) y[x[i]]=i. a La g˘sirea unei ie¸iri din labirint. s ¸ De exemplu.3.n-1 System.. dac˘ pentru camera cu pozitia (2. drumul este afi¸at. ˆ a a Intr-una din camere. a ˆ care: ın − d[1][k] reprezint˘ linia camerei la care s-a ajuns la pasul k.2. codific˘m labirintul printr-o matrice a[i][j] cu elemente ˆ a ıntre 1 ¸ai 15. dar odat˘ aleas˘ aceasta se p˘streaz˘ pˆn˘ la a ¸ a a a a a a sfˆr¸itul problemei). a s s Principiul algoritmului este urm˘torul: a . j) din labirint.i++) System. reasez(k+1).. s a s a a a s O prim˘ problem˘ care se pune este precizarea modului de codificare a a a ie¸irilor din fiecare camer˘ a labirintului.out.. PROBLEME REZOLVATE 303 ok=(ok&&((Math. // ci 1.7 Problema labirintului Se d˘ un labirint sub form˘ de matrice cu m linii ¸i n coloane.. s a a Ne punem problema determin˘rii ie¸irilor pe care le are o camer˘.out.1. matricea se bordeaz˘ cu dou˘ linii s s s a a ¸i dou˘ coloane de valoare egal˘ cu 16. ei ˆ a ¸ a s s ıi va corespunde ¸irul 1010 care reprezint˘ num˘rul 10 ˆ baza zece.abs(i-x[k-1])==2)||(Math.abs(i-x[k-1])==3))).n (la afisare) } // scrieRez } // class 15. Fiecare element a a s al matricei reprezint˘ o camer˘ a labirintului.// inversarea permutarii !!! System..} } } // reasez static void scrieSolutia() { int i. for(i=0.out. a − d[2][k] reprezint˘ coloana camerei respective. 4) exist˘ ie¸iri la N ¸i S.. // ca sa nu fie 0. de coordonate x0 ¸i y0 se g˘ se¸te un om. d.3. s a Dintr-o pozitie oarecare (i.print(++nrVar+" : ").print(++y[i]+" ")..15. as Fiecare camer˘ din labirint poate fi caracterizat˘ printr-un ¸ir de patru cifre a a s binare asociate celor patru directii. Sud ¸i Vest considerate ˆ aceast˘ ordine (putem alege oricare ¸ s ın a alt˘ ordine a celor patru directii. Pentru a testa u¸or ie¸irea din labirint.println().. Se cere s˘ se g˘ seasc˘ toate ie¸irile din labirint.i<n.

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

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

static int[] x=new int[n+1]. a s ¸ s a Din valoarea care se g˘se¸te pe un nivel.currentTimeMillis(). } if(val==0) { nsol++.min(val-i. return..print(x[i]+" "). n=6. f(val-i. } } static void afis1() { System.. for(int i=1.i<=dim. afis2(val.t2.currentTimeMillis(). } . a class PartitieNrGenerare // n = suma de numere { static int dim=0. } static void f(int val. a a La revenire se reface valoarea existent˘.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n"). } int maxok=min(maxp. int poz) { if(maxp==1) { nsol++.i>=1.i--) { x[poz]=i..3. S[k]. int maxp. for(int i=maxok.out.poz+1).i++) System.i). se scad pe rˆnd valorile a s a 1.n. s ¸ ¸ a a a Initial.306 CAPITOLUL 15. . METODA BACKTRACKING 15. t2=System. Imediat ce este ¸ a s apelat˘. dim=poz-1. System. procedura este apelat˘ pentru nivelul 1 ¸i valoarea n.maxp).val).8 Generarea partitiilor unui num˘r natural ¸ a S˘ se afi¸eze toate modurile de descompunere a unui num˘r natural n ca a s a sum˘ de numere naturale. dim=poz-1. t1=System.out. 2. f(n. public static void main(String[] args) { long t1.print("\n"+nsol+" : "). afis1(). procedura va apela o alta pentru afi¸area vectorului (initial afi¸eaz˘ n). S[k] − 1. a Vom folosi o procedur˘ f care are doi parametri: componenta la care s-a a ajuns (k) ¸i o valoare v (care contine diferenta care a mai r˘mas pˆn˘ la n).1).out. valori cu care se apeleaz˘ procedura pentru nivelul urm˘tor. return. nsol=0.

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

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

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

class ParantezeGenerare // 2*n paranteze { static int nsol=0. else { if(npd<n) { x[k]=1.out.k<=n2.out. } if(npi<npd) { x[k]=2.npi+1).npd. System.out.currentTimeMillis(). t2=System. } static void f(int k. f(1.print(++nsol+" : ").npi). } } } static void afis() { int k. int npd.out.310 CAPITOLUL 15.0.3.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n").npd+1.print(") ").k++) if(x[k]==1) System. static int n2=2*n.print("( "). f(k+1. static int[] x=new int[n2+1]. f(k+1.t2. t1=System. static int n=4. METODA BACKTRACKING 15. } }// class . else System. public static void main(String[] args) { long t1.currentTimeMillis().out.0). System.println().9 Problema parantezelor Problema cere generarea tuturor combinatiilor de 2n paranteze (n paranteze ¸ de deschidere ¸i n paranteze de ˆ s ınchidere) care se ˆ ınchid corect. int npi) { if(k>n2) afis(). System. for(k=1.

BLACK=2.15. for(k=1.nods.k<=m.nextToken(). p[i]=-1..io. p=new int[n+1].m.f.nval.color.predecesor.nextToken().3.3..nval. a[i][j]=1.nextToken().nval.k. !!! { color[i]=WHITE. // descoperit.nextToken(). j=(int)st. nodd=(int)st.nval. m=(int)st. n=(int)st.t. static int n. import java.nval.nval. // arborele DFS (parcurgere in adancime) class DFS // momentele de descoperire si finalizare a nodurilor { // drum intre doua varfuri static final int WHITE=0. a=new int[n+1][n+1]. int i. } .k++) { st.culoare static int[][] a.in"))).j.nextToken(). st. st.p. nod_destinatie (drum!) st. // nods=nod_start_DFS.nodd.10 Algoritmul DFS de parcurgere a grafurilor Algoritmul DFS de parcurgerea in adˆncime a grafurilor neorientate foloseste a tehnica backtrackink. color=new int[n+1]. st.i<=n. d=new int[n+1]. public static void main(String[] args) throws IOException { StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dfs.i++) // oricum erau initializati implicit.finalizat. dar .*. GRAY=1. st. static int[] d. a[j][i]=1. nods=(int)st. f=new int[n+1]. } for(i=1. i=(int)st. PROBLEME REZOLVATE 311 15.nextToken().

}// drum(. }//main static void dfs(int u) { int v.print("drum : ").out.println()..print("Finalizat :\t")..out.print(u+" "). System. System. dfs(v). for(v=1. METODA BACKTRACKING t=0.v<=n.out..println(). System.print(x[i]+"\t").out. System.i++) System. d[u]=++t. afisv(d).out. } color[u]=BLACK.out. f[u]=++t. afisv(f).. System. dfs(nods). }//dfs // listele de adiacenta . color[u]=GRAY. } }//class /* 6 7 3 4 1 4 4 6 6 1 drum : 3 1 4 Descoperit : Finalizat : 2 11 5 6 1 12 3 10 4 7 8 9 .) static void afisv(int[] x) { int i.print("Descoperit :\t").i<=n.v++) if(a[u][v]==1) if(color[v]==WHITE) { p[v]=u. System.312 CAPITOLUL 15. for(i=1.out. drum(nodd). !!! // v in Ad(u) !!! static void drum(int u) // nod_sursa ---> nod_destinatie { if(p[u]!=-1) drum(p[u]).

StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("compConexe.nextToken(). ncc=0.nextToken().io. } for(i=1.in"))).nval. PROBLEME REZOLVATE 5 3 2 5 1 3 4 5 */ 313 15. for(k=1.i<=n.i++) . n=(int)st. for(i=1.m.i<=ncc.11 Determinarea componentelor conexe import java.3.nextToken().k<=m. st. m=(int)st. st. i=(int)st.nval.nextToken(). } cc=new int[n+1].3. a=new int[n+1][n+1]. a[i][j]=a[j][i]=1.*.ncc.i++) if(cc[i]==0) { ncc++.j. static int[][] a. conex(i). public static void main(String[] args) throws IOException { int i. j=(int)st. static int [] cc.k++) { st.nval. st.nval.15.k. // determinarea componentelor conexe class CompConexe { static int n.

for(int v=1.*. for(j=1.nctc.out.314 { CAPITOLUL 15.lista.pozLista. METODA BACKTRACKING System.f. GRAY=1.io. // matricea grafului .print(j+" "). System. static int n. } }//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 */ 1 : 1 2 3 2 : 4 5 3 : 6 7 8 9 15. class CompTareConexe { static final int WHITE=0.m.j++) if(cc[j]==i) System. static int[][] a.3.12 Determinarea componentelor tare conexe // determinarea componentelor tare conexe (in graf orientat!) // Algoritm: 1.v<=n.out. static int [] ctc.v++) if((a[u][v]==1)&&(cc[v]==0)) conex(v).println().color. dfs(G) pentru calculul f[u] // 2.out.t=0. dfs(G_transpus) in ordinea descrescatoare a valorilor f[u] // OBS: G_transpus are arcele din G "intoarse ca sens" // Lista este chiar o sortare topologica !!! import java.j<=n.print(i+" : "). BLACK=2.

color[u]=GRAY. // transpusa } for(i=1. System. a=new int[n+1][n+1]. PROBLEME REZOLVATE static int[][] at.print(j+" "). for(k=1. for(j=1. } }//main static void dfsa(int u) { int v.nval. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("compTareConexe.nextToken(). j=(int)st.in"))). 315 // matricea grafului transpus (se poate folosi numai a !) public static void main(String[] args) throws IOException { int i.nval.out.i++) if(color[lista[i]]==WHITE) { nctc++.j++) if(ctc[j]==i) System.k<=m. for(i=1. f=new int[n+1]. st.j.print(i+" : "). lista=new int[n+1]. at[j][i]=1. n=(int)st.i++) if(color[i]==WHITE) dfsa(i).i<=n.i++) color[i]=WHITE.i++) color[i]=WHITE.k++) { st. dfsat(lista[i]). at=new int[n+1][n+1].out. .3.nextToken().k.i<=n.j<=n.nval. i=(int)st.i<=nctc.nextToken(). ctc=new int[n+1].out.i<=n. } for(i=1.println(). st.i<=n. m=(int)st. for(i=1. st.nval.i++) { System.15.nextToken(). color=new int[n+1]. nctc=0. for(i=1. pozLista=n. a[i][j]=1.

.j++) if((at[u][lista[j]]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]).*. for(j=1..v++) if((a[u][v]==1)&&(color[v]==WHITE)) dfsa(v). u .j<=n. lista[pozLista--]=u. METODA BACKTRACKING for(v=1.. 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..io.316 CAPITOLUL 15.3. //"at" //if((a[lista[j]][u]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]).v)=arc ==> ". DFS pentru calcul f[u]. color[u]=GRAY. f[u]=++t. . v . color[u]=BLACK." in lista ("u se termina inaintea lui v") Algoritm: 1.. } }//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 15.v<=n.. //"a" color[u]=BLACK.13 Sortare topologic˘ a Folosind parcurgerea ˆ adˆncime ın a // // // // // // // // Sortare Topologica = ordonare lineara a varfurilor (in digraf) (u. u=nod 2. ctc[u]=nctc. } static void dfsat(int u) // se poate folosi "a" inversand arcele ! { int j.

for(i=1. // lista static int[][] a.i<=n.15.print(lista[i]+" ").nextToken(). muchii.k. GRAY=1.nval.nextToken(). . f=new int[n+1]. t=0. // culoare static int[] lista. // descoperit static int[] f. pozl=n. a=new int[n+1][n+1].i++) color[i]=WHITE.pozl.nval. BLACK=2. st. st. m=(int)st.nods.in"))).println(). j=(int)st. // matricea de adiacenta public static void main(String[] args) throws IOException { int i. System. pozitie in lista static int[] d. PROBLEME REZOLVATE 317 class SortTopoDFS { static final int WHITE=0.out.m. // varfuri. for(k=1. }//main static void dfs(int u) { int v.3. lista=new int[n+1].nods++) if(color[nods]==WHITE) dfs(nods). time. st. for(nods=1. d=new int[n+1].j.i<=n. color[u]=GRAY. } for(i=1. // nods=nod_start_DFS StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("sortTopo. d[u]=++t.nval.nextToken(). a[i][j]=1. color=new int[n+1]. static int n. n=(int)st.t.nextToken().nval. i=(int)st. // finalizat static int[] color.nods<=n.k<=m.i++) System.out.k++) { st.

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

st. for(k=1.i<=n.i<=n. lista[pozl++]=u.i++) System.nval. gi[j]++. st.k<=n.print(lista[i]+" ").v<=n.k<=m. for(v=1.nval.v<=n.out.nextToken().println().15.nextToken().k++) // pun cate un nod in lista { u=nodgi0(). System.nod=-1. a[i][j]=1.v++) // coada cu prioritati (heap) este mai buna !!! if(color[v]==WHITE) if(gi[v]==0) {nod=v.v++) // lista de adiacenta este mai buna !!! . m=(int)st. color[u]=BLACK.nval.k++) { st. gi=new int[n+1]. i=(int)st. break. n=(int)st. }//main static int nodgi0() // nod cu gradul interior zero { int v. j=(int)st.nextToken(). PROBLEME REZOLVATE 319 st. } for(i=1.out. for(v=1. color=new int[n+1]. } static void micsorezGrade(int u) { int v. for(k=1.3.i++) color[i]=WHITE. pozl=1. a=new int[n+1][n+1].nval.} return nod. } for(i=1. micsorezGrade(u).nextToken(). lista=new int[n+1].

m=(int)st. i=(int)st. public static void main(String[] args) throws IOException { int i.j. a[i][j]=a[j][i]=1. } }//class /* 6 4 6 3 1 2 3 4 5 6 */ 1 2 5 6 3 4 15. out.nval.14 Determinarea nodurilor de separare // determinarea nodurilor care strica conexitatea // in graf neorientat conex import java.io. static int[][] a.nval. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("noduriSeparare. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("noduriSeparare. st.nextToken().m. for(k=1. st.nval.3. static int [] cc. } for(i=1.nextToken().nval.print(i+" ").k.*.i<=n. a=new int[n+1][n+1].k++) { st. METODA BACKTRACKING if(color[v]==WHITE) if(a[u][v]==1) gi[v]--.nextToken().out"))).in"))). class NoduriSeparare { static int n.i++) if(!econex(i)) System.out.k<=m.nextToken().320 CAPITOLUL 15. j=(int)st. }//main static boolean econex(int nodscos) { . n=(int)st. st.close().

else return true. a=new int[n+1][n+1]. st.k.cc. . i=(int)st.nval. static int[][]a. if(ncc>1) break. conex(i.nextToken().nextToken(). static int [] cc.v++) if(v!=nodscos) if((a[u][v]==1)&&(cc[v]==0)) conex(v. }//conex }//class 321 15.nextToken().out"))).io. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("muchieRupere. int[] cc=new int[n+1].ncc. st. m=(int)st. ncc=0. PROBLEME REZOLVATE int i.k<=m.nodscos).cc. j=(int)st.nval.et. for(i=1.15 Determinarea muchiilor de rupere import java.3. public static void main(String[] args) throws IOException { int i.15. n=(int)st.int[]cc. }// econex() static void conex(int u.nextToken().i<=n.*. for(k=1.m. // determinarea muchiilor care strica conexitatea class MuchieRupere // in graf neorientat conex { static int n.int et. st.k++) { st.nodscos).in"))).nval.i++) if(i!=nodscos) if(cc[i]==0) { ncc++. } if(ncc>1) return false.j. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("muchieRupere.int nodscos) { cc[u]=et. for(int v=1.v<=n.3.nval.

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

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

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

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

n m 1 8 1 2 1 3 6 3 4 | \ 2 4 | 5 --3 5 | / 5 7 7 5 6 6 7 */ 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 15. a ¸ ın Fiind dat un poligon cu n vˆrfuri notate 1.i<=n.) }//class /* 8 9 <-. METODA BACKTRACKING } while(!((tata==tatas)&&(fiu==fius))). ¸ a ¸ Exemplu . num˘rul de triangulatii distincte.17 Triangulatii .in se afl˘ pe prima linie un singur ın s a num˘r natural reprezentˆnd valoarea lui n (n ≤ 11).. a a Datele de ie¸ire: ˆ fi¸ierul text triang. O diagonal˘ va fi precizat˘ prin dou˘ numere reprezentˆnd cele dou˘ a a a a a vˆrfuri care o definesc.out se vor scrie: s ın s . { for(int i=1.OJI2002 clasa a X-a ¸ O triangulatie a unui poligon convex este o multime format˘ din diagonale ¸ ¸ a ale poligonului care nu se intersecteaz˘ ˆ interiorul poligonului ci numai ˆ vˆrfuri a ın ın a ¸i care ˆ s ımpart toat˘ suprafata poligonului ˆ triunghiuri.i++) if(x[i]==1) System.out..println().. } // compBiconexa(.pe fiecare din urm˘toarele linii cˆte o triangulatie descris˘ prin diagonalele a a ¸ a ce o compun.. }// afisv(. System.out. .. n s˘ se genereze toate a a triangulatiile distincte ale poligonului.326 CAPITOLUL 15. a ¸ .. cele dou˘ numere ce definesc o diagonal˘ se despart prin a a a cel putin un spatiu. Dou˘ triangulatii sunt distincte dac˘ difer˘ ¸ a ¸ a a prin cel putin o diagonal˘. 2. ¸ a Datele de intrare: ˆ fi¸ierul text triang. iar ˆ ¸ ¸ ıntre perechile de numere ce reprezint˘ diagonalele dintr-o a triangulatie se va l˘sa de asemenea minimum un spatiu.) static void afisv(int[] x) // indicii i pentru care x[i]=1.3.print(i+" ")..pe prima linie.

n=(int)st.*.in 5 triang.out 5 1314 2425 5253 3531 4214 327 Timp maxim de executare: 7 secunde/test pe un calculator la 133 MHz. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("triang. st. t1=System. . // numar varfuri poligon static int ndt=n-3.nval. static int[] v1. static PrintWriter out.nextToken().out"))). // numar diagonale in triangulatie static int nd=n*(n-3)/2.v2. ¸ import java.t2. out=new PrintWriter(new BufferedWriter(new FileWriter("triang. // merge si n=12 in 3 sec class Triangulatii { static int n. x=new int[ndt+1]. public static void main(String[] args) throws IOException { long t1.currentTimeMillis(). static int nsol=0. 3 secunde/test pe un calculator la peste 500 MHz. // numarul tuturor diagonalelor static int[] x. Rezolvare detaliat˘ a Se genereaza toate combinatiile de diagonale care formeaza o triangulatie.io. ndt=n-3. PROBLEME REZOLVATE triang.3.15. nd=n*(n-3)/2.in"))). v1=new int[nd+1].

close(). } static void diagonale() { int i.i++) out.} for(i=2.println().k=0. return false.println(catalan(n-2)).j.328 v2=new int[nd+1]. for(i=1.} } static boolean seIntersecteaza(int k. t2=System. v2[k]=j. int i) { int j. // i si x[j] sunt diagonalele !!! for(j=1. } out. METODA BACKTRACKING if(n==3) out.println(0).j<=k-1.j++) if(((v1[x[j]]<v1[i])&&(v1[i]<v2[x[j]])&&(v2[x[j]]<v2[i]) )|| ((v1[i]<v1[x[j]])&&(v1[x[j]]<v2[i])&&(v2[i]<v2[x[j]]))) return true. for(j=3.j++){v1[++k]=i. System. i=1. ++nsol. } static void f(int k) throws IOException { . diagonale().print(v1[x[i]]+" "+v2[x[i]]+" ").println("nsol = "+nsol+" Timp = "+(t2-t1)).j++) {v1[++k]=i.currentTimeMillis().j<=n-1. f(1).out.i<=n-2. CAPITOLUL 15.i++) for(j=i+2.j<=n. } static void afisd() throws IOException { int i. else { out. out. v2[k]=j.i<=ndt.

for(i=2. x[i]=x[i]/d. return rez. r=d%i.i<=n. int[] y=new int[n+1]. y[j]=y[j]/d.} while(i!=0) {c=d/i. d=i. } }// class 329 .} return d. if(a>b) {d=a.i++) x[i]=n+i. if(k<ndt) f(k+1).r. i<=nd-ndt+k. PROBLEME REZOLVATE int i.j++) y[j]=j. } static int cmmdc (int a.i.i<=n. } rez=1. i++) { if(seIntersecteaza(k. } } static int catalan(int n) { int rez.j<=n. for(i=2. int d.i++) { d=cmmdc(y[j].int b) { int d.c.i=a. for(j=2.i<=n.j++) for(i=2. if(y[j]==1) break.i)) continue. for(i=x[k-1]+1.j<=n. int[] x=new int[n+1].i++) rez*=x[i]. x[k]=i.15. else afisd(). int i. i=r.3.} else{d=b. for(j=2.j.i=b.x[i]).

+ nk . . a a Datele de intrare Fi¸ierul partitie. ≥ nk ≥ 1 Cerint˘ ¸a Fiind dat un num˘r natural n.ONI2003 clasa a X-a ¸ Se define¸te o partitie a unui num˘r natural n ca fiind o scriere a lui n sub s ¸ a forma: n = n1 + n2 + . n2 .. (k ≥ 1) unde n1 ..out 7 5 Explicatii: ¸ Cele cinci partitii sunt: ¸ • 1+1+1+1+1+1+1 • 1+1+1+1+3 • 1+1+5 • 1+3+3 •7 Timp maxim de executare: 3 secunde/test Indicatii de rezolvare * ¸ Stelian Ciurea Problema se poate rezolva ˆ mai multe moduri: ın Solutia comisiei se bazeaz˘ pe urm˘toarele formule ¸i teoreme de combina¸ a a s toric˘: a .. ¸ Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ N ≤ 160 Exemplu partitie.in partitie. nk sunt numere naturale care verific˘ urm˘toarea relatie: a a ¸ n1 ≥ n2 ≥ .... conform cerintelor de mai sus..18 Partitie .3. s˘ se determine cˆte partitii ale lui se pot a a a ¸ scrie.. METODA BACKTRACKING 15.330 CAPITOLUL 15.out va contine pe prima linie num˘rul de partitii ale lui n s ¸ a ¸ conform cerintelor problemei. ¸tiind c˘ oricare num˘r ni dintr-o partitie ¸ s a a ¸ trebuie s˘ fie un num˘r impar..in contine pe prima linie num˘rul n s ¸ a Datele de ie¸ire s Fi¸erul partitie. ≥ ni ≥ .

mai mici sau egale cu M . ¸ Rezultatul pentru n = 160 are 8 cifre. s ¸ ¸ . care este transformat ˆ ıntr-un nou program. 2) + . 13/6 . k) − num˘rul de partitii ale unui num˘r n ˆ k p˘rti impare care se pot ¸i repeta a ¸ a ın a¸ s este egal cu num˘rul de partitii ale unui num˘r n ˆ k p˘rti distincte.k s˘ reprezinte num˘rul de partitii ale lui i cu numere impare. + P (n − k.M ≤i. ın s Aceast˘ metod˘ conduce la o rezolvare instantanee a testelor date ˆ concurs. n) = 1 ¸i P (n. ar fi ˆ a a a ınsemnat generarea solutiilor folosind metoda backtracking. a ın a a ¸ din care ultimul este cel mult egal cu al k-lea num˘r impar. k) = P (n − k(k − 1)/2. este format˘ dintr-un un num˘r M .octombrie 2003 Problema se poate rezolva ˆ mai multe moduri.. adic˘ 2 · k − 1. dup˘ ¸ a care se putea folosi metoda vectorului de constante.k = M =1. cel a a a ¸ mult egale cu k. PROBLEME REZOLVATE 331 este − num˘rul de partitii ale unui num˘r n ˆ k p˘rti (nu neap˘rat distincte) a ¸ a ın a¸ a P (n. a ¸ a ın a¸ Problema se poate rezolva ¸i prin backtracking. solutia pentru num˘ul i se g˘se¸te ˆ a ¸ a a s ın Ai. mai mic sau egal cu k. GInfo nr.0 este 1. bazat˘ pe vectorul de constante. a a Dup˘ calcularea elementelor matricei. Un backtracking l˘sat s˘ se execute ˆ ¸ a a ın timpul concursului ar fi putut genera solutiile pentru toate valorile lui N . Pentru valori ¸ ın ¸ mai mari ale lui n. a a ın O alt˘ metod˘. Un a element din A se calculeaz˘ observˆnd c˘ o partitie a lui i cu numere impare. din care ultimul este cel mult egal cu k. 1) + P (n − k. 1) = P (n. f˘r˘ prea mari optimiz˘ri se s aa a poate obtine rezultatul ˆ mai putin de 3 secunde pentru n < 120. se poate alege ¸ ¸ o variant˘ ˆ care Ai. k) = 0 dac˘ n < k. deci nu este necesar˘ implementarea a operatiilor cu numere mari! ¸ Mihai Stroe. k) cu P (n. ın O idee bun˘ de rezolvare a problemei const ˆ folosirea metodei program˘rii a ın a dinamice. Ai−M. ˆ timp ce un backtracking ın folosit ca solutie a problemei ar fi obinut punctajul maxim doar pentru testele mici ¸ ¸i medii (pentru valori ale lui N mai mici decˆt 130). De aici rezult˘ relatia: a ¸ Ai.3. se poate l˘sa programul s˘ ruleze ¸i se retin rezultatele ˆ a a s ¸ ıntr-un vector cu valori initiale.. k) = P (n − k.M M =impar Initial A0. La implementare. ¸i alte a a s numere. unde Ai... pentru a economisi spatiu. ˆ acela¸i mod ca la problema ”Circular” de la clasa a IX-a.15.k reprezint˘ num˘rul de partitii a a ¸ ale num˘rului i cu numere impare. s a Limitele problemei ¸i timpul de execuie de 3 secunde permit rezolv˘rii prin s a backtracking ¸ btinerea unui punctaj multumitor. Aceste valori pot fi salvate ˆ ıntrun vector de constante. Se poate construi o matrice A.k..i .. s a − num˘rul de partitii ale unui num˘ n ˆ k p˘rti distincte este a ¸ a ın a¸ P (n.

}// main(. are ordinul ¸ a a a de complexitate O(n3 ).out.n.. int poz) { if(maxp==1) { nsol++.332 CAPITOLUL 15. public static void main(String[] args) { long t1. } . dim=poz-1.1).. static int n=6. return. ¸ Codul surs˘ a class PartitieNrGenerare // n = suma de numere { static int dim=0. Invit˘m cititorul s˘ caute metode mai eficiente. s s Solutia descris˘ anterior.) static void f(int val. bazat˘ pe metoda program˘rii dinamice.maxp). solutia const˘ ˆ citirea valorii ¸ ¸ a ınn lui N ¸i afi¸area rezultatului memorat. afis2(val. t1=System. } if(val==0) { nsol++. a a Ordinul de complexitate al unei solutii care se bazeaz˘ pe metoda backtrack¸ a ing este exponential.currentTimeMillis().t2. t2=System. int maxp.nsol=0. afis1(). ordinul a de complexitate al solutiei finale ar fi fost O(1).println("nsol = "+nsol+" timp = "+(t2-t1)+"\n"). METODA BACKTRACKING Analiza complexit˘¸ii at Pentru o rezolvare care se bazeaz˘ pe metoda vectorului de constante. f(n. dim=poz-1. return. static int[] x=new int[n+1]. System.currentTimeMillis().

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

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

{ // nsol = 38328320 timp = 4787 static int dim=0. return. out.nsol=0.t2. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("partitie.nextToken().1).close(). } if(val==0) { nsol++.nval. PROBLEME REZOLVATE static int min(int a. PrintWriter out=new PrintWriter(new BufferedWriter( new FileWriter("partitie. t1=System. dim=poz-1. }// main(. . public static void main(String[] args) throws IOException { long t1. static int[] x=new int[161]. f(n.println("nsol = "+nsol+" timp= "+(t2-t1)).int b) { return (a<b)?a:b.in"))). int maxip.i.print(nsol). class PartitieNrImpare2 // n = suma de numere impare . System..) static void f(int val.3.io.*. int poz) { if(maxip==1) { nsol++.currentTimeMillis(). int maxi=((n-1)/2)*2+1.15. t2=System.out. out. 335 int n..maxi.currentTimeMillis().out"))). st. dim=poz-1. n=(int)st. afis2(val). } }// class import java.

i<=val. dim=poz-1. }// f(.336 //afis1(). System.print(x[i]+" "). pentru cei care nu o ¸tiu.i>=3. }// afis2(. int i.i<=dim. ˆ copil˘rie. f(val-i.out. urmeaz˘ partea a doua.maxi).i++) System. int maxiok=min(maxip.int b) { return (a>b)?a:b.i<=dim. } }// class 15. povestea Scufitei ¸ ın a ¸ Ro¸ii.) static void afis1() { int i..i. } nsol++. }// afis1() static void afis2(int val) { int i. //afis2(val).poz+1). return. for(i=1. } CAPITOLUL 15.out.print(x[i]+" "). for(i=1. cunoa¸terea ei nu este necesar˘ pentru a rezolva aceast˘ problem˘.i++) System. System.i=i-2) { x[poz]=i.ONI2003 clasa a X-a ¸ Majoritatea participantilor la ONI2003 au auzit..3...out.print("1 ").out. METODA BACKTRACKING int maxi=((val-1)/2)*2+1.19 Scufita . nu s s a s v˘ faceti griji. Pentru cei care o ¸tiu. a ¸ s a a a Povestea nu spune ce s-a ˆ amplat pe drumul pe care Scufita Ro¸ie s-a ˆ ıntˆ ¸ s ıntors de . } static int min(int a.) static int max(int a.i++) System.out. for(i=1.int b) { return (a<b)?a:b.print("\n"+nsol+" : ").print("\n"+nsol+" : "). for(i=maxiok.

. 1) a unei matrice cu N linii ¸i N coloane. an1 an2 ... ci ¸i s˘ culeag˘ a a a a ın ¸a s a a cˆt mai multe ciupercute. atˆt Scufita.. Dac˘ la sfˆr¸itul unui minut ea are mai ¸ a as putine ciupercute decˆt Lupul.. Lupul o va mˆnca..in are urm˘toarea structur˘: s a a N . Dac˘ Scufita Ro¸ie pierde jocul. scopul ei este nu numai s˘ r˘mˆn˘ ˆ viat˘.a2n . a at Povestea spune c˘ Scufita Ro¸ie a plecat acas˘ cu co¸uletul plin de ciupercute. va pierde jocul. Dac˘ Scufita Ro¸ie ajunge ˆ ¸ ¸ a a ¸ s ıntr-o pozitie ˆ ¸ ın care nu sunt ciupercute.. a ¸ s a s ¸ ¸ folosind indicatiile date de un program scris de un concurent la ONI 2003 (nu vom ¸ da detalii suplimentare despre alte aspecte.out are urm˘toarea structur˘: s a a .. ¸ s a ın ¸ s ˆ fiecare pozitie a matricei fiind amplasate ciupercute. ¸a ¸ a ın Pe drum. pentru aa ın a nu complica inutil enuntul problemei). cum ar fi c˘l˘toria ˆ timp.bnn Date de ie¸ire s Fi¸ierul scufita. Jocul ˆ ¸ ¸ a ıncepe dup˘ a ce amˆndoi participantii au cules ciupercutele din pozitiile lor initiale (nu conteaz˘ a ¸ ¸ ¸ ¸ a cine are mai multe la ˆ ınceputul jocului. Acesta dorea s˘ o m˘nˆnce. a a ¸ Scufita Ro¸ie se afl˘ ˆ pozitia (1. 1) a unei alte matrice similare.b2n .15.. s a a a a Din acest moment. ci doar dup˘ un num˘r ˆ a a ıntreg strict pozitiv de minute de la ˆ ınceput)... va alege la fiecare minut mua a tarea ˆ cˆmpul vecin cu cele mai multe ciupercute (matricea sa este dat˘ astfel ın a ¸ a ˆ ınct s˘ nu existe mai multe posibilit˘¸i de alegere la un moment dat). cunoscut pentru l˘comia sa proverbial˘. pentru a le duce acas˘ (dup˘ ce va veni. dar a decis s˘-i acorde o ¸ans˘ de ın a a a a s a sc˘pare. a a a Lupul. a ¸ s a ˆ Inainte de ˆ ınceperea jocului. Veti afla am˘nunte ˆ continuare.. a Cerint˘ ¸a Scrieti un program care s˘ o ajute pe Scufita Ro¸ie s˘ r˘mˆn˘ ˆ joc ¸i s˘ ¸ a ¸ s a a a a ın s a culeag˘ cˆt mai multe ciupercute la sfˆr¸itul celor 15 minute! a a ¸ as Date de intrare Fi¸ierul scufita.. ea s-a ˆ alnit cu Lupul (fratele lupului care a p˘r˘sit povestea ıntˆ aa ˆ prima parte). Deci Scufita ¸ Ro¸ie va fi liber˘ s˘ plece dac˘ nu va pierde jocul dup˘ 15 minute. provocˆnd-o la un concurs de cules ciupercute. Lupul se afl˘ ˆ pozitia ın ¸ ¸ a ın ¸ (1. bn1 bn2 .. Pe parcursul unui minut..matricea din care culege Lupul b21 b22 .3.dimensiunea celor dou˘ matrice a a11 a12 . S˘ fi fost acest program scris de c˘tre ¸ a a dumneavoastr˘? Vom vedea.b1n .a1n -matricea din care culege Scufita Ro¸ie ¸ s a21 a22 . vˆn˘torul nu o a ¸ a a a a va mai l˘sa s˘ culeag˘)..ann b11 b12 ... PROBLEME REZOLVATE 337 la bunicut˘. cˆt ¸i a ¸ a s Lupul se deplaseaz˘ ˆ a ıntr-una din pozitiile vecine (pe linie sau pe coloan˘) ¸i culeg ¸ a s ciupercutele din pozitia respectiv˘. care i-a ¸ s a a promis c˘ va veni ˆ a ıntr-un sfert de ora (15 minute) pentru a o salva. ea va pierde jocul de asemenea. Scufita Ro¸ie l-a sunat pe Vˆn˘tor.

S. iar ˆ final s˘ strˆng˘ cˆt mai multe ciupercute.. ¸ s s aa a • dup˘ ce unul din juc˘tori culege ciupercutele dintr-o pozitie. METODA BACKTRACKING N R .in scufita.out 4 137 2234 SSSEEENVVNEENVV 5678 9 10 11 12 13 14 15 16 1234 5678 9 10 11 12 13 14 15 16 Explicatie: ¸ Scufita Ro¸ie a efectuat acelea¸i mut˘ri cu cele efectuate de Lup ¸i a avut tot ¸ s s a s timpul o ciupercut˘ ˆ plus.directiile pe care s-a deplasat Scufita Ro¸ie. Est. separate prin cˆte un ¸ ¸ s a spatiu (directiile pot fi N. ˆ final ea a cules toate ciupercutele din matrice. Vest. a a a ¸ • pe testele date. V indicˆnd deplas˘ri spre Nord. Scufita trebuie s˘ mute la fiecare moment. a • nici Scufita Ro¸ie ¸i nici Lupul nu vor p˘r˘si matricele corespunz˘toare. care ¸ s a ¸ este cunoscut la fiecare moment. Rezolvarea prin metoda backtracking ˆ ıncearc˘ la fiecare pas.d15 . Restrictiile ıncˆ a a a a ın ın a a a a ¸ ¸ sunt date de enuntul problemei ¸i de num˘rul de ciupercute culese de Lup.octombrie 2003 Problema se rezolv˘ folosind metoda backtracking ˆ plan. 13/6 .. .338 CAPITOLUL 15. deoarece Scufita nu se va ˆ ın ¸ ıntoarce ˆ pozitia din care ın ¸ tocmai a venit). 1) este situat˘ ˆ coltul de Nord-Vest al matricei) ¸ a ın ¸ Restrictii ¸ • 4 ≤ N ≤ 10. • valorile din ambele matrice sunt numere naturale mai mici decˆt 256. Sud. ¸ ¸ a a pozitia (1. ¸a ın In ¸ Timp maxim de executare: 1 secund˘/test a Indicatii de rezolvare * ¸ Mihai Stroe. Scufita Ro¸ie va avea ˆ ¸ s ıntotdeauna posibilitatea de a rezista 15 minute. astfel In a ¸ a ˆ at s˘ r˘mˆn˘ ˆ joc. cel mult trei mut˘ri sunt a posibile ˆ fiecare moment. a ın Se observ˘ c˘ mut˘rile Lupului sunt independente de mut˘rile Scufitei. de fapt.num˘rul total de ciupercute culese a ¸ d1 d2 . E. GInfo nr. pe rˆnd. astfel a a a a ¸ ele pot fi determinate imediat dup˘ citirea datelor. a ˆ acest punct al rezolv˘rii. fiecare a a mutare din cele cel mult patru posibile (teoretic. ˆ pozitia a a ¸ ¸ ın ¸ respectiv˘ r˘mˆn 0 ciupercute. Exemplu scufita.

PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("scufita.*.3. Codul surs˘ a import java. deci constant (constanta introdus˘ fiind totu¸i a s destul de mare). maxc=0.nextToken(). s-ar putea considera c˘ ordinul de a s a complexitate este limitat superior. static char[] tmax=new char[16]. st. static int[][] a. ordinul de complexitate ın ¸ ar fi O(3M ).in"))). class Scufita { static int n. public static void main(String[] args) throws IOException { int i. primele dou˘ mut˘ri ofer˘ dou˘ variante de alegere ˆ loc de trei. static int[] x. Alte a a a a ın restrictii apar datorit˘ limit˘rii la o matrice de 10 · 10 elemente.io. dar limitele mici ¸i faptul c˘ num˘rul a s a a de mut˘ri disponibile ˆ a ıncepe s˘ scad˘ destul de repede. ¸ Analiza complexit˘¸ii at Fie M num˘rul de mut˘ri pe care le are de efectuat Scufita. num˘rul maxim de st˘ri examinate este mult mai mic.out"))). Ordinul de complexitate este dat de ın rezolvarea prin metoda backtracking.y. ˆ multe cazuri permit un a a ın timp de executie foarte bun.15. de a a exemplu. static char[] tc=new char[16].nval. a s ¸ In a ¸ aceasta este comparat˘ cu solutia optim˘ g˘sit˘ pˆn˘ atunci ¸i dac˘ este mai bun˘ a ¸ a a a a a s a a este retinut˘. n=(int)st. deci nu vor fi luate ˆ calcul. De fapt. PROBLEME REZOLVATE 339 Se urm˘re¸te respectarea restrictiilor impuse. ¸ s respectiv O(M ). a a ¸ Operatiile de citire ¸i scriere a datelor au ordinul de complexitate O(N 2 ).b. Avˆnd ˆ vedere c˘ la fiecare pas Scufita poate alege dintre cel mult 3 mut˘ri a ın a ¸ a (deoarece nu se poate ˆ ıntoarce ˆ pozitia din care a venit). . StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("scufita. ˆ momentul g˘sirii unei solutii. ¸ a a Cum num˘rul M este cunoscut ¸i mic. ¸ a ˆ a nu a fost g˘sit˘ o rezolvare polinomial˘ pentru aceast˘ problem˘ (¸i este Inc˘ a a a a a s improbabil ca o astfel de rezolvare s˘ existe).j.

1. } for(i=1. int[n+2][n+2].i<=15..print(tmax[i]). int[17]. } culegeLupul(1.j<=n.k+1).nextToken(). out.1. f(i-1.nval. int j.i++) out. for(int ii=1.nextToken().j++) { st. f(i. tc[k]=’S’.close().i++) for(j=1. }//f(.1).j++) { st. int[17].k+1). out. METODA BACKTRACKING for(i=1.j-1.j<=n. return.i<=n.. a[i][j]=(int)st. tc[k]=’E’.k+1). if(k==16) { if(x[16]>maxc) { maxc=x[16]. tc[k]=’V’.ii<=15. CAPITOLUL 15.j. } } } } . x[k]=x[k-1]+a[i][j]. f(i. f(1. } if((a[i-1][j]>0)&&(a[i-1][j]+x[k]>=y[k+1])) if((a[i+1][j]>0)&&(a[i+1][j]+x[k]>=y[k+1])) if((a[i][j-1]>0)&&(a[i][j-1]+x[k]>=y[k+1])) if((a[i][j+1]>0)&&(a[i][j+1]+x[k]>=y[k+1])) a[i][j]=aij. f(i+1.i<=n. b[i][j]=(int)st.i++) for(j=1. } a[i][j]=aij.340 a=new b=new x=new y=new int[n+2][n+2].println().println(maxc).k+1).) { { { { tc[k]=’N’. for(i=1.j+1.1).j.nval. }// main() static void f(int i. out. int k) throws IOException { int aij=a[i][j].ii++) tmax[ii]=tc[ii]. a[i][j]=0.

j+1.k+1). }// culegeLupul(.k+1).k+1).j.. PROBLEME REZOLVATE 341 static void culegeLupul(int i.3. int k) { if(k>16) return.k+1). y[k]=y[k-1]+b[i][j]. b[i][j]=0. int j. if((b[i-1][j]>b[i+1][j])&&(b[i-1][j]>b[i][j-1])&&(b[i-1][j]>b[i][j+1])) culegeLupul(i-1. else culegeLupul(i.) }// class .j.j-1. else if(b[i][j-1]>b[i][j+1]) culegeLupul(i. else if((b[i+1][j]>b[i][j-1])&&(b[i+1][j]>b[i][j+1])) culegeLupul(i+1..15.

342 CAPITOLUL 15. METODA BACKTRACKING .

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

Folosirea acestor tabele d˘ ¸i numele tehnicii respective. d2 . decizia di+1 depinde de deciziile anterioare d1 . definirea recursiv˘ a valorii unei solutii optime. at 2. Deosebirea const˘ ˆ faptul c˘ a ¸ a ın a ”divide et impera” partitioneaz˘ problema ˆ subprobleme independente. ˆ aceast˘ situatie. • Dac˘ d1 .. dn . calculul efectiv al valorii unei solutii optime. Astfel. di este ın un ¸ir optim de decizii care duc la trecerea sistemului din starea S0 ˆ starea s ın Si . d2 . ele avˆnd ”sub-subprobleme” comune. • Dac˘ d1 .. d2 . ... se folosesc tabele auxiliare pentru retinerea optimelor ¸ ¸ partiale iar spatiul de solutii se parcurge ˆ ordinea cresc˘toare a dimensiunilor ¸ ¸ ¸ ın a subproblemelor... care satisface una dintre formele principiului optimalit˘¸ii). decizia di depinde de deciziile anterioare di+1 . evitˆndu-se recalcularea ei de cˆte ori subproblema reapare. .. d2 .. ¸ a ¸ Deoarece rezolvarea prin recursivitate duce de cele mai multe ori la ordin de complexitate exponential. se a In a ¸ rezolv˘ fiecare ”sub-subproblem˘” o singur˘ dat˘ ¸i se folose¸te un tablou pentru a a a as s a memora solutia ei.. . di este un ¸ir optim de decizii care duc la trecerea s s sistemului din starea S0 ˆ starea Si .. caracterizarea unei solutii optime (identificarea unei modalit˘ti optime de ¸ a rezolvare.. dn ın este un ¸ir optim de decizii care duc la trecerea sistemului din starea Si ˆ s ın starea Sn ¸i d1 . Spunem c˘ a se aplic˘ metoda ˆ a ınapoi. PROGRAMARE DINAMICA • Dac˘ d1 . . atunci pentru orice i (1 ≤ i ≤ n) di+1 . dn este un ¸ir optim de decizii care duc la trecerea sistemului a s din starea S0 ˆ starea Sn . ˆ a doua variant˘ avem recurente ˆ ¸irul de decizii. . as Asem˘n˘tor cu metoda ”divide et impera”. atunci pentru orice i (1 ≤ i ≤ n) d1 ... .. ˆ prima variant˘ avem a s ¸ In a recurente ˆ ¸ ıntre subprobleme. .. programarea dinamic˘ rezolv˘ a a a a problemele combinˆnd solutiile unor subprobleme.344 ˘ CAPITOLUL 16. reconstituirea unei solutii pe baza informatiei calculate. di . ¸ ¸ .. dn ın este un ¸ir optim de decizii care duc la trecerea sistemului din starea Si s ˆ starea Sn ... d2 . este necesar˘ ¸i o justificare In ın ¸ as sau o demonstratie a faptului c˘ aceste recurente sunt cele corecte. . di+2 ... Spunem c˘ se aplic˘ metoda mixt˘. atunci pentru orice i (1 ≤ i ≤ n) di+1 .. ¸ a a Algoritmul pentru rezolvarea unei probleme folosind programarea dinamic˘ a se dezvolta ˆ 4 etape: ın 1. ın a a a Indiferent de varianta de prezentare. rezolvarea prin programare dinamic˘ a presupune g˘sirea ¸i rezolvarea unui sistem de recurente. le rezolv˘ ¸ a ın a (de obicei recursiv) ¸i apoi combin˘ solutiile lor pentru a rezolva problema initial˘.. di+2 . ¸ 4.. ın Spunem c˘ se aplic˘ metoda ˆ a a ınainte... dn este un ¸ir optim de decizii care duc la trecerea sistemului a s din starea S0 ˆ starea Sn .. a ¸ 3. s a ¸ a ˆ timp ce programarea dinamic˘ se aplic˘ problemelor ale c˘ror subprobleme nu ın a a a sunt independente. .. dn este un ¸ir optim de decizii care duc la trecerea sistemului a s din starea S0 ˆ starea Sn . Astfel. ın a ¸ ın s ˆ afara cazurilor ˆ care recurentele sunt evidente.

s au ca subproblem˘ comun˘ calcularea produsului Ai+1 × .. Observ˘m c˘ subproblemele nu sunt independente... . × Aj . necesitˆnd 1000000+1000000=2000000 ˆ a ınmultiri.... Numim ˆ ınmultire elementar˘ ˆ ¸ a ınmultirea a dou˘ elemente. PROBLEME REZOLVATE 345 16. . Solutie 1... Aceast˘ observatie se aplic˘ ¸i produselor dintre paranteze. × An (pentru care a costul.1 Inmultirea optimal˘ a matricelor ¸ a Consider˘m n matrice A1 . Prin urmare... vom utiliza o matrice M .2 Probleme rezolvate 16. num˘rul minim de ˆ a ınmultiri necesare pentru a calcula A1 × A2 × ¸ . A2 .16. avˆnd m linii ¸i p coloane.. cu o matrice B. Evident.. . ˆ final trebuie s˘ ˆ ın a ınmultim dou˘ matrice. a s a s este n ∗ m ∗ p.. cu semnificatia: s M [i][j] = num˘rul minim de ˆ a ınmultiri elementare necesare pentru a calcula ¸ produsul Ai × Ai+1 × . × An ). (1000. Pentru a retine solutiile subproblemelor. ¸ Reamintim c˘ num˘rul de ˆ a a ınmultiri elementare necesare pentru a ˆ ¸ ınmulti o ¸ matrice A. ¸ a deci vom paranteza produsul astfel: (A1 × A2 × .. 1 ≤ i ≤ j ≤ n..2. × Ak ) × (Ak+1 × . Produsul A1 × A2 × .. Pentru a calcula A1 ×A2 ×. aplicˆnd ın a asociativitatea operatiei de ˆ ¸ ınmultire a matricelor. a ¸ a s subproblemele problemei initiale constau ˆ determinarea parantez˘rii opti¸ ın a male a produselor de matrice de forma Ai × Ai+1 × ... × An se poate calcula ˆ diverse moduri.×An .2. adic˘ num˘rul total de ˆ a a ınmultiri elementare. avˆnd n linii ¸i m coloane. A1 × (A2 × A3 ).. produsul s A1 × A2 × A3 se poate calcula ˆ dou˘ moduri: ın a 1... De exemplu. × Aj .. 10) ¸i (10. × An . × Aj .×Aj ¸i calcularea produsului Ai+1 ×Ai+2 ×.... calcularea a a produsului Ai ×Ai+1 ×. ¸ a Exemplu: Pentru 3 matrice de dimensiuni (10. 1000). s˘ fie minim).. a a 2. cu n linii ¸ ¸ ¸i n coloane.. de dimensiuni d0 × d1 . An . a dn−1 × dn .. (A1 × A2 ) × A3 necesitˆnd 1000000+10000=1010000 ˆ a ınmultiri elementare ¸ 2. × An este M [1][n].. d1 × d2 . ¸ a ˆ functie de modul de parantezare difer˘ num˘rul de ˆ In ¸ a a ınmultiri elementare ¸ necesare pentru calculul produsului A1 × A2 × . 100). 1 ≤ i ≤ j ≤ n.×Aj+1 . Se cere parantezare optimal˘ a produsului A1 × A2 × ..

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

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

. Rezolvare s a 1.out. ≤ aik ) cel mai lung sub¸ir cresc˘tor al ¸irului A. . . 8. 10.. 10. Evident Ai2 = (ai2 ≤ ai3 ≤ . . 6. 60. 100..2 Sub¸ir cresc˘tor maximal s a Fie un ¸ir A = (a1 . an ).print("Ordinea inmultirilor: ").. 50.n).. n}. . 3.. 80) o solutie poate fi ¸ (3.. 30... an )..2. Se cere determinarea unui sub¸ir cresc˘tor al ¸irului A.. ˆ ordinea ˆ care acestea apar ˆ A: ın ın ın ai1 . 80).out. PROGRAMARE DINAMICA System. paranteze(1. 30. .. i = {1. 40. ai2 +1 . 6.. < ik ≤ n. a2 . 60. s a s a De exemplu.println("Numarul minim de inmultiri este: "+m[1][n]). o subproblem˘ a problemei a initiale const˘ ˆ determinarea celui mai lung sub¸ir cresc˘tor care ˆ ¸ a ın s a ıncepe cu ai .out. pentru A = (8. an ). aik . unde 1 ≤ i1 < i2 < .println(). etc.. Observ˘m c˘ el coincide cu cel mai lung sub¸ir cresc˘tor al ¸irului s a a s a s s (ai1 .. ai1 +1 .. }//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 */ 16. System. System. Fie Ai1 = (ai1 ≤ ai2 ≤ .. de lungime maxim˘.. Prin urmare.. ai2 .348 ˘ CAPITOLUL 16. ≤ aik ) este cel mai lung sub¸ir a cresc˘tor al lui (ai2 . Numim sub¸ir al ¸irului A o succesiune de s s s elemente din A. ..

Relatia de recurent˘ care caracterizeaz˘ substructura optimal˘ a problemei ¸ ¸a a a este: l[n] = 1. poz[n] = −1. poz[i] =pozitia elementului care urmeaz˘ dup˘ a[i] ˆ cel mai lung sub¸ir ¸ a a ın s cresc˘tor care ˆ a ıncepe cu a[i].2. j=i+1. ¸ ¸ s fiecare cu n componente. Pentru a retine solutiile subproblemelor vom considera doi vectori l ¸i poz. apoi afi¸am solutia. sau −1 dac˘ un a a a astfel de element nu exist˘.. j++) if (a[i] <= a[j] && l[i]<1+l[j]) { l[i]=1+l[j]. l[i] = max {1 + l[j]|a[i] ≤ a[j]} j=i+1. j = {i + 1.16. pozmax=1. PROBLEME REZOLVATE 349 Subproblemele nu sunt independente: pentru a determina cel mai lung sub¸ir s cresc˘tor care ˆ a ıncepe cu ai . poz[i]=-1. cout<<"\nCel mai lung subsir:\n". i>0. l[n]=1. ai ≤ aj . .n unde poz[i] = indicele j pentru care se obtine maximul l[i]. a 3. avˆnd semnificatia: a ¸ l[i] =lungimea celui mai lung sub¸ir cresc˘tor care ˆ s a ıncepe cu a[i]. n}. 2. i++) if (max<l[i]) { max=l[i]. i!=-1. for (i=pozmax. este necesar s˘ determin˘m cele mai lungi sub¸iruri a a s cresc˘toare care ˆ a ıncep cu aj . } cout<<"Lungimea celui mai lung subsir crescator: " <<max.. j<=n. i<=n. } Pentru a determina solutia optim˘ a problemei. i=poz[i]) cout<<a[i]<<’ ’. pozmax=i. poz[i]=j. i--) for (l[i]=1. for (int i=2. Secventele de program de mai sus sunt scrise ˆ c/C++. ¸ ın . determin˘m valoarea maxim˘ ¸ a a a din vectorul l. ¸ Rezolv˘m relatia de recurent˘ ˆ mod bottom-up: a ¸ ¸a ın int i. ˆ s˘ ¸ ıncepˆnd cu pozitia maximului ¸i utilizˆnd a ¸ s a informatiile memorate ˆ vectorul poz: ¸ ın //determin maximul din vectorul l int max=l[1]. poz[n]=-1. dac˘ un astfel de element exist˘. for (i=n-1. j.

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

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

i++) S[n][i]=T[n][i]. a ¸ Evident. i<=n. Pentru a retine solutiile subproblemelor. ¸ 3. respectˆnd conditiile problemei.. subproblemele nu sunt independente: pentru a a calcula suma maxim˘ a numerelor de pe un drum de la T [i][j] la ultima linie. S[i + 1][j + 1]} 4. ca ˆ exemplul urm˘tor: ın a 7 3 8 2 4 5 7 2 1 4 6 8 0 4 5 Tabelul 16. Rezolv˘m relatia de recurent˘ ˆ mod bottom-up: a ¸ ¸a ın int i.3 Sum˘ maxim˘ ˆ triunghi de numere a a ın S˘ consider˘m un triunghi format din n linii (1 < n ≤ 100). a trebuie s˘ calcul˘m suma maxim˘ a numerelor de pe un drum de la T [i + 1][j] la a a a ultima linie ¸i suma maxim˘ a numerelor de pe un drum de la T [i + 1][j + 1] la s a ultima linie. Vom retine triunghiul ˆ ¸ ıntr-o matrice p˘tratic˘ T . for (i=n-1. fiecare linie a a continˆnd numere ˆ ¸ a ıntregi din domeniul [1.2. 2. i--) for (j=1. p˘tratic˘ ¸ ¸ a a de ordin n.352 ˘ CAPITOLUL 16. 99]. Evident. de ordin n. vom utiliza o matrice S. i = {1.1: Triunghi de numere Problema const˘ ˆ scrierea unui program care s˘ determine cea mai mare a ın a sum˘ de numere aflate pe un drum ˆ a ıntre num˘rul de pe prima linie ¸i un num˘r a s a de pe ultima linie. i>0. n} S[i][j] = T [i][j] + max{S[i + 1][j]. a a la stˆnga sau la dreapta sa. sub diagonala a a principal˘.. fiecare num˘r din acest drum fiind situat sub precedentul. cu semnificatia ¸ S[i][j] = suma maxim˘ ce se poate obtine pe un drum de la T [i][j] la un a ¸ element de pe ultima linie. Suedia 1994) a Rezolvare 1. j++) . Fiecare num˘r din acest drum este situat sub precedentul. solutia problemei va fi S[1][1]. j<=i. la a stˆnga sau la dreapta acestuia. Relatia de recurenta care caracterizeaz˘ substructura optimal˘ a problemei ¸ ¸ a a este: S[n][i] = T [n][i]. Subproblemele problemei date constau ˆ determinarea sumei maxime a ın care se poate obtine din numere aflate pe un drum ˆ ¸ ıntre numarul T [i][j].. (IOI. . pˆn˘ la un a a num˘r de pe ultima linie. j. PROGRAMARE DINAMICA 16. for (i=1. 2.

¸ s a Exemplu Pentru X = (2. O subproblem˘ a problemei date const˘ a a ˆ determinarea celui mai lung sub¸ir comun al lui Xk . 5. 3. Yh ) = 1 + LCS(Xk−1 . yh ) prefixul lui Y de lungime h. Y h) = max(LCS(Xk−1 .. 8).. Vom caracteriza substructura optimal˘ a problemei prin urm˘toarea relatie a a ¸ de recurent˘: ¸a lcs[k][0] = lcs[0][h] = 0. 8. xn ) ¸i Y = (y1 . 5. 8) o s solutie posibil˘ este: Z = (2. 5.. Determinati un sub¸ir comun de lungime maxim˘. if (S[i+1][j]<S[i+1][j+1]) S[i][j]=T[i][j]+S[i+1][j+1]). Yh . denumit˘ lcs.. Dac˘ Xk = Yh atunci a LCS(Xk . n}. 8) ¸i Y = (6.2. . Yh ) ın s lungimea celui mai lung sub¸ir comun al lui Xk . LCS(Xk .4 Sub¸ir comun maximal s Fie X = (x1 . s a a a 2. 5.. 2. xk ) (prefixul lui X de lungime k) ¸i cu Yh = a s (y1 . dac˘ x[k] <> y[h] a lcs[k][h] = 1 + lcs[k − 1][h − 1]. respectiv m s as numere ˆ ıntregi. Yh . ¸ a Solutie 1. 5. x2 . x2 .. Yh ). h = {1. 2. ym ) dou˘ ¸iruri de n. 3. s Utilizˆnd aceste notatii.2. Yh−1 )). Notam cu LCS(Xk . 2.. . problema cere determinarea LCS(Xn .. 5. 3.. 0. iar elementul lcs[k][h] va fi lungimea celui mai lung sub¸ir comun al ¸irurilor s s Xk si Yh ... Din observatia precedent˘ deducem c˘ subproblemele problemei date nu sunt ¸ a a independente ¸i c˘ problema are substructur˘ optimal˘. PROBLEME REZOLVATE { S[i][j]=T[i][j]+S[i+1][j]. lcs[k − 1][h]. y2 . 3. dac˘ x[k] = y[h] a Rezolv˘m relatia de recurent˘ ˆ mod bottom-up: a ¸ ¸a ın .16. Linia ¸i coloana 0 sunt utilizate pentru initializare s a s ¸ cu 0. Not˘m cu Xk = (x1 . 5. 5. 1.. Dac˘ Xk = Y h atunci a LCS(Xk. m} max lcs[k][h − 1]. } 353 Exercitiu: Afi¸ati ¸i drumul ˆ triunghi pentru care se obtine solutia optim˘. s s Observatie ¸ 1. 6. .. precum a ¸ ¸i un astfel de sub¸ir. . Pentru a retine solutiile subproblemelor vom utiliza o matrice cu n + 1 linii ¸ ¸ ¸i m + 1 coloane. y2 . 6. Ym ). 4. 5. k = {1.. 2. 4. 2. . ¸ s ¸ s ın ¸ ¸ a 16. 5. 4. Yh−1 ).. ..

354 ˘ CAPITOLUL 16. k<=n. Deoarece nu am utilizat o structur˘ de date suplimentar˘ cu ajutorul c˘reia a a a s˘ memor˘m solutia optim˘. ¸ import java.*. cout<<"\nCel mai lung subsir comun este: \n". h=m. h--. k--) cout<< d[k] <<’ ’.io. int d[100]. De asemenea sunt afi¸ate matricele auxiliare ¸ ın a s de lucru pentru a se putea urm˘rii mai u¸or modalitatea de determinare recursiv˘ a s a a tuturor solutiilor. PROGRAMARE DINAMICA for (int k=1. k=n. h++) if (x[k]==y[h]) lcs[k][h]=1+lcs[k-1][h-1]. Prin reconstituire vom obtine solutia ˆ ordine invers˘. Sunt determinate cea mai mic˘ ¸i cea mai ın s a ¸ as mare solutie. ˆ ordine lexicografic˘. Secventele de cod de mai sus sunt scrise ˆ C/C++. . ın ¸ ¸ ın a din acest motiv vom memora solutia ˆ ıntr-un vector. for (int i=0. static int [][] a.k>=0. lcs[k][h]. pe care ˆ vom afi¸a de la ıl s sfˆr¸it c˘tre ˆ as a ınceput: cout<<"Lungimea subsirului comun maximal: " <<lcs[n][m]. k++) for (int h=1. else h--. vom reconstitui solutia optim˘ pe baza rezultatelor a a ¸ a ¸ a memorate ˆ matricea lcs. h<=m. k--. else lcs[k][h]=lcs[k][h-1]. Programul urm˘tor este ¸ ın a scris ˆ Java ¸i determin˘ toate solutiile. else if (lcs[k-1][h]>lcs[k][h-1]) lcs[k][h]=lcs[k-1][h]. ) if (x[k]==y[h]) { d[i++]=x[k]. for (k=i-1. // SubsirComunMaximal class scm { static PrintWriter out. } else if (lcs[k][h]==lcs[k-1][h]) k--.

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

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

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

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

i++) System.print(d[i][j]+" "). out.out.println().print(v[i]).i<n. System. m=d[i].print(x.out.length-1.out. System. for(j=0. for(i=1.2. } System. } static void afisv(char[]v) { int i.j++) System.16.length. PROBLEME REZOLVATE for(i=1.println().println("\n").charAt(i-1)+" ").out.out.i<=v.i++) out.i<=v. } } Pe ecran apar urm˘toarele rezultate: a 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 359 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 * | | | * | * * - .out.println(). for(i=1.length-1.i++) { System.j<m.print(v[i]).

””) = 0 d(s. s2) distanta de editare (definit˘ ca fiind num˘rul minim de operatii ¸ a a ¸ de ¸tergere.2. obtinem: as a a ¸ . 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 16.2) Avˆnd ˆ vedere ultima operatie efectuat˘ asupra primului ¸ir de caractere. s) = |s|.1) (16. a ın ¸ a s la sfˆr¸itul acestuia (dar dup˘ modific˘rile efectuate deja). ””) = d(””.2. inserare ¸i modificare) dintre ¸irurile de caractere s1 ¸i s2.5 Distanta minim˘ de editare ¸ a Fie d(s1. (16. Atunci: s s s s d(””.360 5 6 a b c d e f | | | | | | | | | | | | | | | | | | | | | | | | * | | | | | | * * | | | | | * | | | * * | | * ˘ CAPITOLUL 16.2.

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

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

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

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

int b) { return a<b ? a:b.16. int b.min(b.. } static int min(int a.c)).2. PROBLEME REZOLVATE }//afissol(. } }// class Rezultate afi¸ate ˆ fi¸ierul de ie¸ire: s ın s s 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 . int c) { return min(a.) 365 static int min(int a..

6 Problema rucsacului (0 − 1) import java.i++) { st. m=(int)st. st. i++) c[i][0]=0.out"))).nval. v[i]=(int)st.io. int[][] c.nval.*. st.nval.i<=n. n=(int)st. class Rucsac01 { public static void main(String[] args) throws IOException { int n.2.nval.v.366 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 16. for(i=1.in"))).nextToken(). int[] g. i<=n.i++) { st.nextToken(). v=new int[n+1].m.nextToken(). StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("rucsac. .i<=n. g=new int[n+1]. int i. } for(i=1. } for(i=1. PROGRAMARE DINAMICA COST transformare = 5 16. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("rucsac.nextToken().j. c=new int[n+1][m+1]. g[i]=(int)st.

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

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

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

*..xj ]..nt=0... static int x[]. cu efort minim (sau cost).xj ] obtinem dou˘ bucati mai mici: [xi . class Vergea { static int n. ˆ viata real˘... ce valoare are k? Evident.2.xk ] (adic˘ c[i][k]) ¸i + costul t˘ierii a a a s a vergelei [xk .. ın a ¸ ¸a a a Not˘m prin c[i][j] (i < j) costul minim necesar realiz˘rii tuturor t˘ieturilor a a a segmentului de vergea [xi . .c)...xj ] este format din costul transportului acesteia la ma¸ina de a s t˘iat (xj − xi ) + costul t˘ierii vergelei [xi ..370 { ˘ CAPITOLUL 16.xj ] (adic˘ c[k][j]).xj ]. } }// class /* traversare. xn reprezint˘ abscisele punctelor a a ˆ care se vor realiza t˘ieturile (distantele fat˘ de cap˘tul ”din stˆnga” al vergelei). a a a a Pentru j > i s˘ presupunem c˘ realiz˘m prima t˘ietur˘ ˆ xk (i < k < j).b). static int t[][].. static int c[][]. x2 .. Costul pentru ¸ a ¸ s t˘ierea vergelei [xi . PROGRAMARE DINAMICA return min(min(a.xk ] ¸i [xk ...out 3 4 6 2 1 3 2 2 1 1 3 5 4 1 2 3 4 2 7 3 3 1 4 */ 16. Din a a a a a ın vergeaua [xi . < xn < xn+1 ˆ care a s ın xn+1 reprezint˘ lungimea vergelei iar x1 . Costul fiec˘rei operatii de t˘iere a ¸ a este proportional cu lungimea vergelei care trebuie t˘iat˘. ne putem ¸ a a In ¸ a imagina costul ca fiind efortul depus pentru a plasa vergeaua (sau un bu¸tean!) ˆ s ın ma¸ina de t˘iat.. dea lungul unei vergele ˆ a ın locuri pre-specificate. k trebuie s˘ aib˘ acea valoare care s˘ minia a a mizeze expresia c[i][k] + c[k][j]. Obtinem: ¸ c[i][j] = xj − xi + min {c[i][k] + c[k][j]} i<k<j import java.in traversare.. . s a Consider˘m ¸irul de numere naturale 0 < x1 < x2 < ..io. a Dar.9 Problema segment˘rii vergelei a Scopul algoritmului este de a realiza n t˘ieturi.. Evident c[i][i + 1] = 0 pentru c˘ nu este necesar˘ nici o t˘ietur˘.

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

372 }// for h

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

out.println(c[0][n+1]); out.close(); afism(c); afism(t); System.out.println("Ordinea taieturilor: \n"); taieturi(0,n+1); System.out.println(); }//main public static void taieturi(int i,int j) throws IOException { if(i>=j-1) return; int k; k=t[i][j]; System.out.println((++nt)+" : "+i+".."+j+" --> "+k); //br.readLine(); // "stop" pentru depanare ! if((i<k)&&(k<j)) { taieturi(i,k); taieturi(k,j); } }//taieturi static void afism(int[][] x) // pentru depanare ! { int i,j; for(i=0;i<=n+1;i++) { for(j=0;j<=n+1;j++) System.out.print(x[i][j]+" "); System.out.println(); } System.out.println(); }// afism(...) }//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

16.2. 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

373

Ordinea taieturilor: 1 : 2 : 3 : 4 : 5 : */ 0..6 0..4 0..2 2..4 4..6 --> --> --> --> --> 4 2 1 3 5

16.2.10

Triangularizarea poligoanelor convexe

Consider˘m un poligon convex cu n vˆrfuri numerotate cu 1, 2, ..., n (ˆ figur˘ a a ın a n = 9) ¸i dorim s˘ obtinem o triangularizare ˆ care suma lungimilor diagonalelor s a ¸ ın trasate s˘ fie minim˘ (ca ¸i cum am dori s˘ consum˘m cˆt mai putin tu¸ pentru a a s a a a ¸ s trasarea acestora!).
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, orice latur˘ a poligonului face parte dintr-un triunghi al triangulatiei. a ¸ Consider˘m la ˆ a ınceput latura [1, 9]. S˘ presupunem c˘ ˆ a a ıntr-o anumit˘ triangulatie a ¸ optim˘ latura [1, 9] face parte din triunghiul [1, 5, 9]. Diagonalele triangulatiei a ¸ optime vor genera o triangulatie optim˘ a poligoanelor convexe [1, 2, 3, 4, 5] ¸i ¸ a s [5, 6, 7, 8, 9]. Au ap˘rut astfel dou˘ subprobleme ale problemei initiale. a a ¸ S˘ not˘m prin p(i, k, j) perimetrul triunghiului [i, k, j] (i < k < j) i¸i prin a a s c[i][j] costul minim al triagulatiei poligonului convex [i, i + 1, ..., j] (unde i < j). ¸ Atunci: c[i][j] = 0, dac˘ j = i + 1 a

374 ¸i s

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

c[i][j] = min {p(i, k, j) + c[i][k] + c[k][j]}, dac˘ i < j ≤ n a
i≤k<j

16.2.11

Algoritmul Roy-Floyd-Warshall

// Lungime drum minim intre oricare doua varfuri in graf orientat ponderat. // OBS: daca avem un drum de lungime minima de la i la j atunci acest drum // va trece numai prin varfuri distincte, iar daca varful k este varf intermediar, // atunci drumul de la i la k si drumul de la k la j sunt si ele minime // (altfel ar exista un drum mai scurt de la i la j); astfel, este indeplinit // "principiul optimalitatii" import java.io.*; class RoyFloydWarshall // O(n^3) { static final int oo=0x7fffffff; static int n,m; static int[][] d; public static void main(String[]args) throws IOException { int i,j,k; StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("RoyFloydWarshall.in"))); PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("RoyFloydWarshall.out"))); st.nextToken(); n=(int)st.nval; st.nextToken(); m=(int)st.nval; d=new int[n+1][n+1]; for(i=1;i<=n;i++) for(k=1;k<=m;k++) { st.nextToken(); st.nextToken(); st.nextToken(); } for(j=1;j<=n;j++) d[i][j]=oo;

i=(int)st.nval; j=(int)st.nval; d[i][j]=d[j][i]=(int)st.nval;

for(k=1;k<=n;k++) for(i=1;i<=n;i++)

// drumuri intre i si j care trec

16.2. PROBLEME REZOLVATE for(j=1;j<=n;j++) // numai prin nodurile 1, 2, ..., 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]; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) if(d[i][j]<oo) System.out.print(d[i][j]+" "); else System.out.print("*"+" "); System.out.println(); } out.close(); }//main }//class /* 6 6 1 2 1 3 2 3 4 5 5 6 4 6 */

375

3 1 2 1 3 2

2 3 1 * * *

3 4 2 * * *

1 2 2 * * *

* * * 2 1 2

* * * 1 2 3

* * * 2 3 4

16.2.12

Oracolul decide - ONI2001 cls 10

prof. Doru Popescu Anastasiu, Slatina La un concurs particip˘ N concurenti. Fiecare concurent prime¸te o foaie de a ¸ s hˆrtie pe care va scrie un cuvˆnt avˆnd cel mult 100 de caractere (litere mici ale a a a alfabetului englez). Cuvintele vor fi distincte. Pentru departajare, concurentii apeleaz˘ la un oracol. Acesta produce ¸i el ¸ a s un cuvnt. Va cˆ¸tiga concurentul care a scris cuvˆntul ”cel mai apropiat” de al as a oracolului. Gradul de ”apropiere” dintre dou˘ cuvinte este lungimea subcuvˆntului coa a mun de lungime maxim˘. Prin subcuvˆnt al unui cuvˆnt dat se ˆ ¸elege un cuvˆnt a a a ınt a care se poate obtine din cuvˆntul dat, eliminˆnd 0 sau mai multe litere ¸i p˘strˆnd ¸ a a s a a ordinea literelor r˘mase. a Cerint˘ ¸a Se cunosc cuvˆntul c0 produs de oracol ¸i cuvintele ci , i = 1, ..., N scrise de a s concurenti. Pentru a ajuta comisia s˘ desemneze cˆ¸tig˘torul, se cere ca pentru ¸ a as a fiecare i s˘ identificati pozitiile literelor ce trebuie ¸terse din c0 ¸i din ci astfel ˆ at a ¸ ¸ s s ıncˆ prin ¸tergere s˘ se obtin˘ unul dintre subcuvintele comune de lungime maxim˘. s a ¸ a a

376

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

Date de intrare Fi¸ier de intrare: ORACOL.IN s Linia 1: N num˘r natural nenul, reprezentˆnd num˘rul concurentilor; a a a ¸ Linia 2: c0 cuvˆntul produs de oracol; a Liniile 3..N+2: cuvˆnt a pe aceste N linii se afl˘ cuvintele scrise de cei a N concurenti, un cuvˆnt pe o linie; ¸ a Date de ie¸ire s Fi¸ier de ie¸ire: ORACOL.OUT s s Liniile 1..2*N: pozitiile literelor ce trebuie ¸terse ¸ s pe fiecare linie i (i = 1, 3, ..., 2 ∗ N − 1) se vor scrie numere naturale nenule, separate prin cˆte a un spatiu, reprezentˆnd pozitiile de pe care se vor ¸terge litere din cuvˆntul pro¸ a ¸ s a dus de oracol; pe fiecare linie j (j = 2, 4, ..., 2 ∗ N ) se vor scrie numere naturale nenule, separate prin cˆte un spatiu, reprezentˆnd pozitiile de pe care se vor ¸terge a ¸ a ¸ s litere din cuvˆntul concurentului cu num˘rul j/2. a a Restrictii ¸ 2 ≤ N ≤ 100 Dac˘ exist˘ mai multe solutii, ˆ fi¸ier se va scrie una singur˘. a a ¸ ın s a Dac˘ dintr-un cuvˆnt nu se va t˘ia nici o liter˘, linia respectiv˘ din fi¸ierul a a a a a s de intrare va r˘mˆne vid˘. a a a Exemplu ORACOL.IN ORACOL.OUT poate contine solutia: ¸ ¸ 3 3 abc 34 abxd aabxyc 145 acb 3 2 Timp maxim de executare/test: 1 secund˘ a Codul surs˘ a Varianta 1: import java.io.*; // subsir comun maximal - problema clasica ... class Oracol // varianta cu mesaje pentru depanare ... { static final char sus=’|’, stanga=’-’, diag=’*’; static int[][] a; static char[][] d; static String x,y; static boolean[] xx=new boolean[101]; static boolean[] yy=new boolean[101];

16.2. PROBLEME REZOLVATE static char[] z; static int m,n,nc;

377

public static void main(String[] args) throws IOException { int i,j,k; BufferedReader br=new BufferedReader(new FileReader("oracol.in")); PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("oracol.out"))); nc=Integer.parseInt(br.readLine()); x=br.readLine().replace(" ",""); // elimina spatiile ... de la sfarsit ! m=x.length(); for(k=1;k<=nc;k++) { y=br.readLine().replaceAll(" ",""); // elimina spatiile ... daca sunt! n=y.length(); matrad(); afism(a); afism(d); System.out.print("O solutie oarecare: "); z=new char[a[m][n]+1]; for(i=1;i<=m;i++) xx[i]=false; for(j=1;j<=n;j++) yy[j]=false; osol(m,n); System.out.println("\n"); for(i=1;i<=m;i++) if(!xx[i]) out.print(i+" "); out.println(); for(j=1;j<=n;j++) if(!yy[j]) out.print(j+" "); out.println(); } out.close(); System.out.println("\n"); }// main(...) static void matrad() { int i,j; a=new int[m+1][n+1];

378

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

d=new char[m+1][n+1]; for(i=1;i<=m;i++) for(j=1;j<=n;j++) if(x.charAt(i-1)==y.charAt(j-1)) { a[i][j]=1+a[i-1][j-1]; d[i][j]=diag; } else { a[i][j]=max(a[i-1][j],a[i][j-1]); if(a[i-1][j]>a[i][j-1]) d[i][j]=sus; else d[i][j]=stanga; } }// matrad() static void osol(int lin, int col) { if((lin==0)||(col==0)) return; if(d[lin][col]==diag) osol(lin-1,col-1); else if(d[lin][col]==sus) osol(lin-1,col); else osol(lin,col-1); if(d[lin][col]==diag) { System.out.print(x.charAt(lin-1)); xx[lin]=yy[col]=true; } }// osol(...) static int max(int a, int b) { if(a>b) return a; else return b; }// max(...) static void afism(int[][] a) { int i,j; System.out.print(" "); for(j=0;j<n;j++) System.out.print(y.charAt(j)+" "); System.out.println();

16.2. PROBLEME REZOLVATE System.out.print(" "); for(j=0;j<=n;j++) System.out.print(a[0][j]+" "); System.out.println(); for(i=1;i<=m;i++) { System.out.print(x.charAt(i-1)+" "); for(j=0;j<=n;j++) System.out.print(a[i][j]+" "); System.out.println(); } System.out.println("\n"); }// afism(int[][]...) static void afism(char[][] d) // difera tipul parametrului { int i,j; System.out.print(" "); for(j=0;j<n;j++) System.out.print(y.charAt(j)+" "); System.out.println(); System.out.print(" "); for(j=0;j<=n;j++) System.out.print(d[0][j]+" "); System.out.println(); for(i=1;i<=m;i++) { System.out.print(x.charAt(i-1)+" "); for(j=0;j<=n;j++) System.out.print(d[i][j]+" "); System.out.println(); } System.out.println("\n"); }// afism(char[][]...) }// class Varianta 2: import java.io.*; // problema reala ... class Oracol { static final char sus=’|’, stanga=’-’, diag=’*’; static int[][] a; static char[][] d; static String x,y; static boolean[] xx=new boolean[101];

379

380

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

static boolean[] yy=new boolean[101]; static int m,n,nc; public static void main(String[] args) throws IOException { int i,j,k; BufferedReader br=new BufferedReader(new FileReader("oracol.in")); PrintWriter out=new PrintWriter( new BufferedWriter( new FileWriter("oracol.out"))); nc=Integer.parseInt(br.readLine()); x=br.readLine().replace(" ",""); // elimina spatiile ... de la sfarsit ! m=x.length(); for(k=1;k<=nc;k++) { y=br.readLine().replaceAll(" ",""); // elimina spatiile ... daca sunt! n=y.length(); matrad(); for(i=1;i<=m;i++) xx[i]=false; for(j=1;j<=n;j++) yy[j]=false; osol(m,n); for(i=1;i<=m;i++) if(!xx[i]) out.print(i+" "); out.println(); for(j=1;j<=n;j++) if(!yy[j]) out.print(j+" "); out.println(); } out.close(); }// main(...) static void matrad() { int i,j; a=new int[m+1][n+1]; d=new char[m+1][n+1]; for(i=1;i<=m;i++) for(j=1;j<=n;j++) if(x.charAt(i-1)==y.charAt(j-1)) { a[i][j]=1+a[i-1][j-1]; d[i][j]=diag;

16.2. PROBLEME REZOLVATE } else { a[i][j]=max(a[i-1][j],a[i][j-1]); if(a[i-1][j]>a[i][j-1]) d[i][j]=sus; else d[i][j]=stanga; } }// matrad() static void osol(int lin, int col) { if((lin==0)||(col==0)) return; if(d[lin][col]==diag) osol(lin-1,col-1); else if(d[lin][col]==sus) osol(lin-1,col); else osol(lin,col-1); if(d[lin][col]==diag) xx[lin]=yy[col]=true; }// osol(...) static int max(int a, int b) { if(a>b) return a; else return b; }// max(...) }// class

381

16.2.13

Pav˘ri - ONI2001 clasa a X-a a

prof. Doru Popescu Anastasiu, Slatina Se d˘ un dreptunghi cu lungimea egal˘ cu 2N centimetri ¸i l˘¸imea egal˘ cu a a s at a 3 centimetri . Cerint˘ ¸a S˘ se determine num˘rul M al pav˘rilor distincte cu dale dreptunghiulare a a a care au lungimea egal˘ cu un centimetru ¸i l˘¸imea egal˘ cu 2 centimetri. a s at a Datele de intrare Fi¸ier de intrare: pavari.in s Linia 1: N - num˘r natural nenul, reprezentˆnnd jum˘tatea lungimii drepa a a tunghiului. Datele de ie¸ire s Fi¸ier de ie¸ire: pavari.out s s Linia 1: M - num˘r natural nenul, reprezentˆnd num˘rul modalit˘¸ilor de a a a a at pava dreptunghiul. Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ N ≤ 100

yv=new int[1]. int[] y) { int nx=x.xn). y[1]=4. PROGRAMARE DINAMICA pavari. // x[n]=x[n-1]+2*y[n-1]. x[1]=3. xn=xv.in 2 ˘ CAPITOLUL 16.length-1.t. yv[0]=4. { public static void main(String[] args) throws IOException { int k. .*. int nz.nextToken().kk.io.kk>=0. else nz=ny+1. } PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("pavari.out"))).ny=y. for(kk=xn.) static int[] suma(int[] x.382 Exemplu pavari.kk--) out. xv[0]=3.yn.yv)).length. yv=yn. st..i.nval. }// main(.k++) { xn=suma(xv. class Pavari // y[n]=y[n-1]+x[n].n. out. n=(int)st..print(xn[kk]).k<=n.xn. yn=suma(yv. if(nx>ny) nz=nx+1.in"))). int[] xv.yv. xv=xn.length. for(k=2.suma(yv.close(). xv=new int[1].out 11 Timp maxim de executare: 1 secund˘/test a Codul surs˘ * a import java. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("pavari9.

pe care Gigel poate ın ¸ s a atˆrna greut˘¸i distincte din colectia sa de G greut˘¸i (numere naturale ˆ a at ¸ at ıntre 1 ¸i s 25). dar acum dore¸te s˘ ¸tie ˆ cˆte s a ¸ s a s ın a moduri poate fi ea echilibrat˘.2. dar trebuie s˘ foloseasc˘ a a at a a a toate greut˘¸ile de care dispune. } }//suma } 383 16. } if(z[nz-1]!=0) return z. De fapt.i++) { z[i]=t. for(i=0. z[i]=z[i]%10. ¸ Se presupune c˘ este posibil s˘ se echilibreze balanta (va fi posibil pe toate a a ¸ testele date la evaluare). at Folosindu-se de experienta particip˘rii la Olimpiada National˘ de Informatic˘. la aceste brate sunt ata¸ate cˆrlige.2.16. scrieti un program care calculeaz˘ ˆ cˆte moduri se poate echilibra ¸ ¸ a ın a balanta.14 Balanta ONI2002 clasa a X-a ¸ Gigel are o ”balant˘” mai ciudat˘ pe care vrea s˘ o echilibreze. Datele de intrare Fi¸ierul de intrare balanta. if(i<nx) z[i]+=x[i]. else { int[] zz=new int[nz-1]. PROBLEME REZOLVATE int[] z=new int[nz].i<nz. t=0. Gigel poate atˆrna oricˆte greut˘¸i de orice cˆrlig. if(i<ny) z[i]+=y[i].i++) zz[i]=z[i]. ¸a ¸ a a Balanta lui Gigel dispune de dou˘ brate de greutate neglijabil˘ ¸i lungime ¸ a ¸ a s 15 fiecare.i<nz-1. Din loc ˆ loc. ¸ a ¸ a a Gigel a reu¸it s˘ echilibreze balanta relativ repede.in are urm˘toarea structur˘: s a a . ¸a a a aparatul este diferit de orice balant˘ pe care ati vazut-o pˆn˘ acum. for(i=0. return zz. a Cerint˘ ¸a Cunoscˆnd amplasamentul cˆrligelor ¸i setul de greut˘¸i pe care Gigel ˆ are a a s at ıl la dispozitie. t=z[i]/10.

) au fost prezentate ante¸ ¸ ¸ rior. reprezentˆnd valorile greut˘¸ilor pe care Gigel le va folosi pentru a echilibra a at balanta. distincte. Exemplu balanta. iar semnul precizeaz˘ bratul balantei la care este ata¸at crligul. dac˘ suma S s-a obtinut cu primele i−1 greut˘¸i ˆ M moduri. ¸ a ¸ ¸ s ”-” pentru bratul stˆng ¸i ”+” pentru bratul drept. Initial i = 0 ¸i suma 0 se poate obtine ˆ at ¸ s ¸ ıntr-un singur mod. valori sepa a s a at arate prin spatiu.ˆ toate configuratiile ¸ a a a ın ¸ precedente. s • celelalte restrictii (lungimea bratelor balantei etc. folosind primele i a ın a a greut˘¸i. reprezentˆnd amplasamentele cˆrligelor s a a fat˘ de centrul balantei.000. PROGRAMARE DINAMICA • pe prima linie. separate prin spatiu. a at ın punˆnd greutatea i pe cˆrligul k se va obtine suma S +(greutate[i]∗coordonata[k]) a a ¸ . ¸ Restrictii ¸i preciz˘ri ¸ s a • 2 ≤ C ≤ 20.a i-a .out contine o singur˘ linie. ¸ Datele de ie¸ire s Fi¸ierul de ie¸ire balanta. C numere ˆ a ıntregi. La fiecare astfel de pas i se calculeaz˘ ˆ cˆte moduri putem s a ın a obtine fiecare sum˘ introducˆnd o nou˘ greutate . ¸ • pe urm˘toarea linie. • greut˘¸ile folosite au valori naturale ˆ at ıntre 1 ¸i 25. • balanta se echilibreaz˘ dac˘ suma produselor dintre greut˘¸i ¸i coordonatele ¸ a a at s unde ele sunt plasate este 0 (suma momentelor greut˘¸ilor fat˘ de centrul balantei at ¸a ¸ este 0). restul sumelor ˆ 0 moduri. a Se calculeaz˘ ˆ cˆte moduri se poate scrie fiecare sum˘ j.384 ˘ CAPITOLUL 16.000. s • num˘rul M cerut este ˆ a ıntre 1 ¸i 100. pe care se afl˘ un num˘r s s ¸ a a a natural M . num˘rul de variante de plasare a greut˘¸ilor care duc la echilibrarea a at balantei. ın Urmeaza G pa¸i. cuprinse ˆ a ıntre 1 ¸i 25 ins clusiv. ¸ a s ¸ • pe urm˘toarea linie. cu ¸ valori cuprinse ˆ ıntre −15 ¸i 15 inclusiv. valoarea absolut˘ a numerelor reprezint˘ distanta fat˘ de ¸a ¸ a a ¸ ¸a centrul balantei.in 24 -2 3 3458 balanta. 2 ≤ G ≤ 20. Practic.out 2 Timp maxim de executare: 1 secund˘/test a Indicatii de rezolvare * ¸ Solutia comisiei ¸ Problema se rezolva prin metoda program˘rii dinamice. num˘rul C de cˆrlige ¸i num˘rul G de greut˘¸i. G numere naturale distincte.

ˆ Inainte de a testa posibilit˘ile de plasare a greut˘¸ii. Suma maxim˘ este a ¸ a 15 ∗ 25 ∗ 20 = 7500. PROBLEME REZOLVATE 385 ˆ M moduri (la care. Pentru a ˆ s ımbun˘t˘¸i viteza de a at executie a programului. a a a ˆ continuare. . indicii vor varia ˆ ¸ ıntre −300g ¸i 300g. vom putea trece la o nou˘ greutate. Pot fi realizate ˆ at ımbun˘t˘¸iri suplimentare dac˘ se determin˘ distantele a at a a ¸ maxime fat˘ de mijlocul balantei ale celor mai ˆ ¸a ¸ ındep˘rtate cˆrlige de pe cele dou˘ a a a talere ¸i suma total˘ a greut˘¸ilor. evident. d). ¸irul b va contine a at s ¸ doar zerouri. pe balant˘ nu este ag˘¸at˘ nici o greutate. iar ¸irul b va fi reinitializat cu s ın s s ¸ 0. at Analiza complexit˘¸ii at Pentru studiul complexit˘¸ii vom nota num˘rul greut˘¸ilor cu g. de fapt se memoreaza doar ¸ ultimele dou˘ linii (fiecare linie se obtine din precedenta). Ca urmare. Ca urmare. se pot adauga alte moduri de obtinere plasˆnd ın ¸ a greutatea i pe un alt cˆrlig ¸i folosind suma respectiv˘). initial valorile ai vor fi 0 pentru orice indice nenul at ¸ ¸i a0 = 1 (exist˘ o posibilitate ca initial suma momentelor greut˘ilor s˘ fie 0 ¸i nu s a ¸ a a s exist˘ nici o posibilitate ca ea s˘ fie diferit˘ de 0). S˘ presupunem c˘ la un moment a a a a dat exist˘ ai posibilit˘¸i de a obtine suma i. ¸ ın a GInfo 12/6 octombrie 2002 Se observ˘ c˘ suma momentelor greut˘¸ilor este cuprins˘ ˆ a a at a ıntre −6000 ¸i 6000 s (dac˘ avem 20 de greut˘¸i cu valoarea 20 ¸i acestea sunt amplasate pe cel mai a at s ndep˘rtat cˆrlig fat˘ de centrul balantei. Fiecare greutate a a at a poate fi amplasat˘ pe oricare dintre cˆrlige. atunci modulul sumei momentelor fortelor ˆ a a ¸a ¸ ¸ este 152020 = 6000). Dup˘ s a considerarea tuturor perechilor. s a at a ¸ s atunci indicii vor varia ˆ ıntre −d1 s ¸i d2 s.2. a ın ¸ respectiv O(c). valoarea bi+gd va cre¸te cu ai . dup˘ amplasarea noii greut˘¸i exist˘ ai posibilit˘¸i de a a a at a at obtine suma i+gd. cu elemente numere ˆ ıntregi pe 32 de biti. putem p˘stra un ¸ir a ale c˘rui valori ai vor a s a contine num˘rul posibilit˘¸ilor ca suma momentelor greut˘¸ilor s˘ fie i. a ın ¸ Rezultatul final se obtine pe ultima linie. suma momentelor a ¸ ¸a ¸ greut˘¸ilor va cre¸te sau va sc˘dea cu gd (ˆ functie de bratul pe care se afl˘ at s a ın ¸ ¸ a cˆrligul). Dac˘ distantele sunt d1 ¸i d2 . s Initial. iar cel al at a at cˆrligelor cu c. ˆ coloana asociat˘ sumei 0. a¸adar suma momentelor ¸ ¸a at a s greut˘¸ilor este 0. ¸ a at at a Indicii ¸irului vor varia ˆ s ıntre −6000 ¸i 6000. unde g este num˘rul s a greut˘¸ilor. deci ordinul de complexitate al acestor operatii este O(g). iar suma este s.16. a Citirea datelor de intrare corespunz˘toare cˆrligelor ¸i greutatilor se reala a s ¸ izeaz˘ ˆ timp liniar. vom ˆ In ıncerca s˘ amplas˘m greut˘¸ile pe cˆrlige. Dac˘ vom amplasa o greutate de a at ¸ a valoare g pe un cˆrlig aflat la distanta d fat˘ de centrul balantei. Pentru fiecare pereche (i. Rezultatul final va fi dat de valoarea a0 obtinut˘ dup˘ considerarea tuturor ¸ a a greut˘¸ilor disponibile. a Valorile din ¸irul b vor fi salvate ˆ ¸irul a. a s a ˆ acest mod s-ar construi o matrice cu G linii ¸i 2 ∗ (sumamaxima) + 1 In s coloane. deci o linie se incadreaz˘ ˆ mai putin de 64K. Ca urmare. Consider˘m c˘ ¸irul b va contine valori care reprezint˘ num˘rul ¸ a as ¸ a a posibilit˘¸ilor de a obtine sume ale momentelor fortelor dup˘ amplasarea greut˘¸ii at ¸ ¸ a at curente.

out. t1=System. Codul surs˘ a import java. t2=System.currentTimeMillis(). vom considera at ¸ a at s c˘ ordinul de complexitate al travers˘rii ¸irului a este O(g). Ca urmare.nextToken().currentTimeMillis(). A¸adar. System. scrieSolutia(). static int[] carlig=new int[20]. determinaSolutia().in"))). static int nrCarlige.println("TIMP = "+(t2-t1)+" milisecunde"). a a s Num˘rul de travers˘ri este dat tot de num˘rul greut˘¸ilor disponibile. deci a a a at vom avea O(g) travers˘ri. PROGRAMARE DINAMICA Singura m˘rime care nu este considerat˘ constant˘ ¸i de care depinde num˘rul a a as a de posibilit˘¸i de a obtine sumele este num˘rul greut˘¸ilor. st. static long[] b=new long[15001]. }// main() static void citesteDatele() throws IOException { StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("balanta9.io. citesteDatele(). ˆ concluzie. Rezult˘ c˘ ordinul de complexitate al unei a a parcurgeri este O(g)O(c) = O(gc).t2. // // // // a[i] i = -7500.nval. nrGreutati. ordinul de complexitate al algoritmului de rezolvare a acestei In probleme este O(c) + O(g) + O(g 2 c) + O(1) = O(g 2 c).*.386 ˘ CAPITOLUL 16. static int[] greutate=new int[20]. a ˆ timpul travers˘rii vom considera toate cˆrligele pentru fiecare element al In a a ¸irului. nrCarlige=(int)st. ¸ ¸ Afi¸area num˘rului de posibilit˘¸i de a echilibra balanta se realizeaz˘ ˆ timp s a at ¸ a ın constant. 7500 sir auxiliar (a+7500)!!! coordonatele carligelor valorile greutatilor public static void main(String[] args) throws IOException { long t1. class Balanta { static long[] a=new long[15001]. ˆ timp ce ordinul de complexitate al ˆ ın ıntregii operatii care duce la obtinerea rezultatului este O(g)O(gc) = O(g2c). ordinul de complexitate al operatiilor efectuate asupra unui s ¸ element ˆ ıntr-o parcurgere este O(c). .

} }// citesteDate() 387 static void scrieSolutia() throws IOException { PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("balanta9. for (j=-7500.i++) { st.j<=7500. a[7500+0]=1.nextToken().k<nrCarlige.k.j++) if(a[7500+j]!=0) for(k=0.i<nrGreutati.i++) { st.close(). carlig[i]=(int)st.j<=7500.nval. out.16.j++) { a[7500+j]=b[7500+j].i<nrCarlige.2. }// scrieSolutia() static void determinaSolutia() { int i.j. Acesta nu e multumit de ¸ ¸ ın a ın ¸ a ¸ .i<nrGreutati.15 Aliniere ONI2002 clasa a X-a ˆ armat˘. // initial balanta este echilibrata for(i=0.nextToken(). PROBLEME REZOLVATE st.nextToken(). out. o companie este alc˘tuit˘ din n soldati. nrGreutati=(int)st.i++) { for(j=-7500.k++) b[7500+j+carlig[k]*greutate[i]]+=a[7500+j]. La inspectia de dimineat˘ In a a a ¸ ¸ ¸a soldatii stau aliniati ˆ linie dreapt˘ ˆ fata c˘pitanului.out"))). for(int i=0. } } }// determinaSolutia() }// class 16.nval. greutate[i]=(int)st. b[7500+j]=0.2.nval. } for(int i=0.print(a[7500+0]).

cunoscˆnd ˆ altimea fiec˘rui soldat.5. 6 avˆnd ˆ altimile 1. 5.30621 2 1. ¸ s a a Timp maxim de executare: 1 secund˘/test a Indicatii de rezolvare ¸ Solutia comisiei ¸ Problema se rezolv˘ prin metoda program˘rii dinamice. ın˘ ¸ Datele de ie¸ire s Fi¸ierul aliniere. ¸ a Restrictii ¸i preciz˘ri ¸ s a • 2 ≤ n ≤ 1000 • ˆ altimile sunt numere reale ˆ intervalul [0. a ¸ ¸ Datele de intrare Pe prima linie a fi¸ierului de intrare aliniere. C˘pitanul cere cˆtorva soldati s˘ ias˘ din ın ın˘ ¸ a a ¸ a a rˆnd. e drept c˘ soldatii sunt a¸ezati ˆ ordinea numerelor de cod 1. a a . dar nu ˆ ordinea ˆ altimii.86 1. cel putin una din extremit˘¸i (stˆnga sau s ¸ at a dreapta).. iar pe linia urm˘toare un ¸ir de n numere reale. Dac˘ exist˘ mai multe a a a a ¸ a a solutii posibile..4 ¸i 1.. . f˘r˘ a-¸i schimba locurile. at Soldatii cu codurile 5 ¸i 6 v˘d extremitatea dreapt˘. 2. ın˘ ¸ ın Exemplu aliniere. 4.out va contine pe prima linie num˘rul soldatilor care tres ¸ a ¸ buie s˘ p˘r˘seasc˘ formatia.86.86 1.out 8 4 1. doar apropiindu-se unul de a a s aa s altul (pentru a nu r˘mˆne spatii mari ˆ a a ¸ ıntre ei) s˘ formeze un ¸ir ˆ care fiecare a s ın soldat vede privind de-a lungul ¸irului. 1. n a ¸ s ¸ ın din registru.2 1 3 7 8 Explicatie ¸ R˘mˆn soldatii cu codurile 2.in aliniere. a a Soldatul cu codul 4 vede ambele extremit˘¸i. Al k-lea num˘r de pe aceast˘ linie reprezint˘ s ¸ a a a ˆ altimea soldatului cu codul k (1 ≤ k ≤ n).97 2.4 1 1.388 ˘ CAPITOLUL 16.in este scris num˘rul n al s a soldatilor din ¸ir. ın˘ ¸ a Cerint˘ ¸a Scrieti un program care determin˘. cu maximum 5 ¸ s a s zecimale fiecare ¸i separate prin spatii. separate dou˘ cˆte dou˘ printr-un spatiu. Un soldat vede o extremitate dac˘ ˆ a ıntre el ¸i cap˘tul respectiv nu exist˘ s a a un alt soldat cu ˆ altimea mai mare sau egal˘ ca a lui. a a ¸ a ın˘ ¸ s Soldatul cu codul 2 vede extremitatea stˆng˘. se va scrie una singur˘. astfel ca cei r˘ma¸i. 2. 2. num˘rul ¸ a a ın˘ ¸ a a minim de soldati care trebuie s˘ p˘r˘seasc˘ formatia astfel ca ¸irul r˘mas s˘ ¸ a aa a ¸ s a a ˆ ındeplineasc˘ conditia din enunt. iar pe linia urm˘toare codurile acestora ˆ ordine a aa a ¸ a ın cresc˘toare. PROGRAMARE DINAMICA ceea ce vede.5].

iar cel˘lalt spre stˆnga.2. cu ordinul de complexitate O(n2 ). Din a a s aceste motive. lungimea celui mai lung sub¸ir strict a s cresc˘tor care se termin˘ cu el ¸i lungimea celui mai lung sub¸ir strict descresc˘tor a a s s a care ˆ ıncepe cu el. PROBLEME REZOLVATE 389 Se calculeaz˘. dup˘ care se va c˘uta valoarea maxim˘ a sumei lungimilor a a a a ¸irurilor corespunz˘toare unui soldat. ceilalti spre ¸ ¸ s ¸ dreapta. a s ın s Dup˘ identificarea celor doi soldati de ˆ altimi egale (sau demonstrarea faptua ¸ ın˘ ¸ lui c˘ nu exist˘ o pereche de acest gen care s˘ respecte conditiile date) se marcheaz˘ a a a ¸ a toti soldatii din cele dou˘ sub¸iruri. timpul de executie admis ne permite s ¸ folosirea unui algoritm simplu.16. respectiv cel mai ın˘ ¸ ¸ a lung sub¸ir strict descresc˘tor de soldati care urmeaz˘ dup˘ el. la stˆnga sau la dreapta sa poate s˘ se afle un soldat de aceea¸i a a s ˆ altime. Soldatii din primul sub¸ir privesc spre stanga. fie spre dreapta ¸irului. corespunz˘toare celui mai ˆ a a a ınalt soldat dintre cei r˘ma¸i ˆ ¸ir. unul dintre cei doi va privi spre dreapta. Deoarece s-a considerat c˘ o parte din concurenti vor rezolva problema pentru a ¸ un singur soldat central (toti ceilalti soldati p˘strati avˆnd ˆ ¸ ¸ a a ınaltimea mai mic˘) ¸i a s nu vor observa cazul ˆ care se pot p˘stra doi soldati de aceea¸i ˆ ¸ime. a ın GInfo 12/6 octombrie 2002 Pentru fiecare soldat vom determina cel mai lung sub¸ir strict cresc˘tor (din s a punct de vedere al ˆ altimii) de soldati care se termin˘ cu el. deci ordinul de coma ın plexitate al acestei operatii este O(n). Acesta poate privi fie spre stˆnga. respectiv strict descresc˘tor. Pentru aceasta se parcurge a ¸ s . Chiar dac˘ s-ar p˘rea c˘ ˆ acest mod am g˘sit as a a a a ın a solutia problemei. mai exist˘ o posibilitate de a m˘ri num˘rul soldatilor care r˘mˆn ¸ a a a ¸ a a ˆ ¸ir. Urmeaz˘ eventuala identificare a unui alt soldat de aceea¸i ˆ altime care a s ın˘ ¸ respect˘ conditiile referitioare la lungimile sub¸irurilor. a¸adar identificarea soldatului care poate s a s privi ˆ ambele directii este o operatie cu ordinul de complexitate O(n2 ) + O(n2 ) + ın ¸ ¸ O(n) = O(n2 ). s a ¸ a a Dup˘ aceast˘ operatie. S˘ consider˘m soldatul cel mai ˆ ın s a a ınalt ˆ ¸irul r˘mas (cel c˘ruia ˆ corespunde ın s a a ıi suma maxim˘). Se au in vedere cazurile particulare. Totu¸i. pentru fiecare element. Acesta va fi aplicat de dou˘ ori. Solutia const˘ ˆ p˘strarea a dou˘ astfel de sub¸iruri de soldati (unul cresc˘tor a ın a a s ¸ a ¸i unul descresc˘tor) pentru DOI soldati de aceea¸i ˆ altime (eventual identici) ¸i s a ¸ s ın˘ ¸ s eliminarea celorlalti. majoriın a ¸ s ınalt tatea testelor se ˆ ıncadreaz˘ ˆ acest caz. ın˘ ¸ a a s nu putem alege orice soldat cu aceea¸i ˆ altime. Ceilalti soldati vor trebui s˘ p˘r˘seasc˘ formatia. Primul sub¸ir se termin˘ inainte de a ˆ s a ıncepe al doilea. ci doar unul pentru care lungimea s ın˘ ¸ ¸irului strict cresc˘tor (dac˘ se afl˘ spre stˆnga) sau a celui strict descresc˘tor s a a a a a (dac˘ se afl˘ spre dreapta) este aceea¸i cu lungimea corespunz˘toare ¸irului strict a a s a s cresc˘tor. vom determina soldatul pentru care suma lungima a ¸ ilor celor dou˘ ¸iruri este maxim˘. ¸ ¸ a s ¸ ¸ a aa a ¸ Analiza complexit˘¸ii at Citirea datelor de intrare se realizeaz˘ ˆ timp liniar. ¸ Chiar dac˘ exist algoritmi eficienti (care ruleaz˘ n timp liniar-logaritmic) de a ¸ a determinare a celui mai lung sub¸ir ordonat.

t1=System. subsirCresc().println("TIME = "+(t2-t1)+" millisec "). System. nr soldati ramasi static int[] predCresc. // inaltimile soldatilor static int ns.io. // ramas in sir public static void main(String[] args) throws IOException { long t1.in". }// main() static void citescDate() throws IOException { StreamTokenizer st=new StreamTokenizer( .currentTimeMillis(). // predecesor in subsirul crescator static int[] lgCresc.390 ˘ CAPITOLUL 16. ˆ timpul parcurgerii sub¸irurilor sunt marcati soldatii care r˘mˆn a In s ¸ ¸ a a ˆ formatie. ˆ momena s a ın a ın tul construirii celor dou˘ sub¸iruri. citescDate(). Deteminarea celor dou˘ sub¸iruri se realizeaz˘ ˆ timp liniar dac˘. operatia necesit˘ s ¸ at ¸ a un timp liniar. ˆ concluzie. scrieRezultate(). PROGRAMARE DINAMICA ¸irul soldatilor de la soldatul identificat anterior spre extremit˘¸i. // lungimea sirului crescator care se termina cu i static int[] succDesc. // succesor in subsirul descrescator static int[] lgDesc. respectiv succesorul a s a a fiec˘rui soldat. t2=System.*. nsr. // nr soldati. alegeSoldati(). subsirDesc().t2. class Aliniere { static final String fisi="aliniere. // lungimea sirului descrescator care urmeaza dupa i static boolean[] ramas.currentTimeMillis(). ın ¸ Pentru scrierea datelor de ie¸ire se parcurge ¸irul marcajelor ¸i sunt identificati s s s ¸ soldatii care p˘r˘sesc formatia.out. static float[] inalt. Ordinul de complexitate al acestei operatii este ¸ aa ¸ ¸ O(n). ordinul de complexitate al algoritmului de rezolvare a acestei In probleme este O(n) + O(n2 ) + O(n) + O(n) + O(n) = O(n2 ). Codul surs˘ a import java. se p˘streaz˘ predecesorul.

nval. predCresc=new int[ns]. inalt[i]=(float)st. } } }//subsirCresc() static void subsirDesc() { int i.i>=0. succDesc=new int[ns].i<ns. // nu exista nici un soldat mai mic dupa ns-1 succDesc[ns-1]=-1. for(int i=0.j<i.j--) if(inalt[j]<inalt[i]) // soldat mai mic if(lgDesc[i]<lgDesc[j]+1) // sir mai lung .nval.i++) { lgCresc[i]=1. lgDesc[ns-1]=0. lgDesc=new int[ns].} }//citescDate() static void subsirCresc() { int i.16.j. ramas=new boolean[ns]. st.j++) if(inalt[j]<inalt[i]) if(lgCresc[i]<lgCresc[j]+1) // sir mai lung { lgCresc[i]=lgCresc[j]+1.2.nextToken(). PROBLEME REZOLVATE 391 new BufferedReader(new FileReader(fisi))). for(i=1.j. // i nu are succesor for(j=ns-1. // nu exista nici un soldat mai mic dupa i succDesc[i]=-1. ns=(int)st.j>i. predCresc[0]=-1. inalt=new float[ns].i--) { lgDesc[i]=0.nextToken(). lgCresc[0]=1.i<ns. // nu are predecesor for (int j=0. // subsirul formar doar din i predCresc[i]=-1. lgCresc=new int[ns]. predCresc[i] = j. // ns-1 nu are succesor for(i=ns-2.i++) {st.

if(ic!=id) // in "mijloc" sunt doi soldati cu aceeasi inaltime nsr++. while(id!=-1) // indice descrescator { ramas[id]=true. // indicii care delimiteaza in interior cele doua subsiruri for(i=0.i<ns. ic=predCresc[ic].i++) if(lgCresc[i]+lgDesc[i]>nsr) { nsr=lgCresc[i]+lgDesc[i].i<ns.392 { ˘ CAPITOLUL 16. } while(ic!=-1) // indice crescator { ramas[ic] = true. } // in "mijlocul" sirului se pot afla doi soldati cu aceeasi inaltime ic=im. // caut in stanga un subsir cu aceeasi lungime --> soldat cu aceeasi inaltime for(i=im-1. id. } }// alegeSoldati() . id=im. succDesc[i]=j. id=succDesc[id]. PROGRAMARE DINAMICA lgDesc[i]=lgDesc[j]+1.i>=0. // caut in dreapta un subsir cu aceeasi lungime --> soldat cu aceeasi inaltime for(i=im+1. im=i. // este posibil ca in mijloc sa fie doi soldati cu inaltimi egale int im=-1.i--) if(lgCresc[ic]==lgCresc[i]) ic=i. } } }// subsirDesc() // actualizarea lg subsir // actualizare succesor static void alegeSoldati() { int i.i++) if(lgDesc[id]==lgDesc[i]) id=i. // indicele soldatului din mijloc int ic.

Urm˘toarele N linii contin s ¸ ¸ a ¸ coordonatele vˆrfurilor. separate prin spatii.16 Munte . out. doar cu statia K − 1. ¸ ın a Se dore¸te ca lantul de telecabine s˘ asigure leg˘tura ˆ s ¸ a a ıntre vˆrful 1 ¸i vˆrful a s a N . a ın a s fiecare vˆrf i fiind precizat prin coordonata X[i] pe axa OX ¸i prin ˆ altimea H[i].ONI2003 cls 10 ˆ Intr-o zon˘ montan˘ se dore¸te deschiderea unui lant de telecabine. statia 1 va fi conectat˘ doar cu statia 2. ¸ a Vˆrfurile sunt date ˆ ordine de la stˆnga la dreapta ¸i numerotate de la 1 la N .print((i+1)+" "). a ¸ s ¸ a ¸ iar statia K.in contine trei numere ˆ s ¸ ıntregi N .2. Statiile a a s ¸ ¸ de telecabine pot fi ˆ ınfiintate pe oricare din cele N vˆrfuri ale zonei montane. ˆ cazul ˆ care cele trei vˆrfuri sunt coliniare. ¸ Date de ie¸ire s ˆ fi¸ierul munte. a a a Date de intrare Prima linie a fi¸ierului de intrare munte.out"))). cu restrictiile de mai sus. a s ın˘ ¸ Se vor ˆ ınfiinta exact K statii de telecabine. ˆ plus.nsr). Astfel.out veti afi¸a: In s ¸ s . out. K ¸i L. pe toate testele date la evaluare. a ¸ a s separate printr-un spatiu. for(int i=0. X[i] ¸i H[i]. chiar dac˘ distan c dintre vrfurile i ¸i j este mai mic˘ decˆt L. se dore¸te ca lungimea total˘ a cablurilor folosite pentru conectare s a s˘ fie minim˘. iar statia K ˆ vˆrful N .16. Statia 1 va fi obligatoriu amplasat˘ ˆ vˆrful ¸ ¸ ¸ a ın a 1.println(ns.i<ns. cu semnificatiile de mai sus. }// scriuRezultate() }// class 16. Statia de telecabine i (2 ≤ i ≤ K) ¸ ¸ ¸ va fi conectat˘ cu statiile i − 1 ¸i i + 1. vˆrfurile i ¸ a a a ¸i j (i < j) nu pot fi conectate direct dac˘ exist˘ un vˆrf v (i < v < j) astfel ˆ at s a a a ıncˆ segmentul de dreapta care ar uni vˆfurile i ¸i j nu ar trece pe deasupra vˆrfului a s a v. Mai mult.close(). a a O restrictie suplimentar˘este introdus˘ de formele de relief.i++) if(!ramas[i]) out. ¸ a s a a Cerint˘ ¸a Dˆndu-se amplasarea celor N vrfuri ale lantului muntos. a a ¸ Se garanteaz˘ c˘. Lungimea cablului folosit pentru a conecta dou˘ statii este egal˘ cu a a a ¸ a distanta dintre ele.2. un cablu care une¸te dou˘ statii consecutive nu poate ¸ In s a ¸ avea lungimea mai mare decˆt o lungime fixat˘ L. conectarea va fi posibil˘. PROBLEME REZOLVATE 393 static void scrieRezultate() throws IOException { PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("aliniere. se consider˘ toate trei ca fiind In ın a a statii. stabiliti o modalitate a ¸ ¸ de dispunere a celor K statii de telecabine astfel ˆ at lungimea total˘ a cablurilor ¸ ıncˆ a folosite pentru conectare s˘ fie minim˘. linia i + 1 contine coordonatele vˆrfului i.

13/6 . a Aceast˘ subproblema se rezolv˘ prin metoda program˘rii dinamice dup˘ cum a a a a urmeaz˘: se construie¸te o matrice A cu K linii ¸i N coloane. unde Ai. a Indicatii de rezolvare . Dac˘ exist˘ mai multe variante. Se va obtine o matrice OK. GInfo nr. a a Practic. astfel ˆ at oricare dou˘ statii consecutive s˘ aib˘ s ¸ ¸ ıncˆ a ¸ a a OK-ul corespunz˘tor egal cu 1.pentru a ilustra restrictia introdus˘ de formele de relief.trasarea unui cablu direct ˆ ıntre vˆrfurile 1 ¸i 5 ar fi contravenit restrictiei a s ¸ referitoare la lungimea maxim˘ a unui cablu. s-ar fi obtinut o solutie cu 2 a ın ¸ ¸ statii de telecabine ˆ loc de 3 (deci solutia ar fi invalid˘ ¸i pentru valori mari ale ¸ ın ¸ as lui L).descriere solutie * ¸ ¸ Mihai Stroe.in munte. s ¸ Restrictii ¸i preciz˘ri ¸ s a 2 ≤ N ≤ 100 2 ≤ K ≤ 30 ¸i K ≤ N s 0 ≤ L. Primul pas const˘ ımp˘ ¸ ın a a ˆ determinarea perechilor de vˆrfuri care pot fi unite printr-un cablu. . rotunjit˘ la cel mai a a a apropiat numar ˆ ıntreg (pentru orice ˆ ıntreg Q. ordonate cresc˘tor. a ¸ a a afi¸ati una oarecare.j reprezint˘ a s s a lungimea total˘ minim˘ a unui lant cu i statii care conecteaz˘ vˆrfurile 1 ¸i j.pe a doua linie K numere distincte ˆ ıntre 1 ¸i N .octombrie 2003 Problema se rezolv˘ prin metoda program˘rii dinamice.pe prima linie lungimea total˘ minim˘ a cablurilor. X[i].5 se rotunjeste la Q + 1). s a ın˘ ¸ a vˆrfurile 5 ¸i 7 nu au putut fi conectate direct datorit˘ ˆ altimii vˆrfului 6. Acest pas ın a se rezolv˘ folosind cuno¸tinte elementare de geometrie plan˘ (ecuatia dreptei. Q.000 ¸i X[i] < X[i + 1] s Exemplu munte. a a ¸ ¸ a a s . ˆ plus. De asemenea. unde OKi. a s a ın˘ ¸ a Timp maxim de executare: 1 secund˘/test. numerele s a vˆrfurilor ˆ care se vor ˆ a ın ınfiin¸ statii de telecabine.out 7 5 11 22 0 16 13567 43 68 74 12 16 13 16 14 16 Explicatii ¸ . Folosind aceast˘ matrice. problema se poate ˆ arti ˆ dou˘ subprobleme. y = a s ¸ a ¸ a ∗ x + b). preciz˘m c˘ vˆrfurile ¸ a a a a 1 ¸i 4 nu au putut fi conectate direct datorit˘ ˆ altimii vˆrfului 3. . H[i] ≤ 100. PROGRAMARE DINAMICA . se vor conecta vˆrfurile s ın a a 1 ¸i N cu un lant de K statii.j are valoarea 1 dac˘ vˆrfurile i ¸i ¸ a a s j pot fi unite ¸i 0 ˆ caz contrar.394 ˘ CAPITOLUL 16.

N . st. ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O(N 3 ). ¸ s Pentru i cuprins ˆ ıntre 2 ¸i N . unde s Ti. static int n. double[][] a. n=(int)st.t2. componentele matricei se calculeaz˘ astfel: s a Ai. PROBLEME REZOLVATE 395 Initial A1. public static void main(String[] args) throws IOException { long t1.v + dist(v. s Analiza complexit˘¸ii at Operatia de citire a datelor are ordinul de complexitate O(N ).nval. unde v < j ¸i OKv.io.m. a a a ın Subproblemele puteau fi tratate ¸i simultan. ¸ s s Deoarece K ≤ N .1 = 0. int[][] t.2. ceea ce complica implementarea.currentTimeMillis().16. ¸ Lungimea total˘ minim˘ este regasit˘ ˆ AK. j)}.nextToken()..j se construie¸te o matrice T .i = +∞ ¸i Ai. StreamTokenizer st=new StreamTokenizer(br). // cu mesaje pt depanare dar .1 = +∞ pentru i > 1.MAX_VALUE.. A1.in")). Codul surs˘ a Prima variant˘: a import java. Matricea T este folosit˘ a a a pentru reconstituirea solutiei. // m=nr statii (in loc de K din enunt) static static static static int[] x. boolean[][] ok.j reprezint˘ v-ul care minimizeaz˘ expresia de mai sus.h. Algoritmul bazat pe metoda program˘rii dinamice are ordinul de complexitate a O(N 2 · K). ¸ Calculul matricei OK are ordinul de complexitate O(N 3 ).*.L. int i. BufferedReader br=new BufferedReader(new FileReader("munte. Reconstituirea solutiei ¸i afi¸area au ordinul de complexitate O(N ).j = 1 s Concomitent cu calculul elementelor Ai. . t1=System. fara traseu class Munte1 { static final int oo=Integer.j = min{Ai−1.

nval.i<=m. a=new double[m+1][n+1].i++) a[i][1]=oo. afism(a). for(k=1. x=new int[n+1]. afism(ok). t=new int[m+1][n+1].kmin. for(j=2.out. L=(int)st. System.println(i+" "+j+" "+k+" "+ok[k][j]).nval.println("Timp = "+(t2-t1)).dkj.nextToken(). }// main() static void matriceaA() { int i. ok=new boolean[n+1][n+1].i++) { st. m=(int)st. matriceaA(). t2=System. kmin=-1.k<j. PROGRAMARE DINAMICA st. h[i]=(int)st.nextToken().j.i<=n. .nextToken().j++) a[1][j]=oo.396 ˘ CAPITOLUL 16.currentTimeMillis(). x[i]=(int)st. st. st.nval.j++) { min=oo.out.j<=n. // implicit este false for(i=1. h=new int[n+1]. for(i=2. } matriceaOK(). for(i=2.i++) { for(j=2.nval.i<=m. afisSolutia().nextToken(). a[1][1]=0.min. double d.k.j<=n.k++) { System.

j.ij<=j-1. return Math.out.kmin=k. sp2=(h[ij]-y1)*(x2-x1)-(y2-y1)*(x[ij]-x1). if(sp1*sp2<=0) { ok[i][j]=ok[j][i]=false. ij .out. afism(a).ij++) // i . }// dist(. } } }// for k a[i][j]=min.. PROBLEME REZOLVATE 397 if(ok[k][j]) { dkj=dist(k.j). }// for i }// matriceaA() static double dist(int i.out. }// for j System. for(ij=i+1. System. d=(double)(x[i]-x[j])*(x[i]-x[j])+(double)(h[i]-h[j])*(h[i]-h[j]). ok[i][j]=ok[j][i]=true. } if(!ok[i][j]) break..16.i++) { x1=x[i]. for(i=1. j { sp1=(0 -y1)*(x2-x1)-(y2-y1)*(x[ij]-x1)..y1.sp1.sp2.x2. y1=h[i].println("Linia: "+i).) static void matriceaOK() { int i. d=a[i-1][k]+dkj. break.println(i+" "+j+" ("+ij+")\t"+sp1+"\t"+sp2).i<=n-1.j++) { x2=x[j]. if(d<min) { min=d.println(i+" "+j+" "+k+" dkj="+dkj+" d="+d+" min="+min).y2.ij.x1. for(j=i+1.. int j) { double d.sqrt(d).j<=n.2. y2=h[j]. }//for ij . System.

println().i<a. System.i++) { for(j=1.println().out.length.out.println().. PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter("munte.length. }//afisSolutia() static void afism(int[][] a) { int i. for(i=1.j.out. }// afism(.close().out. System.j.i<a.j<a[i]. }// afism(.print(a[i][j]+" ").398 ˘ CAPITOLUL 16.length.length. out.. } .i<a.length.print(a[i][j]+" ").print(a[i][j]+" ").j)>L+0. out. for(i=1.i++) { for(j=1.j++) System.out. System. PROGRAMARE DINAMICA if(ok[i][j]) if(dist(i.) static void afism(double[][] a) { int i.) static void afism(boolean[][] a) { int i.j<a[i].j++) System.j.out.println().j.j++) System.j<a[i].out.out")))..println().length. } System. }//for j }// for i }// matriceaOK() static void afisSolutia() throws IOException { int i.i++) { for(j=0.0000001) ok[i][j]=ok[j][i]=false.println(a[m][n]). } System.out. for(i=0..

out. .nextToken(). statia=new int[m+1]. // FINAL: fara mesaje si cu traseu .nval. n=(int)st. t1=System. int[] statia..in")). t=new int[m+1][n+1].78) { // solutia: 1 4 5 .81) P4(300. a=new double[m+1][n+1].L. public static void main(String[] args) throws IOException { long t1. double[][] a. }// afism(.. h=new int[n+1].m..currentTimeMillis().i<=n.2.nval.nval.nextToken(). static int n.nextToken().t2. } matriceaOK(). x[i]=(int)st.nextToken(). st.*. BufferedReader br=new BufferedReader(new FileReader("munte. m=(int)st. int[][] t.i++) { st.nval. for(i=1.MAX_VALUE.nval. este gresita (1 4 nu trece de P2 si P3!) static final int oo=Integer.59) P2(171. x=new int[n+1]..println(). PROBLEME REZOLVATE System. st. L=(int)st.j.io.) }// class A doua variant˘: a 399 import java. st. st.nextToken(). // m=nr statii (in loc de K din enunt) static static static static static int[] x.h.. int i. ok=new boolean[n+1][n+1]. boolean[][] ok.16. h[i]=(int)st. dar class Munte2 // test 8 : P1(99.96) P3(239. StreamTokenizer st=new StreamTokenizer(br)..

out. return Math. } } }// for k a[i][j]=min. d=(double)(x[i]-x[j])*(x[i]-x[j])+(double)(h[i]-h[j])*(h[i]-h[j]).k. t2=System.. }// main() static void matriceaA() { int i. if(d<min) { min=d. for(i=m. }// for j }// for i }// matriceaA() static double dist(int i.currentTimeMillis().. for(j=2. PROGRAMARE DINAMICA matriceaA().min.j<=n. for(i=2. a[1][1]=0. j=n.i++) a[i][1]=oo. for(k=1. d=a[i-1][k]+dkj.400 ˘ CAPITOLUL 16.) .j++) a[1][j]=oo.k<j. j=t[i][j].i<=m. }// dist(.k++) { if(ok[k][j]) { dkj=dist(k. } afisSolutia(). System.j.kmin=k. int j) { double d.i>=1.println("Timp = "+(t2-t1)). t[i][j]=kmin. double d.i--) { statia[i]=j.j<=n.i++) { for(j=2.kmin.sqrt(d). for(i=2.i<=m.j++) { min=oo.j).dkj. kmin=0.

5)).ij++) // i .17 L˘custa .close(). }//for ij if(ok[i][j]) if(dist(i.2. PrintWriter out = new PrintWriter( new BufferedWriter(new FileWriter("munte.i<=m.x1.j)>L) ok[i][j]=ok[j][i]=false.j++) { x2=x[j].j<=n.println((int)(a[m][n]+0.out"))). sp2=(h[ij]-y1)*(x2-x1)-(y2-y1)*(x[ij]-x1). break.000=10. out. if(sp1*sp2<=0) { ok[i][j]=ok[j][i]=false. cu valori a a s naturale. }//for j }// for i }// matriceaOK() static void afisSolutia() throws IOException { int i.000.println(). PROBLEME REZOLVATE 401 static void matriceaOK() { int i. Travers˘m matricea pornind de la coltul stˆnga-sus la coltul dreapta-jos. } if(!ok[i][j]) break.000 depaseste int !!! for(i=1.000*100. }//afisSolutia() }// class 16. for(i=1..j. out. out. for(ij=i+1. ij .ij<=j-1.2.print(statia[i]+" ").y2.ij.i++) { x1=x[i].OJI2005 clasa a X-a a Se consider˘ o matrice dreptunghiular˘ cu m linii ¸i n coloane.i++) out.x2..000. a ¸ a ¸ .sp2. y2=h[j].i<=n-1. // 100. for(j=i+1. y1=h[i]. long sp1. ok[i][j]=ok[j][i]=true.16. j { sp1=(0 -y1)*(x2-x1)-(y2-y1)*(x[ij]-x1).y1.

out va contine o singur˘ linie pe care va fi scris˘ s s ¸ a a suma minim˘ g˘sit˘. reprezentˆnd num˘rul de linii ¸i respectiv num˘rul ¸ a a s a de coloane ale matricei. Exceptie face ultima deplasare (cea a a ¸ ˆ care ne afl˘m pe ultima linie). separate prin cˆte un spatiu. Cerint˘ ¸a Scrieti un program care s˘ determine suma minim˘ care se poate obtine ¸ a a ¸ pentru o astfel de traversare. Astfel traversarea ¸ s a va consta din vizitarea a 2m celule. cˆte n a a a numere pe fiecare linie. cˆnd vom face doar un salt pentru a ajunge ˆ ın a a ın coltul dreapta-jos.1 + a1. 255].in 45 34579 66344 63396 65382 lacusta.i = a1. Datele de intrare Fi¸ierul de intrare lacusta. Pe urm˘toarele m linii este descris˘ matricea. 2) → (3.in contine pe prima linie dou˘ numere naturale s ¸ a separate printr-un spatiu m n. Vom completa initial elementele de pe a doua linie a matricei B. 3) → (2. 3) → (4. La fiecare deplasare se execut˘ un a a a salt pe orizontal˘ ¸i un pas pe vertical˘. Exemple lacusta. a Vom nota prin A matricea dat˘ ¸i vom construi o matrice B ale c˘rei elemente as a bij vor contine sumele minime necesare pentru a ajunge ˆ celula (i. dar nu vom mai face ¸i pasul corespunz˘tor. Valorile celorlalte elemente ın a a b2i vor fi calculate pe baza formulei: b2.402 ˘ CAPITOLUL 16. j) pornind ¸ ın din celula (i − 1. Un salt ˆ as a ınseamn˘ c˘ putem trece de la o a a celul˘ la oricare alta aflat˘ pe aceea¸i linie.1 ¸ va fi ∞ deoarece ˆ aceast˘ celul˘ nu se poate ajunge. j). 1) → (1.out 28 Explicatie Drumul este: (1. a ¸ Datele de ie¸ire s Fi¸ierul de ie¸ire lacusta. 5) Timp maxim de executare: 1 secund˘/test a Indicatii de rezolvare * ¸ Ginfo nr. a a a Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ m. . iar un pas ˆ a a s ınseamn˘ c˘ putem trece a a de la o celul˘ la celula aflat˘ imediat sub ea. 3) → (2. Valoarea b2.i . PROGRAMARE DINAMICA O traversare const˘ din mai multe deplas˘ri.i + a2. 15/3 martie 2005 Pentru rezolvarea acestei probleme vom utiliza metoda program˘rii dinamice. 2) → (3. 3) → (4. n ≤ 100 • Valorile elementelor matricei sunt numere ˆ ıntregi din intervalul [1.

ci ı trebuie efectuat un salt orizontal. ˆ aceast˘ situatie vom alege al doilea minim de In a ¸ pe linia anterioar˘. b=new int[m][n].j. a ˆ final alegem minimul valorilor de pe ultima linie a matricei B (f˘r˘ a lua In aa ˆ considerare elementul de pe ultima coloan˘ a acestei linii) la care adaug˘m ın a a valoarea amn .out"))). valorile bij vor fi calculate pe baza formulei: bi. class Lacusta1 { static final int oo=100000.2.i++) for(j=0. static int m.n.j<n.j + min(bi−1.nval. // 0 <= i <= m-1.nextToken().i<m. t1=System. Evident.j++) { . PROBLEME REZOLVATE Pentru celelalte linii. st. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("lacusta. for(i=0.b.*. st. int i. relatia nu este valabil˘ pentru elementul de s ¸ a pe coloana k care corespunde minimului. m=(int)st. 403 unde k variaz˘ ˆ a ıntre 1 ¸i n.j = ai. 0 <= j <= n-1 public static void main(String[] args) throws IOException { long t1.io.nval.currentTimeMillis(). deoarece nu se poate coborˆ direct. n=(int)st.nextToken(). Coduri surs˘ * a Prima variant˘: a import java.j + ai−1. static int[][] a. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("lacusta. a=new int[m][n].t2.16.k ).in"))).

out.min=oo..j<n. }// minLinia(. static int m.close(). System.j++) if(j!=jj) if(b[ii][j]<min) min=b[ii][j].io.nextToken(). .j++) b[1][j]=a[0][0]+a[0][j]+a[1][j]. a[i][j]=(int)st.i<m. // prima linie (i=0) din b este oo // a doua linie (i=1) din b for(j=1. t2=System.j).j++) b[i][j]=oo. // urmatoarele linii din b for(i=2. si traseul ! class Lacusta2 { static final int oo=100000.i++) for(j=0.t2. // 1 <= i <= m.j<n.b. PROGRAMARE DINAMICA st. fara coborare b[m-1][n-1]=minLinia(m-1.out. out.println(b[m-1][n-1]). static int[][] a. 1 <= j <= n public static void main(String[] args) throws IOException { long t1. for(j=0.*.) }// class A doua variant˘: a import java. // "obligatoriu" (!) si ultima linie (i=n-1) dar .404 ˘ CAPITOLUL 16.. } for(i=0.n.n-1)+a[m-1][n-1].. int jj) // min pe linia=ii fara pozitia jj==col { int j.. }// main() static int minLinia(int ii..i++) for(j=0.j++) b[i][j]=a[i][j]+a[i-1][j]+minLinia(i-1.j<n.i<m..println("Timp = "+(t2-t1)). return min.currentTimeMillis().j<n.nval. // suplimentar .

// prima linie (i=1) din b este oo // a doua linie (i=2) din b for(j=2.nextToken().out"))).nval. out. n=(int)st. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("lacusta. a[i][j]=(int)st.j<=n.min.j0. jmin=-1.in"))).j++) if(j!=j0) if(b[i][j]<min) { min=b[i][j].nval.out.n)+a[m][n].. for(j=1.j++) { st.2. for(i=1. jmin=j. // urmatoarele linii din b for(i=3.nextToken().println(b[m][n]).nval. m-1 { min=oo.i<=m-1.j++) b[2][j]=a[1][1]+a[1][j]+a[2][j].j++) b[i][j]=a[i][j]+a[i-1][j]+minLinia(i-1. . 405 // "obligatoriu" (!) si ultima linie (i=n) dar .j<=n.j<=n.print((i-1)+" "+jmin+" --> ").j. st.i++) for(j=1.i++) for(j=1. // initializare aiurea ! j0=1.j<=n. int i.nextToken().} System. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("lacusta. // pentru linia 2 System. } for(i=1.jmin. fara coborare b[m][n]=minLinia(m. for(i=2..j<=n.j++) b[i][j]=oo. a=new int[m+1][n+1].i++) // liniile 2 .out.i<=m.print(1+" "+1+" --> ").close(). PROBLEME REZOLVATE t1=System.currentTimeMillis(). out.i<=m. b=new int[m+1][n+1].i++) for(j=1. st..j).16.i<=m. m=(int)st.

min=oo.n.print(i+" "+jmin+" --> "). static int m.println(m+" "+n).) }// class Varianta 3: import java.j++) if(j!=jj) if(b[ii][j]<min) min=b[ii][j].currentTimeMillis().j++) if(j!=j0) if(b[i][j]<min) { min=b[i][j]. // fara matricea de costuri (economie de "spatiu") class Lacusta3 // traseul este .j0. int minc.406 ˘ CAPITOLUL 16.j<=n. // 1 <= i <= m.. return min.println("Timp = "+(t2-t1)). static final int oo=100000. }// minLinia(. }// main() static int minLinia(int ii.io. j0=jmin.t2. jmin=j. min=oo. t1=System.. for(j=1..out.currentTimeMillis(). 1 <= j <= n public static void main(String[] args) throws IOException { long t1. System.j<n.} System. for(j=1. static int[][] a.. int jj) // min pe linia=ii fara pozitia jj==col { int j.out. . pentru depanare { // daca se cere .*.out. System..minsol.print(i+" "+jmin+" --> ").. } j0=n. System. int i.. PROGRAMARE DINAMICA System.jmin.out.print((i-1)+" "+jmin+" --> ")..out. se poate inregistra si apoi .j. t2=System.

PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("lacusta.j<=n. j0=jmin. for(j=1.j<=n.print(i+" "+jmin+" --> "). minsol=minc.j++) if(j!=j0) if(a[i-1][j]+a[i][j]<minc) {minc=a[i-1][j]+a[i][j].out.2.print(2+" "+jmin+" --> ").} System.j++) if(a[1][1]+a[1][j]+a[2][j]<minc) {minc=a[1][1]+a[1][j]+a[2][j].print(1+" "+jmin+" --> ").i++) { minc=oo. for(j=1. a=new int[m+1][n+1].16.out.print((i-1)+" "+jmin+" --> ").nval. PROBLEME REZOLVATE StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("lacusta.nextToken().out"))).j<=n.in"))).print(1+" "+1+" --> "). m=(int)st.nextToken(). // initializare aiurea ! for(i=3. } j0=n. jmin=j.i<=m-1.i<=m.j<=n.j++) { st. j0=jmin.out.i++) for(j=1.j++) . minc=oo.nval.out. n=(int)st.} System. for(i=1. st.nval. a[i][j]=(int)st. System. for(j=2. jmin=-1.nextToken().out. } minsol=oo. 407 // a doua linie (i=2) minc=oo. jmin=j. st. System. jmin=-1. System. minsol+=minc.

// 1 <= i <= m.nval. st.println(minsol).n.out. static int m. n=(int)st. // fara matricea de costuri (economie de "spatiu") class Lacusta4 // si .j<=n.*.. static int[][] a. t1=System. m=(int)st.i++) // citesc numai primele doua linii for(j=1. st. out.print(m+" "+jmin+" --> "). 1 <= j <= n public static void main(String[] args) throws IOException { long t1. jmin=j.nval.in"))).i<=2.println("Timp = "+(t2-t1)).ii.nextToken().t2.out. int minc. System.j. minsol+=minc+a[m][n]. System.currentTimeMillis().jj. }// main() }// class Varianta 4: import java..nextToken().j++) { . PROGRAMARE DINAMICA if(j!=j0) if(a[m-1][j]+a[m][j]<minc) {minc=a[m-1][j]+a[m][j].println(m+" "+n).jmin. a=new int[2][n+1]. fara matricea initiala (numai doua linii din ea !) { // calculez pe masura ce citesc cate o linie ! static final int oo=100000.close().out. for(i=1. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("lacusta.out.minsol.out"))).currentTimeMillis(). System.} System. out. int i. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("lacusta.io.print((m-1)+" "+jmin+" --> "). t2=System.j0.408 ˘ CAPITOLUL 16.

} System.nval.i++) // citesc mai departe cate o linie { for(j=1. minc=oo.j++) if(a[1%2][1]+a[1][j]+a[2%2][j]<minc) {minc=a[1%2][1]+a[1%2][j]+a[2%2][j].print(i+" "+jmin+" --> "). j0=jmin.print(i+" "+jmin+" --> "). jmin=-1. } j0=n. } //citesc linia m for(j=1. for(j=1. minsol+=minc. jmin=j.j<=n.out.nval.print(1+" "+jmin+" --> ").out. } minc=oo. } minsol=oo. a[i%2][j]=(int)st.i<=m-1.print((i-1)+" "+jmin+" --> "). jmin=j.j<=n. System.} System.} System.nextToken(). a[i%2][j]=(int)st.nextToken(). // initializare aiurea ! for(i=3. for(j=2. PROBLEME REZOLVATE st. minsol+=minc+a[m%2][n].print((i-1)+" "+jmin+" --> ").j++) { st.nextToken(). System.16. minsol=minc.out.j++) if(j!=j0) if(a[(m-1+2)%2][j]+a[m%2][j]<minc) {minc=a[(i-1+2)%2][j]+a[i%2][j].j++) if(j!=j0) if(a[(i-1+2)%2][j]+a[i%2][j]<minc) {minc=a[(i-1+2)%2][j]+a[i%2][j]. for(j=1.j<=n. 409 jmin=-1. // a doua linie (i=2) minc=oo.j<=n. a[m%2][j]=(int)st.out.out. System. System.print(1+" "+1+" --> "). j0=jmin. jmin=j.out.j++) { st.2.nval.j<=n. .print(2+" "+jmin+" --> ").out.

out.in"))).nextToken().currentTimeMillis().println(minsol).minsol.410 ˘ CAPITOLUL 16. out.nval. } System. t2=System.nextToken().nval.out. st. . a[j]=(int)st. m=(int)st.t2. int i. static int m.ii. a11=a[1]. jmin=-1.print(1+" "+1+" --> "). // numai o linie din matricea initiala ! class Lacusta5 { static final int oo=100000.out. }// main() }// class Varianta 5: import java. a[1]=(int)st.*. // citesc numai prima linie for(j=1. minc=oo. n=(int)st.nextToken().j.currentTimeMillis().nval. a=new int[n+1].out"))).j<=n. // citesc a doua linie st.println(m+" "+n).jmin.out.a11. t1=System. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("lacusta.io. // 1 <= j <= n public static void main(String[] args) throws IOException { long t1. System. st.j++) { st.println("Timp = "+(t2-t1)).close(). PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("lacusta.n.nextToken(). int minc.aa. PROGRAMARE DINAMICA System.j0.nval.jj. static int[] a.

print((m-1)+" "+jmin+" --> "+m+" "+jmin+" --> "). PROBLEME REZOLVATE for(j=2. st.nval.out. st. a[j]=(int)st. if(aa+a[j]<minc) {minc=aa+a[j].j<=n.nval. jmin=-1.j<=n.i<=m-1.2.} } System.} } System..j++) { aa=a[j]. for(j=1. for(j=1. // citesc ultimul element st. . jmin=-1. j0=jmin. j0=jmin. a[j]=(int)st. a[n]=(int)st. for(i=3. j0=jmin.nval.nextToken().out.nextToken().out. minsol+=minc.println(m+" "+n). st. jmin=j.print(1+" "+jmin+" --> "+2+" "+jmin+" --> ").nextToken().16.} } System.j<=n-1. minsol+=a[n].out. minsol=a11+minc. if(aa+a[j]<minc) {minc=aa+a[j]. } //citesc linia m (primele n-1 componente) minc=oo. jmin=j. a[j]=(int)st. if(j!=j0) if(aa+a[j]<minc) {minc=aa+a[j].nextToken().nval.i++) { minc=oo. minsol+=minc.j++) { aa=a[j].print((i-1)+" "+jmin+" --> "+i+" "+jmin+" --> ").. jmin=j.j++) { aa=a[j]. 411 // citesc mai departe cate o linie si . System.

ar putea fi ˆ artit˘ astfel: a at ımp˘ ¸ a • 4 (unit˘¸i primului mo¸tenitor) 3 (unit˘¸i celui de-al doilea).println("Timp = "+(t2-t1)). sau at s • 7 (unit˘¸i doar primului mo¸tenitor).out. 7. Fiind un tip original ¸i pasionat de matematic˘ a scris un testaa s a ment inedit. A hot˘rˆt ca banii s˘ fie distribuiti conform celei de-a N -a posibilit˘¸i din aa a ¸ at ordinea lexicografic˘. 6 1.println(minsol). sau at s • 5 (unit˘¸i primului mo¸tenitor) 2 (unit˘¸i celui de-al doilea).2. N date s˘ calculeze ¸i s˘ afi¸eze ¸ a s a s num˘rul total de posibilit˘¸i de ˆ artire a averii. s a at ımp˘ ¸ .412 out. a ımp˘ ¸ a Datele de intrare Fi¸ierul de intrare avere.close(). }// main() }// class 16. iar sumele pe care le acord˘ mo¸tenitorilor a a a s s˘ fie ˆ ordine strict descresc˘toare. Italag le-a scris ˆ ordine lexicografic˘. sau at s at • 4 (unit˘¸i primului mo¸tenitor) 2 (unit˘¸i celui de-al doilea) 1 (unitate celui at s at de-al treilea). System.18 Avere ONI2005 cls 10 Italag a fost toat˘ viata pasionat de speculatii bursiere reu¸ind s˘ adune o a ¸ ¸ s a avere considerabil˘. 5 2. a Cerint˘ ¸a Scrieti un program care pentru numerele S. Italag decide s˘-¸i ˆ a s ımpart˘ toat˘ averea. a ın a De exemplu dac˘ averea ar fi 7 unit˘¸i monetare.currentTimeMillis(). Testamentul contine dou˘ numere naturale: S reprezentˆnd averea ce ¸ a a trebuie ˆ artit˘ mo¸tenitorilor ¸i N reprezentˆnd alegerea sa pentru ˆ artirea ımp˘ ¸ a s s a ımp˘ ¸ averii. PROGRAMARE DINAMICA t2=System. precum ¸i modul ˆ care se face a at ımp˘ ¸ s ın aceast˘ ˆ artire conform cu a N -a posibilitate din ordinea lexicografic˘. ˘ CAPITOLUL 16. V˘zˆnd c˘ ˆ este foarte greu s˘ verifice dac˘ nu cumva a omis vreo variant˘ a a a ıi a a a de ˆ artire.out va contine dou˘ linii: s s ¸ a − pe prima linie va fi afi¸at num˘rul total de modalit˘¸i de ˆ artire a averii. out.in contine o singur˘ linie pe care se afl˘ dou˘ numere s ¸ a a a naturale separate printr-un singur spatiu: ¸ − primul num˘r (S) reprezint˘ suma total˘ a a a − cel de-al doilea (N ) reprezint˘ num˘rul de ordine al pozitiei c˘utate. 4 3. sau at s at • 6 (unit˘¸i primului mo¸tenitor) 1 (unitate celui de-al doilea). Pentru exemplul de mai sus: ımp˘ ¸ ın a 4 2 1. a a ¸ a Datele de ie¸ire s Fi¸ierul de ie¸ire avere.

. astfel ˆ at xi = yi . Spunem c˘ x preced˘ s as a a pe y din punct de vedere lexicografic. Universitatea Bucure¸ti s Problema se rezolv˘ prin programare dinamic˘ dup˘ valoarea maxim˘ pe care a a a a poate s˘ o ia primul num˘r ˆ cadrul descompunerii ¸i dup˘ suma total˘.in 12 5 avere.out 15 651 avere.16.. s Exemple: 4 2 1 preced˘ secventa 4 3 deoarece (4 = 4.out 962056220379782044 175 68 63 58 54 45 40 36 34 32 20 18 17 14 11 9 3 2 1 avere.out 5 43 avere. pentru a a ıncˆ orice i = 1. Elementele sale vor fi separate prin cˆte ¸ ın a a un spatiu. . x2 . y2 . a a ın s a a Obtinem recurenta: ¸ ¸ . a at a • Posibilit˘¸ile de ˆ artire a averii sunt numerotate ˆ at ımp˘ ¸ ıncepˆnd cu 1.. . yp ) dou˘ ¸iruri.. iar 6 1 preced˘ a ¸ a 7 deoarece 6 < 7.in 72 avere.in 700 912345678912345678 Timp maxim de executie/test: 1 secund˘ pentru Windows ¸i 0. ¸ Restrictii ¸i preciz˘ri ¸ s a • 1 < S < 701 • 0 < N < num˘rul total de posibilit˘¸i cu suma S a at • Se acord˘ punctaj partial pentru fiecare test: 5 puncte pentru determinarea a ¸ corect˘ a num˘rului de posibilit˘¸i de ˆ artire a lui S ¸i 5 puncte pentru detera a at ımp˘ ¸ s minarea corect˘ a posibilit˘¸ii N .. Emilian Miron. din ordinea lexicografic˘. dac˘ exist˘ k ≥ 1.2. Exemple: avere. k − 1 ¸i xk < yk . xm ) ¸i y = (y1 . a • Fie x = (x1 . PROBLEME REZOLVATE 413 − pe cea de-a doua linie va fi afi¸at˘ a N -a posibilitate de ˆ artire a lui S s a ımp˘ ¸ conform cerintei ˆ ordine lexicografic˘.1 secunde ¸ a s pentru Linux.... 2 < 3).descriere solutie * ¸ ¸ stud. Limita total˘ de memorie sub Linux este 3Mb din care 1Mb pentru a stiv˘.. a Indicatii de rezolvare .

nval. 2. static long c[][]. t1=System.. Reconstructia se face recalculˆnd valorile la fiecare pas pentru ¸ a S-ul curent.S c[v][s]=c[v-1][s]. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("avere. ¸ a a ¸ ¸ Observˆnd L = maximO(S 1/2 ) timpul total este O(S 3/2 )..t2. unde L este lungimea solutiei. // // // // // // c[0][0]=1. Astfel calcul˘m toate valorile folosind un singur vector ¸i p˘str˘m la a s a a fiecare pas c[i][S]. c=new long[S+1][S+1]. pentru v>=1 si s>=v test 4 ??? test 5 = gresit N (>.v-1 a ¸ +c[v − 1][s − v] //punnd pe prima pozitie v ¸ c[0][0] = 1. PROGRAMARE DINAMICA c[v − 1][s] //punˆnd pe prima pozitie 1. a¸a c˘ ea se poate a a ¸ a s a calcula folosind un singur vector.414 c[v][s] = ˘ CAPITOLUL 16. a O solutie backtracking obtine 20 puncte.. for(v=0.v<=S. static long n. iar una cu memorie O(N 2 ) 50 ¸ ¸ puncte.. c[0][s] = 0 pentru s > 0 Num˘rul total de posibilit˘¸i va fi egal cu c[S][S]. int s. Solutia descris˘ efectueaz˘ O(S 2 ∗ L) operatii.nextToken().v.*.nanoTime(). S=(int)st.2. ..nextToken().in")))... Codul surs˘ a import java.. Procesul continu˘ pentru S = S − i ¸i ıncˆ s a s N = N − c[i − 1][S] pˆn˘ cˆnd N = 0. st.) in fisier intrare toate celelalte: OK rezultat si timp !!! dar fara economie de memorie !!!!!!! public static void main(String[] args) throws IOException { long t1. n=(int)st. . a a a Observ˘m c˘ recurenta depinde doar de linia anterioar˘. pentru s=1.io. Aceasta ne ajut˘ pentru a respecta limita de a memorie. st. pentru v>=1 si s<v c[v][s]=c[v-1][s]+c[v-1][s-v]. class Avere1 { static int S. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("10-avere. c[0][s]=0..nval.v++) c[v][0]=(long)1. a at Reconstituirea solutiei se face stabilind primul num˘r ca fiind cel mai mic i ¸ a astfel ˆ at c[i][S] ≥ N ¸i c[i − 1][S] < N .out"))).

16. while((n>0)&&(S>0)) { v=0.println("Timp = "+((double)(t2-t1))/1000000000).v<=S. S=S-v.out.v++) for(s=0. n=n-c[v-1][S]..close(). out. System. PROBLEME REZOLVATE 415 for(v=1. t2=System.2. out.j<=S. else c[v][s]=c[v-1][s]+c[v-1][s-v].i++) { for(j=0.print(v+" "). while(c[v][S]<n) v++.println().. //afism().out. for(i=0.s++) if(s<v) c[v][s]=c[v-1][s].println(c[S][S]). System.j. } out.s<=S.. .j++) System.i<=S. } }// afism() }// class /* 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 2 1 1 1 0 0 0 0 0 0 1 1 1 2 2 2 2 2 1 1 1 0 0 1 1 1 2 2 3 3 3 3 3 3 2 2 1 1 1 2 2 3 4 4 4 5 5 5 5 1 1 1 2 2 3 4 5 5 6 7 7 8 1 1 1 2 2 3 4 5 6 7 8 9 10 1 1 1 2 2 3 4 5 6 8 9 10 12 1 1 1 2 2 3 4 5 6 8 10 11 13 1 1 1 2 2 3 4 5 6 8 10 12 14 1 1 1 2 2 3 4 5 6 8 10 12 15 Press any key to continue.print(c[i][j]+" "). }// main(.) static void afism() { int i.nanoTime().out..

pl˘tite cu 23. dac˘ are doar 5 zile de activitate. ¸ s a¸ Datele de intrare Fi¸ierul de intrare suma. 205. El poate uni cifrele sumelor stabilite ¸i le poate desp˘rti ta s a¸ apoi. PROGRAMARE DINAMICA 16. pentru fiecare zi de activitate ˆ slujba ¸ s ın sultanului.ONI2005 cls 10 Traditia este ca. fiecare sum˘ trebuie s˘ fie o valoare proprie (s˘ nu In ¸ a a a ˆ ınceap˘ cu 0). sn a ¸ reprezentˆnd sumele atribuite de sfatul ¸˘rii. a ta Datele de ie¸ire s Fi¸ierul de ie¸ire suma.out va contine o singur˘ linie pe care va fi afi¸at un s s ¸ a s singur num˘r natural reprezentˆnd prima total˘ maxim˘ care se poate obtine..in contine: s ¸ − pe prima linie un num˘r natural n reprezentˆnd num˘rul de zile de activa a a itate − pe linia urm˘toare. el poate opta pentru o nou˘ distributie a cifrelor ın a ¸ numerelor stabilite de marele sfat astfel: pentru prima zi cere 2 galbeni. pentru a treia 417. iar pentru ziua a cincea doar 2 galbeni. 5 ¸i respectiv 40 de a a s galbeni. a deoarece sfatul ¸˘rii a hot˘rˆt pentru ziua ˆ ai o sum˘ de 53 de galbeni. dup˘ dorint˘. a ¸ a a • Pentru 20% din teste. suma primit˘ pe fiecare zi s˘ nu dep˘¸easc˘ 999 de a ¸a ıncˆ a a as a galbeni ¸i s˘ primeasc˘ cel putin un galben pentru fiecare dintre zilele de activitate. s primind astfel 1167 de galbeni ˆ total.. ˆ total 680 de galbeni. Exemple . ın Cerint˘ ¸a Pentru num˘rul de zile n ¸i cele n sume stabilite de sfatul ¸˘rii pentru Jibal. a • Orice sum˘ dintr-o distributie trebuie s˘ fie nenul˘. . n numere naturale separate prin spatii s1 . a s ta scrieti un program care s˘ determine cea mai mare prim˘ total˘ care se poate ¸ a a a obtine prin unirea ¸i desp˘rtirea cifrelor sumelor date. s˘ modifice sumele stabilite de sfatul a s s a ¸˘rii. dar nu foarte mult. la ie¸irea la pensie. astfel ˆ at. pentru ziua a treia 12 galbeni. n ≤ 10. Astfel. a a a a ¸ Restrictii ¸i preciz˘ri ¸ s a • 1 < n < 501 • 0 < si < 1000. pentru orice 1 ≤ i ≤ n • ˆ orice distributie. a a a a ta vizirul Magir a primit pentru doar 5 zile de activitate prima total˘ de 411 galbeni. prime¸te dreptul ca.416 */ ˘ CAPITOLUL 16. la ie¸irea la pensie. pentru ta aa ıntˆ a ziua a doua 200 de galbeni. s2 . pentru a patra 205 ¸i pentru a cincea 540 de galbeni. celebru pentru contributia adus˘ la rezolvarea conflictului din ¸ a zon˘.19 Suma . pentru 50% din teste n ≤ 50. pentru a doua 3.2. pentru ziua a patra 144 de galbeni. Vizirul Jibal.. marele vizir s˘ primeasc˘ o prim˘ stabilit˘ de marele sfat al ¸˘rii. s a a ¸ Astfel. 417.

16.in 5 23 417 205 5 40 suma. s˘ mai existe ¸ansa construirii a s cu cifrele r˘mase a unei soluti cu n termeni. {nr..out 1608 Timp maxim de executie/test: 1 secund˘ pentru Windows ¸i 0. a ¸ Conditia nc-nr>=n-p^. begin . ¸ type pnod=^nod. nr este num˘rul de ordine al cifrei curente.2. de termeni si ultimul termen} next:pnod end. PROBLEME REZOLVATE suma.nc. procedure citire.u:pnod. Indicatii de rezolvare . p. n este num˘rul total a a de termeni ¸i p^. prin s a ¸ a crearea unui nou termen cu ajutorul cifrei curente. f:text.in 3 58 300 4 suma. var n.descriere solutie ¸ ¸ Solutia oficial˘ ¸ a Solutia I ¸ Solutia propus˘ utilizeaz˘ metoda program˘rii dinamice. fiecare cifr˘ contribuie la expandarea solutiilor precedente care au a ¸ ¸ans˘ de dezvoltare ulterioar˘. c:char.t-1)*3+2 (unde nc este num˘rul total de cifre care ¸ a se distribuie.1 secunde ¸ a s pentru Linux.i. Vectorul best memoreaz˘ la fiecare moment suma s a a a cea mai mare format˘ dintr-un num˘r dat de termeni.out 362 Explicatie ¸ Prima maxim˘ (362) se obtine a ¸ chiar pentru distributia 58 300 4 ¸ Explicatie ¸ Prima maxim˘ (1608) se obtine a ¸ pentru distributia 2 341 720 5 540 ¸ 417 suma.t este num˘rul de termeni ai solutiei curente) testeaz˘ ca.last:word. best:array[1. prin lipirea cifrei curente la ultimul ¸ a termen al solutiei curente.1000]of longint.x:longint. nod=record s:longint.k:longint. a a Astfel. {determina numarul total de cifre} var i.t testeaz˘ ca. s˘ mai existe ¸ansa construirii cu cifrele r˘mase a unei ¸ a s a solutii cu n termeni. a a Conditia nc-nr<=(n-p^. Este implementat ¸ a a a un algoritm de expandare tip Lee realizat cu o coad˘ alocat˘ dinamic. {suma} t.

u^.t-1)*3+2) and (best[p^.dispose(q) until gata.s.t:=p^. {recalcularea valorilor maxime memorate in vectorul best} procedure optim. if (p^.next.s+cif.418 ˘ CAPITOLUL 16. end. {expandarea corespunzatoare cifrei curente} procedure calc(nr:longint. q:=p.next). begin for i:=1 to n do best[i]:=0. repeat if (cif>0) and (nc-nr<=(n-p^.in’).u:=u^. var c.last<100)and(nc-nr>=n-p^.t.gata:=false. u^. u^. var i:longint.’suma. if p=c then gata:=true.gata:=false. begin c:=u.last:=p^.p:=p^.t] then best[q^. close(f) end. u^.t]:=q^.last*10+cif.last*9+cif.x:=x div 10 until x=0 end.s:=p^. u^.u:=u^. u^.s:=p^.q:pnod. for i:=1 to n do begin read(f. end. gata:boolean.t]=p^. q:pnod. gata:boolean.next.reset(f). PROGRAMARE DINAMICA assign(f. q:=p.next). if q=u then gata:=true. repeat if q^.cif:byte). .s>best[q^.n).next.s) then begin new(u^.last:=cif end.s+p^. readln(f.x).t+1.t:=p^.t) then begin new(u^. repeat inc(nc).

ord(c)-48). ignorand spatiile} reset(f).p^. end. ¸ s ¸ .out’). 419 Solutia II ¸ Problema se rezolv˘ prin metoda programare dinamic˘. optim end.c) until c<>’ ’.t:=1. calc(i. // punˆnd ultima sum˘ format˘ doar din cifra a[i] a a a smax[p-2][n-1]+a[p-1]*10+a[p].’suma.rewrite(f).best[n]).N ). s a Obtinem memorie O(N ) ¸i timp de executie O(L ∗ N ) = O(N 2 ).close(f) END. BEGIN citire. repeat read(f.2. p^.s.s:=ord(c)-48..s. p = 2 sau p = 3 ¸i cazurile ın a a s ˆ care a[p]. p^. Concaten˘m nua a a merele ¸i obtinem un ¸ir (s˘ ˆ not˘m a) de cifre (de lungime L maxim N ∗ 3). // punˆnd ultima sum˘ din 2 cifre a a smax[p-3][n-1]+a[p-2]*100+a[p-1]*10+a[p] // punˆnd ultima sum˘ din 3 cifre a a ) Trebuie avute ˆ vedere cazurile limit˘ cˆnd p = 1. a[p − 1] sau a[p − 2] sunt zero.last:=p^. best[1]:=p^. assign(f.writeln(f. close(f). {reluarea citirii cifrelor. Obtinem ¸ a¸ s a a ın ¸ relatia: ¸ smax[p][n] = min( smax[p-1][n-1]+a[p]..16. for i:=2 to nc do begin repeat read(f.c) until c<>’ ’.L) calcul˘m prima maxim˘ pe care o putem ¸ a a obtine desp˘rtind sub¸irul de pˆn˘ la p inclusiv ˆ n sume (n = 1. u:=p. s ¸ s a ıl a Pentru fiecare pozitie p (p = 1. a s a Pentru u¸urinta ˆ implementare stoc˘m smax[p][n] = −inf init pentru cazul s ¸ ın a ˆ care sub¸irul de pˆn˘ la p nu poate fi ˆ artit ˆ mod corect ˆ n sume. PROBLEME REZOLVATE q:=q^. new(p). nu p˘str˘m decˆt pe a a ¸ a a a acestea ¸i linia curent˘ pentru a nu avea probleme cu memoria. iar ın s a a ımp˘ ¸ ın ın observˆnd c˘ recurenta depinde doar de ultimele 3 linii. a¸a c˘ excludem termenii din expresia de minim. readln(f). moment ˆ care nu putem forma o sum˘ ın ın a de lungimea respectiv˘.next until gata.

PROGRAMARE DINAMICA import java.close(). n=(int)st.i<=n. nc+=3. j=0. // xj are 1: cifra c[i] // s[i-2][j-1]+c[i-1]*10+c[i]. nc+=2.x.out"))). }// main(.nval. out.nextToken().} else if(x<100) {c[++j]=x/10.io.out.println("Eroare date !").in"))). // nc=nr cifre din sir static int[] c=new int[1501]. out.nextToken().} else if(x<1000) {c[++j]=x/100. // xj are 2 cifre: c[i-1]c[i] // s[i-3][j-1]+c[i-2]*100+c[i-1]*10+c[i]). // fara economie de spatiu class Suma1 { static int n. } s=new int[nc+1][n+1]. // sirul cifrelor static int[][] s. c[++j]=x%10.j.nval. c[++j]=x%10. nc=0. public static void main (String[] args) throws IOException { int i. st. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("suma..} else System. c[++j]=(x/10)%10.nc.print(s[nc][n]).*. PrintWriter out=new PrintWriter(new BufferedWriter( new FileWriter("suma. calcul(). x=(int)st. nc+=1. if(x<10) {c[++j]=x.420 Codul surs˘ a Varianta 1: ˘ CAPITOLUL 16.. for(i=1. afism(). // xj are 3 cifre .i++) { st.) static void calcul() { // s[i][j]=max(s[i-1][j-1]+c[i].

16. int b) { if(a>b) return a. }// for }// calcul() static int max(int a. if((c[2]!=0)&&(c[3]!=0)) s[3][3]=c[1]+c[2]+c[3]. else // c[2]!=0 && c[3]!=0 s[3][2]=max(c[1]+c[2]*10+c[3]. } .j++) { smax=0. s[1][1]=c[1].s[i-1][j-1]+c[i]). else return b. if(c[2]!=0) s[2][2]=c[1]+c[2]. if((c[i-1]!=0)&&(s[i-2][j-1]!=0)) smax=max(smax. 421 // i = pozitie cifra in sirul cifrelor // j = pozitie numar in sirul final al numerelor if(j<=i) { if((c[i]!=0)&&(s[i-1][j-1]!=0)) smax=max(smax.i<=nc.j<=n.s[i-3][j-1]+c[i-2]*100+c[i-1]*10+c[i]). PROBLEME REZOLVATE int i.j. } else // c[3]!=0 { if(c[2]==0) s[3][2]=c[1]*10+c[3]. } for(i=4.smax. } s[i][j]=smax. if(c[3]==0) { if(c[2]!=0) s[3][2]=c[1]+c[2]. s[3][1]=c[1]*100+c[2]*10+c[3].i++) for(j=1. if((c[i-2]!=0)&&(s[i-3][j-1]!=0)) smax=max(smax.s[i-2][j-1]+c[i-1]*10+c[i]).c[1]*10+c[2]+c[3]). s[2][1]=c[1]*10+c[2].2.

nval.print(j+"\t"). // cu economie de spatiu !!! class Suma2 { static int n. n=(int)st.i++) { st.out. for(i=1. nc+=1.println().println(). x=(int)st.} else . System. for(j=1. // sirul cifrelor static int[][] s.print(s[i][j]+"\t").out.io.nval. for(i=1. PrintWriter out=new PrintWriter(new BufferedWriter( new FileWriter("suma.j.out.i++) { System.nextToken(). System. if(x<10) {c[++j]=x.j.i<=nc.*.out.print(i+" "+c[i]+" :\t").j<=n.t2.print(" \t").x. nc=0.nc. public static void main (String[] args) throws IOException { long t1.i<=n. for(j=1.out. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("suma.j++) System.out.nextToken(). ˘ CAPITOLUL 16. st.in"))). t1=System.j++) System.out"))). int i. } }// afism() }// class Varianta 2: import java. PROGRAMARE DINAMICA System.j<=n.currentTimeMillis(). // nc=nr cifre din sir static int[] c=new int[1501].422 static void afism() { int i. j=0.

) static void calcul() { // s[i][j]=max(s[i-1][j-1]+c[i]. s[2][1]=c[1]*10+c[2]. System. if((c[2]!=0)&&(c[3]!=0)) s[3][3]=c[1]+c[2]+c[3].} else System.. c[++j]=(x/10)%10. }// main(. // xj are 2 cifre: c[i-1]c[i] // s[i-3][j-1]+c[i-2]*100+c[i-1]*10+c[i]).smax. c[++j]=x%10.println("Eroare date !").out.i<=nc.j++) // j = pozitie numar in sirul final al numerelor { smax=0. } s=new int[4][n+1].c[1]*10+c[2]+c[3]). c[++j]=x%10.} else if(x<1000) {c[++j]=x/100. if(c[3]==0) { if(c[2]!=0) s[3][2]=c[1]+c[2]. s[1][1]=c[1].i++) // i = pozitie cifra in sirul cifrelor for(j=1. s[3][1]=c[1]*100+c[2]*10+c[3].out. // cu economie de spatiu !!! calcul(). nc+=2. t2=System. // xj are 3 cifre int i.println("Timp = "+(t2-t1)). if(c[2]!=0) s[2][2]=c[1]+c[2].print(s[nc%4][n]).. } for(i=4.16.currentTimeMillis().j. nc+=3.close(). out. } else // c[3]!=0 { if(c[2]==0) s[3][2]=c[1]*10+c[3]. out. PROBLEME REZOLVATE 423 if(x<100) {c[++j]=x/10. else // c[2]!=0 && c[3]!=0 s[3][2]=max(c[1]+c[2]*10+c[3].// xj are 1: cifra c[i] // s[i-2][j-1]+c[i-1]*10+c[i]. if(j<=i) .2.j<=n.

io.out"))). if((c[i-2]!=0)&&(s[(i-3+4)%4][j-1]!=0)) smax=max(smax. for(i=1. n=(int)st. PROGRAMARE DINAMICA if((c[i]!=0)&&(s[(i-1+4)%4][j-1]!=0)) smax=max(smax.s[(i-3+4)%4][j-1]+c[i-2]*100+c[i-1]*10+c[i]). // sirul cifrelor static int[][] s. } }// calcul() static int max(int a. int b) { if(a>b) return a.i<=n. if((c[i-1]!=0)&&(s[(i-2+4)%4][j-1]!=0)) smax=max(smax.i++) .*. // o solutie public static void main (String[] args) throws IOException { int i.nextToken(). else return b. st.nval.s[(i-2+4)%4][j-1]+c[i-1]*10+c[i]).x. PrintWriter out=new PrintWriter(new BufferedWriter( new FileWriter("suma. // nc=nr cifre din sir static int[] c=new int[1501]. } }// class Varianta 3: import java.s[(i-1+4)%4][j-1]+c[i]). static int[][] p. } s[i%4][j]=smax. // fara economie de spatiu dar cu afisare o solutie ! class Suma3 { static int n.nc. j=0.in"))).424 { ˘ CAPITOLUL 16. nc=0.j. // predecesori static int[] sol. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("suma.

System. afism(s).println(). sol=new int[n+1]. c[++j]=(x/10)%10. x=(int)st. }// main(. System.} else System. // xj are 3 cifre int i. for(i=i1.out. p=new int[nc+1][n+1].} else if(x<100) {c[++j]=x/10.out.i<=i2.i1.out. nc+=2.println("Eroare date !").out.k--) { i1=p[i2][k]. System. out. nc+=1.println(sol[k]). i2=nc.i++) sol[k]=sol[k]*10+c[i]..out.k>=1. i2=i1-1.16.nextToken().println().} else if(x<1000) {c[++j]=x/100. if(x<10) {c[++j]=x.close(). nc+=3.. calcul(). solutia(). PROBLEME REZOLVATE { 425 st.// xj are 1: cifra c[i] // s[i-2][j-1]+c[i-1]*10+c[i].smax. } }// solutia() static void calcul() { // s[i][j]=max(s[i-1][j-1]+c[i]. System.nval.i2.) static void solutia() { int i. afisv(sol).print(k+" : "+i1+"->"+i2+" ==> ").print(s[nc][n]). for(k=n.j. c[++j]=x%10. afism(p). // xj are 2 cifre: c[i-1]c[i] // s[i-3][j-1]+c[i-2]*100+c[i-1]*10+c[i]). } s=new int[nc+1][n+1]. out.k. .2. c[++j]=x%10.

else p[3][2]=3.j++) // j = pozitie numar in sirul final al numerelor { smax=0. if(smax==s[i-3][j-1]+c[i-2]*100+c[i-1]*10+c[i]) p[i][j]=i-2.j<=n. s[2][1]=c[1]*10+c[2]. } else // c[3]!=0 { if(c[2]==0) { s[3][2]=c[1]*10+0+c[3]. if(s[3][2]==c[1]+c[2]*10+c[3]) p[3][2]=2.c[1]*10+c[2]+c[3]). } if((c[i-1]!=0)&&(s[i-2][j-1]!=0)) { smax=max(smax.s[i-3][j-1]+c[i-2]*100+c[i-1]*10+c[i]). } } for(i=4. s[3][1]=c[1]*100+c[2]*10+c[3]. if(smax==s[i-2][j-1]+c[i-1]*10+c[i]) p[i][j]=i-1. } if((c[i-2]!=0)&&(s[i-3][j-1]!=0)) { smax=max(smax. if(c[3]==0) { if(c[2]!=0) s[3][2]=c[1]+c[2].426 ˘ CAPITOLUL 16. } .s[i-1][j-1]+c[i]). if((c[2]!=0)&&(c[3]!=0)) s[3][3]=c[1]+c[2]+c[3]. PROGRAMARE DINAMICA s[1][1]=c[1].i<=nc. p[3][2]=3.i++) // i = pozitie cifra in sirul cifrelor for(j=1. if(c[2]!=0) s[2][2]=c[1]+c[2].} else // c[2]!=0 && c[3]!=0 { s[3][2]=max(c[1]+c[2]*10+c[3]. if(j<=i) { if((c[i]!=0)&&(s[i-1][j-1]!=0)) { smax=max(smax. if(smax==s[i-1][j-1]+c[i]) p[i][j]=i.s[i-2][j-1]+c[i-1]*10+c[i]). p[1][1]=p[2][1]=p[3][1]=1.

out. } static void afism(int[][] x) { int i. for(j=1.out.out. for(i=1.println(). } }// afism() static void afisv(int[] sol) { int i. int b) { if(a>b) return a.out. for(j=1.println().i++) { System.2.print(i+" "+c[i]+" :\t").out. } System. else return b.j.out.16.out. System.i<=nc.out.sum=0.i<=n. for(i=1. System. System.out.print(j+"\t").print(x[i][j]+"\t").println().j<=n.j++) System. PROBLEME REZOLVATE }// if s[i][j]=smax. }// afisv() }// class 427 .print(" \t"). System.print(sol[i]+" "). sum+=sol[i].j++) System.println(" ==> "+sum). }// for }// calcul() static int max(int a.j<=n.i++) { System.

428 ˘ CAPITOLUL 16. PROGRAMARE DINAMICA .

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

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

2 Un algoritm eficient . ¸ Dorim s˘ avans˘m cu mai mult de un pas la o nou˘ ˆ a a a ıncercare.KMP 431 17. UN ALGORITM EFICIENT .2.KMP Algoritmul KMP (Knuth-Morris-Pratt) determin˘ potrivirea ¸irurilor folosind a s informatii referitoare la potrivirea sub¸irului cu diferite deplasamente ale sale. f˘r˘ s˘ risc˘m aa a a s˘ pierdem vreo solutie! a ¸ .17. ¸ s Pentru t: 1 a ¸i s 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˘ dou˘ potriviri: a a 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 Sirul ineficient de ˆ ¸ ıncerc˘ri este: a 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 evidentiat˘ prin caracter boldat ¸ a iar solutiile sunt marcate cu *.

. 1.... ... R˘mˆne s˘ verific˘m apoi dac˘ t[i] = p[k + 1]. s Cum determin˘m practic valorile functiei next? a ¸ . ...k] este sufix pentru p[1. Cu alte cuvinte. tj ) (cuprins˘ ˆ a ¸ a ıntre pozitiile i ¸i j) din ¸irul t = (t1 .432 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 17. .. a a a a a Observ˘m c˘ noul deplasament depinde numai de ¸ablonul p ¸i nu are nici o a a s s leg˘tur˘ cu textul t.... dorim s˘ determin˘m cel mai ıncˆ a a lung sufix al secventei p[1... . POTRIVIREA SIRURILOR ¸ 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˘m prin t[i.1: Deplasare optim˘ a Folosind Figura 17.j] secventa de elemente consecutive (ti .i − 1] = p[1.2. s Care este cel mai bun deplasament d′ pe care trebuie s˘-l ˆ a ıncercam? d p: 1 ... m . j j+1 .. .. Este astfel realizat˘ ¸i potrivirea textului t cu ¸ablonul p. m i-j . s a ¸ s ¸ s deci t[i − j.. dorim s˘ determin˘m cel mai mare indice k < j astfel a a ˆ at p[1... m − 1} este definit˘ astfel: a next(j) = max{k/k < j ¸i p[1. m} → {0.. a a Algoritmul KMP utilizeaz˘ pentru determinarea celor mai lungi sufixe o a functie numit˘ next.. ¸ a Dat fiind ¸irul de caractere p[1.... 2.. . 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 . t2 . a s s t[i − k... n p: ...k] = p[j + 1 − k..m]........j]}..j] iar noul deplasament d′ trebuie ales astfel ˆ at s˘ ¸ ıncˆ a realizeze acest lucru.. j+1-k i-k 1 .i − 1] = p[1..k].. .. ¸ s s S˘ presupunem c˘ suntem la un pas al verific˘rii potrivirii cu un deplasament a a a d ¸i prima nepotrivire a ap˘rut pe pozitia i din text ¸i pozitia j + 1 din ¸ablon.j] ¸i t[i] = p[j + 1]. tn ). Figura 17.j]. ... functia s ¸ next : {1.. i-1 k i k+1 .

dac˘ p[j + 1] = p[k ′ + 1] atunci next(j + 1) = k ′ + 1. a s Dac˘ p[j+1] = p[k+1] (folosind notatiile din figur˘) atunci next[j+1] = k+1. a Presupunem c˘ au fost determinate valorile next[1]. ... .17. . j+1-k . next[2]. . Evident. .... p: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx j+1-k' . k+1 j+1 k'+1 . . atunci vom avea next(j + 1) = 0.....m]=pattern static int[] next.. acest algoritm se termin˘ ˆ a ıntr-un num˘r finit de pa¸i pentru c˘ a s a j > k > k′ > . ....2 pentru a urm˘ri mai u¸or rationamentul! ˆ aceast˘ a a s ¸ In a figur˘ next[j] = k ¸i next[k] = k ′ ..io. // nr aparitii static char[] t. ≥ 0.. .. j 1 k' Figura 17.. . UN ALGORITM EFICIENT ...k].. a Dac˘ nici acum nu avem egalitate de caractere.. next[j].j]! Fie acesta p[1. a Cum determin˘m next[j + 1]? a xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 433 1 k+1-k' k p: p: 1 ..... . Dac˘ ajungem la 0.n]=text. . a ¸ a Obtinem: ¸ dac˘ p[j + 1] = p[next(j) + 1] atunci next[j + 1] = next(j) + 1.p...2. ... .. // t[1... .. a Obtinem: ¸ dac˘ p[j + 1] = p[next(next(j)) + 1] atunci next[j + 1] = next(next(j)) + 1.*.. a Ordinul de complexitate al algoritmului KMP este O(n + m).. p[1. Dar acest sufix a a mai mic este cel mai lung sufix pentru p[1.. import java. class KMP { static int na=0.2: Functia next ¸ Ne ajut˘m de Figura 17.. Astfel.KMP Initializ˘m next[1] = 0.. vom continua acela¸i rationaa s ¸ ment pˆn˘ cand g˘sim o egalitate de caractere sau lungimea prefixului c˘utat a a a a este 0. a Ce se ˆ ampl˘ dac˘ p[j + 1] = p[k + 1]? ıntˆ a a C˘ut˘m un sufix mai mic pentru p[1.. cu alte cuvinte k ′ = next(k) = next(next(j))....k ′ ].

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

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

. . sau num˘rul −1 dac˘ nu avem o permutare circular˘. ˆ realitate nu e nevoie de concatenarea efectiv˘ a s ın s In a ¸irului. formate numai a as din litere mari ale alfabetului latin.in 10 ABCBAABBAB BABABCBAAB circular. adic˘ indicele k. a ın Folosim algoritmului KMP de c˘utare a unui sub¸ir. y2 . s Date de intrare Pe prima linie a fi¸ierului de intrare circular...Balcescu” .io.. POTRIVIREA SIRURILOR ¸ 17. a s Concaten˘m primul ¸ir cu el ˆ si ¸i c˘ut˘m prima aparitie a celui de-al a s ınsu¸ s a a ¸ doilea ¸ir ˆ ¸irul nou format. import java. // pozitia de potrivire .out se va scrie cel mai mic num˘r natural p s a pentru care ¸irul de pe linia a treia este o permutare circular˘ cu p pozitii a ¸irului s a ¸ s de pe linia a doua. Colegiul National ”N. Pe liniile urm˘toare sunt dou˘ ¸iruri de caractere de lungime n.*.Braila Se spune c˘ ¸irul y1 ..1 secunde ¸ Rezolvare (indicatia autorului): O variant˘ cu dou˘ ”for”-uri e foarte u¸or ¸ a a s de scris. Date de ie¸ire s Pe prima linie a fi¸ierului circular.3..in este scris numarul natural s n. . yn este o permutare circular˘ cu p pozitii a ¸irului as a ¸ s x1 . Cerint˘ ¸a Pentru dou˘ ¸iruri date determinati dac˘ al doilea este o permutare circular˘ as ¸ a a a primului ¸ir.Campion 2003-2004 Runda 6 Autor: prof. dar nu se ˆ ıncadreaz˘ ˆ timp pentru n mare. class Circular { static int n..3 17. a a a Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ n ≤ 20000 Exemple circular. unde indicii mai mari a ca n se consider˘ modulo n... y2 = xp + 2. Mot Nistor.d=-1. xn dac˘ y1 = xp + 1. cu k > n se refer˘ la elementul de a a a indice k − n.out 7 Timp maxim de executie/test: 0.1 Probleme rezolvate Circular .436 CAPITOLUL 17. . yn = xp + n. doar ¸inem cont c˘ indicii care se refer˘ la ¸irul ”mai lung” trebuie luati s t a a s ¸ modulo n. x2 .

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

. f˘cˆnd o permutare circular˘ cu d pozitii rezultˆnd ¸irul s a a a ¸ a s p(cn−d+1 ).y).close(). PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("circular.ONI2006 baraj Copiii solarieni se joac˘ adesea trimitˆndu-¸i mesaje codificate.println(d).out"))).2 Cifru .. a¸a c˘ noi le vom ¸ s a reprezenta prin numere de la 1 la m.438 CAPITOLUL 17.p(cn ) s a s ¸ se rote¸te spre dreapta. codificarea mesajului se realizeaz˘ s a astfel: se ˆ ınlocuie¸te fiecare liter˘ ci cu p(ci ). Dat fiind un mesaj ˆ limbaj solarian. POTRIVIREA SIRURILOR ¸ while((i<=2*n)&&(d==-1)) // t[1. s a ¸ ¸ Cerint˘: ¸a . pentru mesajul 213321.. p(3) = 2) ¸i d = 2.. reprezentat de noi ca o succesiune ın de n numere cuprinse ˆ ıntre 1 ¸i m. c1 c2 . a p(2) = 1.. De exemplu. apoi ¸irul obtinut p(c1 )p(c2 ). }//main() }//class /* circular. }// while }// kmp public static void main(String[] args) throws IOException { readData().in circular. apoi s a ¸ s rotind spre dreapta ¸irul cu dou˘ pozitii obtinem codificarea 131322.p(cn−d ). out. if(p[j+1]==t[(i>n)?(i-n):i]) j++.out ---------------------20 5 12312123412123412341 12341212341234112312 */ 17. a Alfabetul solarian contine m litere foarte complicate.. break.n] { while(j>0&&p[j+1]!=t[(i>n)?(i-n):i]) j=next[j]. Aplicˆnd permutarea p vom obtine ¸irul 132213.. } i++..3.p(cn−1 )p(cn )p(c1 )p(c2 ).cn . permutarea p = (312) (adic˘ p(1) = 3. kmp(x. out. Pentru codia ¸a s ficare ei folosesc un cifru bazat pe o permutare p a literelor alfabetului solarian ¸i s un num˘r natural d.. if(j==m) { d=i-n.

. ¸ a ¸ a Pentru teste cu m ≤ 5 se acord˘ 40 de puncte din care 20 pentru teste ¸i cu a s n ≤ 2000. s a ¸ Pe cea de a treia linie este scris mesajul codificat ca o succesiune de n numere cuprinse ˆ ıntre 1 ¸i m separate prin cˆte un spatiu.. .. s a Date de intrare: Fi¸ierul de intrare cifru. Mai exact se vor scrie valorile a a p(1).. deci pen¸a ¸ s a s tru prima aparitie a simbolului se ia distanta fat˘ de ultima aparitie a aceluia¸i ¸ ¸ ¸a ¸ s simbol). s s ¸ a reprezentˆnd num˘rul de pozitii cu care s-a realizat permutarea circular˘ spre a a ¸ a dreapta.. 1.. dac˘ concaten˘m primul ¸ir cu el a a a s ˆ si rezultˆnd o complexitate O(n)..out va contine pe prima linie num˘rul natural d. .17. 2. .in contine pe prima linie numele naturale n ¸i m. p(2). s ¸ s separate prin spatiu.3. a ¸ Restrictii: ¸ n ≤ 100000 m ≤ 9999 Mesajul contine fiecare num˘r natural din intervalul [1. iar pentru n mic se a poate c˘uta permutarea pentru fiecare d = 0. ınsu¸ a Pentru m mic se pot genera toate permut˘rile multimii {1. determinati cifrul folosit s ¸ (permutarea p ¸i num˘rul d). Dac˘ pentru d exist˘ mai multe posibilit˘¸ii se va alege valoarea minim˘. PROBLEME REZOLVATE 439 Date fiind un mesaj necodificat ¸i codificarea sa. n.in cifru. s a ¸ Date de ie¸ire: s Fi¸ierul de ie¸ire cifru.io. Pe cea de a doua linie este scris mesajul necodificat ca o succesiune de n numere cuprinse ˆ ıntre 1 ¸i m separate prin cˆte un spatiu.out 63 2 213321 312 131322 Timp maxim de executie/test: 0. a a at a Pe urm˘toarea linie este descris˘ permutarea p. a Codul surs˘ a import java. F˘cˆnd aceast˘ recodificare pentru cele dou˘ ¸iruri reducem problema la dea a a as terminarea permut˘rii circulare care duce primul ¸ir ˆ al doilea. p(m) separate prin cˆte un spatiu. m] cel putin o dat˘..2 secunde ¸ Indicatii de rezolvare: Solutia comisiei ¸ Fiecare aparitie a unui simbol din alfabet ˆ ¸ ıntr-un ¸ir se ˆ s ınlocuie¸te cu distanta s ¸ fat˘ de precedenta aparitie a aceluia¸i simbol (considerˆnd ¸irul circular..*. m} f˘cˆnd a ¸ a a pentru fiecare permutare o c˘utare (cu KMP de exemplu). Exemplu: cifru. reprezentˆnd lungimea mesajului ¸i respectiv num˘rul de ¸ a s a litere din alfabetul solarian. . care poate fi a s ın rezolvat˘ cu un algoritm de pattern matching.

1.d1).... t1=new int[n+1]. static int[] perm.. m=9. // sablon in KMP .// permutarea static int n. distanta(t1. (d1) static int[] p. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("cifru.nval.999 .i<=n. n=100.println().. } for(i=1.deplasarea=-1.. afisv(d0. //afisv(t1. int i. d0=new int[n+1]. d0 dublat .out. de eliberat ! static int[] d0.. } distanta(t0. p=new int[n+1]..m..n static int[] ua. //afisv(t0..j.i<=n.. // text mesaj codificat --> spatiu .d0)...nextToken(). ua=new int[m+1].n). // distante .1. for(i=1. afisv(d1..nextToken().. // text in KMP . d1=new int[n+1]. System...nextToken()..1.. ==> d[] mai rapid . n=(int)st.in"))).nval. // pozitia ultimei aparitii .nval.m . POTRIVIREA SIRURILOR ¸ class kmp { static int[] t0. maxim !!! ==> 200K public static void main(String[] args) throws IOException { StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("9-cifru.440 CAPITOLUL 17.2.d0) .i++) { st.nval..out. // . ... // text mesaj necodificat --> spatiu ... mesaj codificat static int[] t. System... 1. st. t0[i]=(int)st. // prefix in KMP . // distante .k..j0. mesaj necodificat static int[] d1... m=(int)st.out"))).i++) { st.n)..n).j1. de eliberat ! static int[] t1.000. t0=new int[n+1]... t1[i]=(int)st..1. (d0..2.1. st.println().n).nextToken().. spatiu !!! static int[] s.

p. PROBLEME REZOLVATE s=d0. . j1=0.. j0=n-deplasarea+i.pozi=-1. out.. out. for(i=1.n)-1.. //System.i.17.. int n.tn si s1. int[] s.m).sm { int k. } //afisv(perm. System.. for(i=1.i<=m. aici standard dar .i++) out..i++) t[i]=t[n+i]=d1[i]. } if(k==m) break.i++) perm[i]=0.i<=n.3. if(perm[t0[j0]]==0) { perm[t0[j0]]=t1[j1]. int m)// t1.1. if(j0>n) j0=j0-n. // d1 dublat si caut d0 .out.out.n)...println(deplasarea).print(perm[i]+" "). afisv(p..out. k++..i++) { while(k>0&&s[k+1]!=t[i]) k=p[k].. k=0. // economie de spatiu .1.close().. for(i=1.n). prefix(s. //afisv(t. perm=ua.i<=n.println(deplasarea).i++) { j1++. //afisv(s.2*n). }// main static int kmp(int[] t. for (i=1... // ocupa spatiu prea mult.i<=m. // permutarea .println().. System.i<=n. 441 t=new int[2*n+1]. // nr elemente plasate deja in permutare ..2*n.1. if (s[k+1]==t[i]) k++. for(i=1.println(i+" : "+j0+" "+j1).n).s.1. deplasarea=kmp(t... k=0.

.i++) { if(ua[t[i]]!=0) // stiu pozitia spre stanga a lui t[i] . d=distante .i++) ua[i]=0... POTRIVIREA SIRURILOR ¸ if(k==m) { pozi=i-m+1. // numai prima aparitie . { if(ua[t[i]]<i) d[i]=i-ua[t[i]]..i<=n. // caut in zona n. // e mai la dreapta .int[] d) // t=text.) static void prefix(int[] s.. // distanta spre stanga .n-1.n-2. else d[i]=i-ua[t[i]]+n. } // nu a aparut inca in 1.. continue... // noua pozitie a lui t[i] . { int i.. m=dimensiune { int i.. while(t[i]!=t[j]) { k++..k. // e mai la stanga . pana la n inclusiv . }// kmp(.. }// for i }// distanta(.int m) // s=sablon.out.k.442 CAPITOLUL 17. } d[i]=k..j.) static void distanta(int[] t..i<=m. ua[t[i]]=i.. for(i=1.i-1 ==> de la n spre stanga k=i.int[] p. for(i=1...println("incepe pe pozitia "+pozi). j--. p=prefix... j=n.. break... !!! } }// for return pozi... //System. ua[t[i]]=i. .

i<=m. } }// prefix() static void afisv(int[] x..) }// class 443 ..i<=i2.println().17.print(x[i]+" ").out. for(i=i1.3.out.i++) { k=p[i-1]. if(s[k+1]==s[i]) p[i]=k+1.i++) System. int i2) { int i. }// afisv(. System. else p[i]=0. int i1. PROBLEME REZOLVATE p[1]=0. for(i=2. while(k>0&&s[k+1]!=s[i]) k=p[k].

444 CAPITOLUL 17. POTRIVIREA SIRURILOR ¸ .

P2 (x2 . cazul b) ˆ figur˘ a ın a Orientarea depinde de valoarea expresiei o(P1 (x1 . y3 ). cazul c) ˆ figur˘ ın ın a • vˆrfuri coliniare: m12 = m23 . y2 ). P3 (x3 . P2 (x2 . y1 ). cazul a) ˆ figur˘ ın a ın a • ˆ sensul acelor de ceas (spre dreapta): m12 > m23 . a ın s 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 ¸i P2 P3 (ˆ aceast˘ ordine): s ın a • ˆ sens trigonometric (spre stˆnga): m12 < m23 . y2 ) ¸i P3 (x3 .1 Determinarea orient˘rii a Consider˘m trei puncte ˆ plan P1 (x1 . y1 ). y3 )) = (y2 − y1 ) · (x3 − x2 ) − (y3 − y2 ) · (x2 − x1 ) 445 .Capitolul 18 Geometrie computational˘ ¸ a 18.

. P3 P4 ).... P2 (x2 . + xn−1 yn + xn y1 − y1 x2 + y2 x3 + .. Pn−1 Pn ) ¸i (Pn−1 Pn .Pn (xn yn )..Pn → P1 ın a a este ˆ sensul acelor de ceasornic ¸i este nul˘ dac˘ punctele P1 (x1 . y2 ).2  < 0 ⇒ sens trigonometric  o(P1 (x1 .3 Aria poligoanelor convexe Aria poligonului convex cu n vˆrfuri P1 (x1 .. Pn (xn yn ) sunt colineare. y3 )) = 0 ⇒ coliniare   > 0 ⇒ sensul acelor de ceas Testarea convexit˘¸ii poligoanelor at Consider˘m un poligon cu n vˆrfuri P1 (x1 .. P2 (x2 ..Pn (xn yn ).. y1 ). y2 ).4 Pozitia unui punct fat˘ de un poligon convex ¸ ¸a . ın s a a . y1 )P2 (x2 ... P3 (x3 . Reciproca acestei afirmatii este deasemenea adev˘rat˘ ¸ a a ˆ cazul poligoanelor convexe. y1 ). P2 P3 ). (P2 P3 . y2 ).. GEOMETRIE COMPUTATIONALA ¸ 18. y2 )... ın 18.446 astfel ˘ CAPITOLUL 18. este negativ˘ dac˘ orientarea P1 → P2 → .. Pn P1 ) s au aceea¸i orientare sau sunt colineare. + yn−1 xn + yn x1 | 2 Expresia de sub modul este pozitiv˘ dac˘ orientarea P1 → P2 → . .Pn → P1 a a este ˆ sens trigonometric. a a Poligonul este convex dac˘ ¸i numai dac˘ perechile de segmente as a (P1 P2 . (Pn−2 Pn−1 .. y1 )P2 (x2 .. s P7 P6 P5 P1 P2 P3 P4 P1 P7 P6 P5 P2 P3 P4 a) b) 18. n ≥ 3 a se poate determina cu ajutorul urm˘toarei formule: a 1 |x1 y2 + x2 y3 + . n ≥ 3.

. y0 ) este ˆ interiorul a a ın sau pe frontiera poligonului convex P1 (x1 . y0 ) este s a a a ˆ interiorul poligonului.Pn ) = k=1 arietriunghi (P0 Pk Pk+1 ) unde punctul P (xn+1 . Functia fi (x.. y) are valori ın a ¸ de acela¸i semn pentru toate punctele din acela¸i semiplan. y0 ) se afl˘ ˆ interiorul poligonului (acesta a a ın fiind convex) trebuie s˘ verific˘m dac˘ toate vˆrfurile poligonului ˆ a a a a ımpreun˘ cu a punctul P0 (x0 . y0 ) (sau s s sunt nule dac˘ accept˘m prezenta punctului P0 (x0 . y1 ). Dorim s˘ determin˘m dac˘ punctul P0 (x0 . 18. valori cu semn contrar s s pentru toate punctele din cel˘lalt semiplan ¸i valoarea 0 pentru doate punctele a s situate pe dreapt˘.. y1 )P2 (x2 . a a n ≥ 3 ¸i un punct P0 (x0 . y2 ). adic˘ toate valorile s a fi (xj .5. Dorim s˘ determin˘m dac˘ punctul P0 (x0 . y0 ). a Pentru a fi siguri c˘ punctul P0 (x0 . y0 ) se afl˘ ˆ interiorul poligonului convex.... yn+1 ) este de fapt tot punctul P1 (x1 .18. a a Ecuatia dreptei (Pi Pi+1 ) este ¸ (Pi Pi+1 ) : y − yi = yi+1 − yi (x − xi ) xi+1 − xi Aducem la acela¸i numitor ¸i consider˘m functia s s a ¸ fi (x.. y2 ). Vom verifica dac˘ pentru ¸ a s a a orice latur˘ [Pi Pi+1 ] (1 ≤ i ≤ n) a poligonului toate celelalte vˆrfuri sunt ˆ a a ın acela¸i semiplan cu P0 (x0 . y1 )P2 (x2 . a a ¸ Aceasta este o conditie necesar˘ dar nu ¸i suficient˘. y0 ) este s a a a ˆ interiorul poligonului. a a n ≥ 3 ¸i un punct P0 (x0 . adic˘ punctul Pn+1 este de fapt tot punctul P1 . yj ) (1 ≤ j ≤ n. ın Pentru comoditatea prezent˘rii consider˘m ¸i punctul Pn+1 (xn+1 . y0 ) pe frontiera poligonului). POZITIA UNUI PUNCT FATA DE UN POLIGON CONCAV ¸ ¸˘ 447 Consider˘m un poligon convex cu n vˆrfuri P1 (x1 .. y0 ) (din cele dou˘ determinate de dreapta suport a s a laturii respective) iar dac˘ se ˆ ampl˘ acest lucru atunci putem trage concluzia a ıntˆ a c˘ punctul P0 (x0 . y0 ) sunt de aceea¸i parte a dreptei (Pi Pi+1 ). y0 ).5 Pozitia unui punct fat˘ de un poligon concav ¸ ¸a Consider˘m un poligon concav cu n vˆrfuri P1 (x1 .Pn (xn yn ) este verificarea urm˘toarei relatii: a ¸ n ariepoligon (P1 P2 . y2 ). y) = (y − yi ) · (xi+1 − xi ) − (x − xi ) · (yi+1 − yi ) Dreapta (Pi Pi+1 ) ˆ ımparte planul ˆ dou˘ semiplane. j = i ¸i j = i + 1) au acela¸i semn cu fi (x0 .Pn (xn yn ).Pn (xn yn ). y1 )P2 (x2 . ın . a a ın O alt˘ modalitate de verificare dac˘ punctul P0 (x0 . s a Consider˘m o latur˘ oarecare [Pi Pi+1 ] (1 ≤ i ≤ n) a poligonului. yn+1 ) unde a a s x1 = xn+1 ¸i y1 = yn+1 .

*.1 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 ˆ a¸ur˘toarea convex˘ (cazul a) ˆ figur˘): ınf˘s a a ın a import java. !!! { . a ın P7 P6 P5 P1 P2 P3 P4 P1 P2 P3 P4 P7 P6 P5 a) b) 18. // infasuratoare convexa class Jarvis1 // pe frontiera coliniare ==> le iau pe toate . Dac˘ punctul este ˆ interiorul unui poligon convex ¸ a ın obtinut prin partitionarea poligonului concav atunci el se afl˘ ˆ interiorul acestuia. ¸ ¸ a ın Dac˘ nu se afl˘ ˆ nici un poligon convex obtinut prin partitionarea poligonului a a ın ¸ ¸ concav atunci el nu se afl˘ ˆ interiorul acestuia.448 ˘ CAPITOLUL 18..6.6 ˆ a¸ur˘toarea convex˘ Inf˘s a a ˆ Impachetarea Jarvis 18. GEOMETRIE COMPUTATIONALA ¸ Poligonul concav se descompune ˆ poligoane convexe cu ajutorul diagoın nalelor interne ¸i se folose¸te un algoritm pentru poligoane convexe pentru fiecare s s poligon convex astfel obtinut..io.

k<=k2.i<=n.˘ ˘ 18.println().i++) { //System.i. for(i=1.out. else return 0. for(k=k1. ˆ ASURATOAREA CONVEXA INF ˘ ¸ static static static static static int int int int int n. System.out.out.println("Stanga_Jos ==> P"+i0+" : "+x[i0]+" "+y[i0]).npic=0.k++) System. int i2. } static void infasurareJarvis() throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System.6. i0=1. System. int k2) { int k.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) .in)).i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i. else if(s>0) return 1.out.i.print(a[k]+" ").i2)). [] u. //br. //br. int i3) { long s=(y[i1]-y[i2])*(x[i3]-x[i2])-(y[i3]-y[i2])*(x[i1]-x[i2]). [] x.println("orient("+i1+"."+i2+")="+orient(i1. i1=i0.readLine(). if(orient(i1. System. for(i=2. int i0.i. else if(orient(i1. if(i2>n) i2-=n. 449 // npic=nr puncte pe infasuratoarea convexa // precedent // urmator static void afisv(int[] a. int k1. do { i2=i1+1.i<=n. if(s<0) return -1.readLine().i.i2. [] y.out."+i+".println(npic+" --> "+i1). [] p.i1. } static int orient(int i1.i2)>0) i2=i. npic++.

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

int i3) { long s=(y[i1]-y[i2])*(x[i3]-x[i2])-(y[i3]-y[i2])*(x[i1]-x[i2]). int i2.out. for(k=k1. i1=i2."+i+".out. } static void infasurareJarvis() throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System.i2)>0) i2=i. if(orient(i1. . i0=1. if(s<0) return -1. else return 0.println(npic+" --> "+i1). //br.out. ˆ ASURATOAREA CONVEXA INF ˘ ¸ static void afisv(int[] a.i.˘ ˘ 18. System. p[i2]=i1.println().i.i. int k1. else if(orient(i1.out.i2)). int i0. npic++.k<=k2.i<=n.i. for(i=2. do { i2=i1+1.i1.i2.k++) System.println("Stanga_Jos ==> P"+i0+" : "+x[i0]+" "+y[i0]).6.readLine().print(a[k]+" ").i<=n.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.readLine(). //br. } u[i1]=i2.i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i.i++) { //System. System. if(i2>n) i2-=n.println("orient("+i1+". else if(s>0) return 1. System. for(i=1. i1=i0.out."+i2+")="+orient(i1. int k2) { int k.in)). } 451 static int orient(int i1.

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

} . ˆ ASURATOAREA CONVEXA INF ˘ ¸ 453 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.6.k<=k2. System. } static int orient(int i0.k++) System. if(s<0) return -1. else return 0.˘ ˘ 18. int i2) { long s=(y[i1]-y[i0])*(x[i2]-x[i0])-(y[i2]-y[i0])*(x[i1]-x[i0]). for(k=k1.print(a[k]+" "). int k1. int k2) { int k.print(" "). else if(s>0) return 1.int i1.out.out.

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

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

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

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

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

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

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

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

nextToken().nval. a 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 18.nextToken().1: Dreptunghi minim de acoperire Putem s˘ presupunem c˘ punctele formeaz˘ un poligon convex. } scanareGraham().nval.462 ˘ CAPITOLUL 18. Dintre aceste dreptunghiuri ¸ a se alege cel cu aria minim˘.7 Dreptunghi minim de acoperire a punctelor Se poate determina dreptunghiul minim de acoperire pentru ˆ a¸ur˘toarea ınf˘s a convex˘ (figura 18. }//main }//class 18.1) pentru a prelucra mai putine puncte dar nu este obligatorie a ¸ aceast˘ strategie. y[k]=(int)st. o[k]=k. x[k]=(int)st. a . GEOMETRIE COMPUTATIONALA ¸ st. Pentru fiecare latur˘ a poligonului convex se determin˘ drepa a tunghiul minim de acoperire care contine acea latur˘. Determinarea a a a dreptunghiului de arie minim˘ care contine ˆ interiorul s˘u (inclusiv frontiera) a ¸ ın a toate punctele date se poate face observˆnd c˘ o latur˘ a sa contine o latur˘ a a a a ¸ a poligonului convex. st.

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

464

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸

Gr˘dinile roditoare ale B˘r˘ganului sufer˘ anual pierderi imense din a aa a cauza secetei. C˘ut˘torii de ap˘ au g˘sit n fˆntˆni din care doresc s˘ alimenteze n a a a a a a a gr˘dini. Fie Gi , Fi , i = 1, ..., n puncte ˆ plan reprezentˆnd puncte de alimentare a ın a ale gr˘dinilor ¸i respectiv punctele ˆ care se afl˘ fˆntˆnile. Pentru fiecare punct a s ın a a a se dau coordonatele ˆ ıntregi (x, y) ˆ plan. ın Pentru a economisi materiale, leg˘tura dintre o gr˘din˘ ¸i o fˆntˆn˘ se reaa a as a a a lizeaz˘ printr-o conduct˘ ˆ linie dreapt˘. Fiecare fˆntˆn˘ alimenteaz˘ o singur˘ a a ın a a a a a a gr˘din˘. Consiliul Judetean Galati pl˘te¸te investitia cu conditia ca lungimea toa a ¸ ¸ a s ¸ ¸ tal˘ a conductelor s˘ fie minim˘. a a a Fiecare unitate de conduct˘ cost˘ 100 lei noi (RON). a a Cerint˘ ¸a S˘ se determine m, costul minim total al conductelor ce leag˘ fiecare gr˘din˘ a a a a cu exact o fˆntˆn˘. a a a Date de intrare Fi¸ierul de intrare seceta.in va contine: s ¸ • Pe prima linie se afl˘ num˘rul natural n, reprezentˆnd num˘rul gr˘dinilor a a a a a ¸i al fˆntˆnilor. s a a • Pe urm˘toarele n linii se afl˘ perechi de numere ˆ a a ıntregi Gx Gy , separate printr-un spatiu, reprezentˆnd coordonatele punctelor de alimentare ale gr˘dinilor. ¸ a a • Pe urm˘toarele n linii se afl˘ perechi de numere ˆ a a ıntregi Fx Fy , separate printr-un spatiu, reprezentˆnd coordonatele punctelor fˆntˆnilor. ¸ a a a Date de ie¸ire s Fi¸ierul de ie¸ire seceta.out va contine: s s ¸ m − un num˘r natural reprezentˆnd partea ˆ a a ıntreag˘ a costului minim total a al conductelor. Restrictii ¸i preciz˘ri ¸ s a • 1 < n < 13 • 0 ≤ Gx, Gy, F x, F y ≤ 200 • Nu exist˘ trei puncte coliniare, indiferent dac˘ sunt gr˘dini sau fˆntˆni a a a a a • Orice linie din fi¸ierele de intrare ¸i ie¸ire se termin˘ prin marcajul de sfˆr¸it s s s a as de linie. Exemplu seceta.in 3 14 33 47 23 25 31 seceta.out 624 Explicatie ¸ Costul minim este [6.24264 * 100]=624 prin legarea perechilor: Gradini Fantani 14 23 33 31 47 25

Timp maxim de executie/test: 1 sec sub Windows ¸i 0.5 sec sub Linux. ¸ s

18.9. PROBLEME REZOLVATE Indicatii de rezolvare * ¸

465

Solutia oficial˘, lect. Ovidiu Dom¸a ¸ a s Num˘rul mic al punctelor permite generarea tuturor posibilit˘¸ilor de a conecta a at o gr˘din˘ cu o fˆntˆn˘ neconectat˘ la un moment dat. a a a a a a Pentru fiecare astfel de combinatie g˘sit˘ se calculeaz˘ suma distantelor ¸ a a a ¸ (Gi, F j), ˆ linie dreapta, folosind formula distantei dintre dou˘ puncte ˆ plan, ın ¸ a ın studiat˘ la geometrie. (d(A(x, y), B(z, t) = (x − z)2 + (y − t)2 ). a Acest˘ solutie implementat˘ corect asigur˘ 60 − 70 de puncte. a ¸ a a Pentru a obtine punctajul maxim se tine cont de urm˘toarele aspecte: ¸ a 1. Se construie¸te ˆ prealabil matricea distantelor d(i, j) cu semnificatia s ın ¸ ¸ distantei dintre gr˘dina i ¸i fˆntˆna j. Aceasta va reduce timpul de calcul la ¸ a s a a variantele cu peste 9 perechi. 2. Pentru a elimina cazuri care nu pot constitui solutii optime se folose¸te ¸ s proprietatea patrulaterului c˘ suma a doua laturi opuse (conditie care asigur˘ a ¸ a unicitatea conect˘rii unei singure fˆntˆni la o singur˘ gr˘din˘) este mai mic˘ decˆt a a a a a a a a suma diagonalelor. De aceea nu se vor lua ˆ considerare acele segmente care se ın intersecteaz˘. Conditia de intersectie a dou˘ segmente care au capetele ˆ punctele a ¸ ¸ a ın de coordonate A(a1, a2), B(b1, b2), C(c1, c2), D(d1, d2) este ca luˆnd segmentul a AB, punctele C ¸i D s˘ se afle de aceea¸i parte a segmentului AB ¸i respectiv s a s s pentru segmentul CD, punctele A ¸i B s˘ se afle de aceea¸i parte (se ˆ s a s ınlocuie¸te s ˆ ecuatia dreptei ce trece prin dou˘ puncte, studiat˘ ˆ clasa a 9-a). ın ¸ a a ın Observatie: Pentru cei interesati, problema are solutie ¸i la un nivel superior, ¸ ¸ ¸ s folosind algoritmul de determinare a unui flux maxim de cost minim. Variant˘ cu determinarea intesectiei segmentelor. a ¸ 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();

466

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ 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;

18.9. PROBLEME REZOLVATE

467

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;

468

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ if((x1==x2)&&(x3==x4)) // ambele segmente verticale if(x1!=x3) return false; else if(intre(y1,y3,y4)||intre(y2,y3,y4)) return true; else return false; if((y1==y2)&&(y3==y4)) // ambele segmente orizontale if(y1!=y3) return false; else if(intre(x1,x3,x4)||intre(x2,x3,x4)) return true; else return false; 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,x3,x4)||intre(x2,x3,x4)) return true; else return false; else return false;// 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(x2!=x1) y=y1+(y2-y1)*(x-x1)/(x2-x1); else y=y3+(y4-y3)*(x-x3)/(x4-x3); if(intre(x,x1,x2)&&intre(y,y1,y2)&&intre(x,x3,x4)&&intre(y,y3,y4)) return true; else return false; }

} static boolean intre(int c, int a, int b) // c este in [a,b] ? { int aux; if(a>b) {aux=a; a=b; b=aux;} if((a<=c)&&(c<=b)) return true; else return false; } static boolean intre(double c, int a, int b) // c este in [a,b] ? { int aux; if(a>b) {aux=a; a=b; b=aux;} if((a<=c)&&(c<=b)) return true; else return false; } static void afisare() throws IOException { int k;

18.9. PROBLEME REZOLVATE

469

out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.out"))); out.println((int)(costMin*100)); out.close(); } } Variant˘ cu cu determinarea pozitiei punctelor in semiplane ¸i mesaje pentru a s depanare. import java.io.*; // cu determinarea pozitiei punctelor in semiplane class Seceta2 // cu mesaje pentru depanare ! { 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(); 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++)

470 {

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸

st.nextToken(); xg[k]=(int)st.nval; st.nextToken(); yg[k]=(int)st.nval; } for(k=1;k<=n;k++) { st.nextToken(); xf[k]=(int)st.nval; st.nextToken(); yf[k]=(int)st.nval; } } 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 } 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((s(xg[k],yg[k],xg[j],yg[j],xf[a[j]],yf[a[j]])* s(xf[i],yf[i],xg[j],yg[j],xf[a[j]],yf[a[j]])<0)&& (s(xg[j], yg[j], xg[k],yg[k],xf[i],yf[i])* s(xf[a[j]],yf[a[j]],xg[k],yg[k],xf[i],yf[i])<0)) { afisv(k-1);// pe pozitia k(gradina) vreau sa pun i(fantana) System.out.print(i+" ");// pe pozitia j(gradina) e pus a[j](fantana) System.out.print(k+""+i+" "+j+""+a[j]); System.out.print(" ("+xg[k]+","+yg[k]+") "+" ("+xf[i]+","+yf[i]+") ");

18.9. PROBLEME REZOLVATE System.out.println(" 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; afisv(n); System.out.println(" "+s+" "+costMin+" "+(++nv)); } static void afisv(int nn) { int i; for(i=1;i<=nn;i++) System.out.print(a[i]); } // 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 } ("+xg[j]+","+yg[j]+") "+"

471 ("+xf[a[j]]+","+yf[a[j]]+") ");

static void afisare() throws IOException { int k; out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.out"))); out.println((int)(costMin*100)); out.close(); } }

472

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸

Variant˘ cu cu determinarea pozitiei punctelor in semiplane, f˘r˘ mesaje a aa pentru depanare. import java.io.*; // cu determinarea pozitiei punctelor in semiplane class Seceta3 // Java este "mai incet" decat Pascal si C/C++ { // test 9 ==> 2.18 sec 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(); 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(); xg[k]=(int)st.nval; st.nextToken(); yg[k]=(int)st.nval; } for(k=1;k<=n;k++)

18.9. PROBLEME REZOLVATE { st.nextToken(); xf[k]=(int)st.nval; st.nextToken(); yf[k]=(int)st.nval; } } 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 }

473

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((s(xg[k], yg[k], xg[j],yg[j],xf[a[j]],yf[a[j]])* s(xf[i], yf[i], xg[j],yg[j],xf[a[j]],yf[a[j]])<0)&& (s(xg[j], yg[j], xg[k],yg[k],xf[i], yf[i])* s(xf[a[j]],yf[a[j]],xg[k],yg[k],xf[i], yf[i])<0)) { ok=false; break; } if(!ok) continue; a[k]=i; if(k<n) f(k+1); else verificCostul(); } } static void verificCostul()

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

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

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

Pe urm˘toarele n linii se afl˘ pozitiile a a a a a ¸ caselor. iar puterea antenei s˘ fie minim˘. se verific˘ dac˘ diferenta dintre solutia afi¸at˘ ¸i cea corect˘ a a ¸ ¸ s as a (ˆ valoare absolut˘) este < 0.2 Antena .9. astfel a ¸ a ˆ at fiecare cas˘ s˘ se afle ˆ interiorul sau pe frontiera zonei circulare ˆ care ıncˆ a a ın ın emite antena. Pe cea de a doua linie se va scrie un num˘r real reprezentˆnd puterea antenei. rupt˘ de bucuriile ¸i necazurile In a a a a a a s civilizatiei moderne. s ¸ a reprezentˆnd num˘rul de case din zon˘. va trebui s˘ instaleze o anten˘ de emisie special˘ pentru aceasta. prin urmare. a a Restrictii ¸i preciz˘ri ¸ s a • 2 < N < 15001 • −15000 < x. Liceul International de Informatic˘ Bucure¸ti a s ˆ Delta Dun˘rii exist˘ o zon˘ s˘lbatic˘. Raza zonei este denumit˘ puterea antenei.out contine pe prima linie dou˘ numere reale seps s ¸ a arate printr-un spatiu x y reprezentnd abscisa ¸i ordonata pozitiei optime de am¸ s ¸ plasare a antenei.in contine pe prima linie un num˘r natural n. Centrul zonei coincide cu a a punctul ˆ care este pozitionat˘ antena. y < 15001 • Numerele reale din fi¸ierul de ie¸ire trebuie scrise cu trei zecimale cu rotuns s jire.01. a Postul de radio al ONI 2005 dore¸te s˘ emit˘ pentru toti locuitorii din zon˘ s a a ¸ a ¸i. s a a a O anten˘ emite unde radio ˆ a ıntr-o zon˘ circular˘. cu atˆt antena este mai scump˘. Mai exact. Osman Ay. • La evaluare. ce reprezint˘ abscisa ¸i respectiv ordonata casei i. ın s ¸ Datele de ie¸ire s Fi¸ierul de ie¸ire antena. pozitiile acestora fiind specificate prin In a a a ¸ coordonatele carteziene de pe hart˘. pe linia i + 1 se afl˘ dou˘ numere ˆ a a ıntregi separate printr-un spatiu x y. ¸ ˆ aceast˘ zon˘ exist˘ doar n case. ın ¸ a a Cu cˆt puterea antenei este mai mare. ın a Exemplu . s a Datele de intrare Fi¸ierul de intrare antena. a a a Prin urmare trebuie selectat˘ o pozitie optim˘ de amplasare a antenei.18. Nu exist˘ dou˘ case ¸ a s a a ˆ aceea¸i locatie. ¸ a ¸ a precum ¸i puterea minim˘ a acesteia.9.ONI2005 clasa a X-a prof. a a Cerint˘ ¸a Scrieti un program care s˘ determine o pozitie optim˘ de amplasare a antenei. PROBLEME REZOLVATE 477 18.

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

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

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

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

dx=x2-x1. double x2.dy. fiind cam ¸uubred. int y1.sqrt(dx*dx+dy*dy). dar nu pe o distant˘ mai mare decˆt o valoare ın ¸ ¸a a dat˘ (scris˘ pe fiecare par) ¸i fiecare segment de gard. double y1. dx=x2-x1.sqrt(dx*dx+dy*dy). } static double d(double x1.482 ˘ CAPITOLUL 18... }// swap(. GEOMETRIE COMPUTATIONALA ¸ static double d(int x1. x[i]=x[j]. Date de intrare . distantele pe care fiecare par poate fi deplasat sunt ¸ numere naturale strict pozitive ¸i figura format˘ de terenul initial este un poligon s a ¸ neconcav. int x2. s Terenul este ˆ ımprejmuit complet cu segmente drepte de gard ce se sprijin˘ la a ambele capete de cˆte un par zdrav˘n. P˘cal˘ iese iar ˆ a a a a a ın cˆ¸tig ¸i prime¸te dreptul s˘ str˘mute ni¸te pari. cum i-o fi voia. dy=y2-y1. double y2) { double dx.dy. cel˘lalt r˘mˆnˆnd nemi¸cat.) }// class 18. int j) { int aux. Se ¸tie c˘ ¸ a as a a a s a parii sunt dati ˆ ¸ ıntr-o ordine oarecare. return Math. x[j]=aux.3 Mo¸ia lui P˘cal˘ . as s s a a s a astfel ˆ at s˘-¸i extind˘ suprafata de teren. unul cˆte unul. Dar ˆ ıncˆ a s a ¸ ınvoiala prevede c˘ fiecare par a poate fi mutat ˆ orice directie. return Math. pozitiile lor initiale sunt date prin numere ¸ ¸ ˆ ıntregi de cel mult 3 cifre. dy=y2-y1.9. se a cere suprafata maxim˘ cu care poate s˘-¸i extind˘ P˘cal˘ proprietatea. poate fi a a s s rotit ¸i prelungit de la un singur cap˘t. aux=x[i]. aux=y[i]. a¸a cum era ˆ a a s ınvoiala. int y2) { double dx. un petec de teren de pe mo¸ia boierului. La o nou˘ prinsoare.OJI2004 clasa a XI-a s a a P˘cal˘ a primit. s a a a a a s Cunoscnd pozitiile initiale ale parilor ¸i valoarea ˆ ¸ ¸ s ınscris˘ pe fiecare par. } //interschimb punctele i si j static void swap(int i. y[j]=aux. y[i]=y[j].

a . yi < 1000 numere ˆ ıntregi 0 < di ≤ 20 numere ˆ ıntregi poligonul neconcav se define¸te ca un poligon convex cu unele vˆrfuri coliniare s a pozitiile parilor sunt date ˆ ¸ ıntr-o ordine oarecare poligonul obtinut dup˘ mutarea parilor poate fi concav ¸ a pozitiile finale ale parilor nu sunt ˆ mod obligatoriu numere naturale ¸ ın Exemplu Pentru fi¸ierul de intrare s 4 -3 0 2 3 0 3 0 6 2 0 -6 6 se va scrie ˆ fi¸ierul de ie¸ire valoarea 30.OUT se scrie un num˘r real cu 4 zecimale ce reprezint˘ In s a a suprafata maxim˘ cu care se poate m˘ri mo¸ia.coordonatele initiale ¸i distanta pe care poate fi mutat parul n ¸ s ¸ Date de ie¸ire s ˆ fi¸ierul MOSIA./test a 18.0000 ın s s Explicatie: prin mutarea parilor 1 ¸i 2 cu cˆte 2 ¸i respectiv 3 unit˘¸i.coordonatele initiale ¸i distanta pe care poate fi mutat parul 2 ¸ s ¸ . adic˘ dac˘ sunt a ¸ a a ˆ ındeplinite conditiile: ¸ • nu exist˘ piese suprapuse. ¸ a ¸ at a ¸ Timp limit˘ de executare: 1 sec. pentru care se a s a a cunosc coordonatele vˆrfurilor lor.coordonatele initiale ¸i distanta pe care poate fi mutat parul 1 ¸ s ¸ x2 y2 d2 . semiaxa [Ox pe cateta de lungime a. respectiv b ¸i un sistem de coordonate xOy cu originea ˆ unghiul s ın drept al triunghiului. Tat˘l lui Ionic˘ vrea s˘ verifice dac˘ pe tabl˘ a a a a a a piesele realizeaz˘ o partitie a triunghiului dreptunghic desenat. respectiv semiaxa [Oy pe cateta de lungime b. ¸ a a s Restrictii ¸i observatii: ¸ s ¸ 3 < N ≤ 200 num˘r natural a −1000 < xi . se ¸ s a s at obtine un teren avˆnd suprafata cu 30 de unit˘¸i mai mare decˆt terenul initial.ONI2006 baraj ¸ Ionic˘ a primit de ziua lui de la tat˘l s˘u un joc format din piese de form˘ a a a a triunghiular˘ de dimensiuni diferite ¸i o suprafatu a magnetic˘ pe care acestea pot a s ¸ a fi a¸ezate..9.9. xn yn dn .IN contine n + 1 linii cu urm˘toarele valori: s ¸ a n . PROBLEME REZOLVATE 483 Fi¸ierul MOSIA.18. La un moment dat Ionic˘ a¸eaz˘ pe tabla magnetic˘ n piese.4 Partitie .num˘rul de pari a x1 y1 d1 .. s Pe suprafata magnetic˘ este desenat un triunghi dreptunghic cu lungimile ¸ a catetelor a.

¸ a ın Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ n ≤ 150 • 1 ≤ k ≤ 10 • a. GEOMETRIE COMPUTATIONALA ¸ • piesele acoper˘ toat˘ portiunea desenat˘ (ˆ form˘ de triunghi dreptunghic). 31000] • Coordonatele vrfurilor pieselor sunt numere ntregi din intervalul [0.*. b. cˆte o pies˘ pe o linie.3 secunde/test ¸ Prelucrare ˆ Java dup˘ rezolvarea ˆ C a autorului problemei ın a ın import java. 2. Urmeaz˘ k grupe de linii. .484 ˘ CAPITOLUL 18. Grupa de linii corespunz˘toare unui set este format˘ a a dintr-o linie cu numerele a. class part { static final int ON_EDGE=0. cˆte o linie pentru fiecare set de date.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.. reprezens ¸ a tˆnd num˘rul de seturi de date din fi¸ier. a ¸ ın Cerint˘ ¸a Se cere s˘ se verifice dac˘ piesele plasate pe tabla magnetic˘ formeaz˘ o a a a a partitie a triunghiului desenat pe tabla magnetic˘.. ¸ a Date de intrare Fi¸ierul de intrare part.out 1 0 Timp maxim de executie: 0.. Exemplu part.io.in contine pe prima linie un num˘r natural k. . static final int INSIDE=1. b sunt numere ˆ ıntregi din intervalul [0. In s a Pe linia i (i = 1. a a a a Date de ie¸ire s ˆ fi¸ierul part. 31000].out se vor scrie k linii. cˆte o grup˘ a a s a a a pentru fiecare set de date. a a ¸ a ın a • nu exist˘ portiuni din piese ˆ afara triunghiului desenat. n separate ˆ ıntre ele prin cˆte un spatiu ¸i n linii cu a ¸ s cˆte ¸ase numere ˆ a s ıntregi separate prin spatii reprezentˆnd coordonatele vˆrfurilor ¸ a a (abscis˘ ordonat˘) celor n piese. k) se va scrie 1 dac˘ triunghiurile din setul de date i formeaz˘ a a o partitie a triunghiului desenat pe tabla magnetic˘ sau 0 ˆ caz contrar.

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

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

5 Triunghi .i++) for(j=0.nval. } X[N][0]=0.i++) for(j=i+1.println(solve()). i.nval. st. X[N][1]=A.18.j<N.) { st. return 1. X[N][2]=0. A=(int)st. PROBLEME REZOLVATE 487 for(i=0. for(i=0.9. Y[N][2]=B. Y[i][j]=(int)st. }// main(. Y[N][1]=0. } public static void main(String[] args) throws IOException { int tests.out"))).9.nextToken().. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("part. st. tests=(int)st.j++) if(triangle_intersect(i.nextToken().nextToken().i<N. } out. tests-->0. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("part..i<N.nextToken(). X[i][j]=(int)st.in"))).nval.ONI2007 cls 9 ˆ comuna Triunghi din Romˆnia sunt n ¸˘rani codificati prin numerele In a ta ¸ .nval. out. Y[N][0]=0.nextToken(). st.) }// class 18.nval.j)) return 0. B=(int)st. N=(int)st. st.nextToken(). for(. j.j++) { st.j<3.close().nval.

n. Pe ¸ ta linia i + 1 se afl˘ coordonatele colturilor suprafetei de teren triunghiulare detinute a ¸ ¸ ¸ de ¸˘ranul i. iar y ordonat˘). . n.. a a a b) Codul ¸˘ranului ce detine un document cu suprafata de teren. Din p˘cate. 2. cu un spatiu ˆ ın a ¸ ıntre ele. x2. ˆ fora ¸ ın matul: x1 y1 x2 y2 x3 y3. . 2. Dup˘ anul 1990 a ˆ a ınceput retrocedarea suprafetelor de p˘mˆnt detinute ¸ a a ¸ ˆ ınainte de colectivizare. cu un spatiu ˆ a a ın ¸ ıntre ele (x abscis˘. ce reprezint˘ coordonatele celor trei colturi ale suprafetei a ¸ ¸ triunghiulare detinute de un ¸˘ran (x1. Fiecare ¸˘ran are un document prin care dovede¸te c˘ este ta s a proprietar pe o singur˘ suprafat˘ de teren de form˘ triunghiular˘. GEOMETRIE COMPUTATIONALA ¸ 1. Cerint˘ ¸a S˘ se scrie un program care s˘ determine: a a a) Codurile ¸˘ranilor care au documente cu suprafete de p˘mˆnt ce contin ˆ ta ¸ a a ¸ ın interior sau pe frontier˘ fˆntˆna. care include ta ¸ ¸ toate celelalte suprafete. a a Date de ie¸ire s Fi¸ierul de ie¸ire triunghi.. 3000] • cele trei colturi ale fiec˘rei suprafete de p˘mˆnt sunt distincte ¸i necoliniare ¸ a ¸ a a s • nu exist˘ doi ¸˘rani care s˘ detin˘ aceea¸i suprafat˘ de p˘mˆnt a ta a ¸ a s ¸a a a • nu se acord˘ punctaje partiale. iar y1. sau cifra a a ta a 0. ¸ Date de intrare Fi¸ierul de intrare triunghi. Ultima linie a fi¸ierului (linia n + 2) va contine coordota s ¸ natele fˆntˆnii ˆ formatul x y. Dac˘ nu exist˘ ¸˘rani a a ta cu conditia din cerint˘.. pe prima linie se va scrie cifra 0. y3 ordonate).488 ˘ CAPITOLUL 18.. pe s a ta urm˘toarele n linii cˆte 6 valori numere ˆ a a ıntregi separate prin cˆte un spatiu.out va contine pe prima linie r˘spunsul de la s s ¸ a punctul a). Pe linia a doua se va scrie ¸ ¸a r˘spunsul de la punctul b). ˆ aceast˘ comun˘ exist˘ o fˆntˆn˘ cu ap˘.. x3 abscise. O suprafat˘ de p˘mˆnt este dat˘ prin coordonatele celor a ¸ ta ¸a a a a trei colturi.in are pe prima linie num˘rul n de ¸˘rani. a ¸ Exemplu . fiind posibil ca ea s˘ fie revendiIn a a a a a a a a cat˘ de mai multi ¸˘rani. doca ¸a a a a umentele dau b˘taie de cap primarului (care se ocup˘ de retrocedarea suprafetelor a a ¸ de p˘mˆnt). a a ta Restrictii ¸i preciz˘ri ¸ s a • 2 ≤ n ≤ 65 • coordonatele colturilor suprafetelor de p˘mˆnt ¸i ale fˆntˆnii sunt numere ¸ ¸ a a s a a ˆ ıntregi din intervalul [−3000. iar fˆntˆna este considerat˘ punctiform˘ ¸i dat˘ prin coordonatele ¸ a a a a s a punctului. adic˘: num˘rul de ¸˘rani care ˆ a a ta ındeplinesc conditia din cerint˘ ¸i apoi ¸ ¸a s codurile lor (ˆ ordine cresc˘toare). y2. adic˘: codul ¸˘ranului cu proprietatea cerut˘. i = 1.. dac˘ nu exist˘ un astfel de ¸˘ran. pentru c˘ sunt portiuni din suprafetele de p˘mˆnt care se reg˘sesc pe a a a ¸ ¸ a a a mai multe documente.

. Afi¸am nr ¸i vectorul sol.9. ˆ caz afirmativ se afi¸eaz˘ p.18. . ˆ caz a a ın afirmativ nr = nr + 1 ¸i sol[nr] = i. Doru Popescu Anastasiu) ¸ Not˘m cu T1 . T2 .. i = 1. ¸ a a ¸ ¸ ta s Timp maxim de executie/test: 0. Pentru acest triunghi verific˘m dac˘ toate a a a a celelalte n − 1 triunghiuri sunt interioare sau pe frontiera lui Tp (adic˘ dac˘ au a a toate vˆrfurile ˆ interiorul sau pe frontiera lui Tp ).out 212 2 489 Explicatie: ¸ La punctul a). Astfel dea a a termin˘m triunghiul p de arie maxim˘.1 secunde ¸ Indicatii de rezolvare . 2. a ın In s a altfel 0. ta ¸ ¸a suprafetele de p˘mˆnt detinute de ceilalti ¸˘rani (cu codurile 1 ¸i 3)... s s˘ s Pentru a verifica dac˘ I este interior sau pe frontiera unui triunghi Ti este a suficient s˘ verific˘m dac˘: a a a aria(Ai Bi Ci ) = aria(IAi Bi ) + aria(IAi Ci ) + aria(IBi Ci ) O alt˘ variant˘ ar fi s˘ folosim pozitia unui punct fat˘ de o dreapt˘.io. n. Tn triunghiurile corespunz˘toare suprafetelor ¸i cu I a a ¸ s punctul unde se g˘se¸te fˆntˆna.. a a a ¸ ¸a a b) Dac˘ exist˘ un asemenea triunghi atunci el este de arie maxim˘. a a a s La punctul b). n verific˘m dac˘ I este interior sau pe frontiera lui Ti . a) nr = 0 Pentru i = 1. cu codurile 1 ¸i 2. ..*. a s a a Ti = Ai Bi Ci . .. class Pereti { . sunt doi ¸˘rani care detin suprafete de p˘mˆnt ce au ˆ interior ta ¸ ¸ a a ın sau pe frontier˘ fˆntˆna.descriere solutie ¸ ¸ Descrierea solutiei (Prof. PROBLEME REZOLVATE secv.. ¸˘ranul cu codul 2 detine o suprafat˘ de teren care include.in 3 10 0 0 10 10 10 0 100 100 0 -100 0 0 0 10 0 0 10 10 5 secv.. Codul surs˘ a import java.

x1[i]=(int)st. st. for(i=1. } if(nr>0) { out. s2=aria(x2[i]. .nval. n=(int)st. for(i=1. int smax=-1. y1[i]=(int)st.nextToken(). boolean ok.i<=n.nval.s3. st. st. int[] x1=new int[66].println("i = "+i+" --> "+s+" "+s1+" "+s2+" "+s3).imax=-1. st.x3[i].j. y0=(int)st.x0.nval.y2[i].out. int[] y2=new int[66]. if(s==s1+s2+s3) {nr++.nextToken(). y3[i]=(int)st.y3[i].nextToken().s1.nextToken().y1[i].x2[i].x2[i].y2[i].x3[i].nextToken().490 static static static static static static static static static ˘ CAPITOLUL 18.nval.print(nr+" "). st.in"))).y0). x2[i]=(int)st.} //System.y1[i].y2[i].x0.x3[i].x0. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("14-triunghi.nval.nextToken(). imax=i. int[] y3=new int[66]. x0=(int)st.s. st. int[] x2=new int[66].x0.y1[i].y0).s2.nr=0. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("triunghi.} s1=aria(x1[i]. y2[i]=(int)st. public static void main(String[] args) throws IOException { int i.i++) { s=aria(x1[i]. s3=aria(x1[i]. int[] sol=new int[66].i++) { st.nextToken(). if(s>smax) {smax=s.nval. int[] y1=new int[66]. sol[nr]=i.y0.nval.nextToken().nextToken(). x3[i]=(int)st.i<=n.out"))). } st.nval.y3[i].y0). int[] x3=new int[66].nval. GEOMETRIE COMPUTATIONALA ¸ int n.y3[i]). st.

x2[imax]. return s. break. } s1=aria(x1[imax].9. break. int y3) // dubla .y1[imax]. break.x3[imax].x2[imax].y3[i]).y3[i]).x1[i]. if(smax!=s1+s2+s3) { ok=false. if(s<0) s=-s.x1[i].x3[i]. { int s=x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1. int x2.y2[imax].y1[imax].x1[i].y1[i]).. s3=aria(x1[imax]. int x3.y3[imax]. for(i=1.x3[imax].y2[imax]. s3=aria(x1[imax].println("imax = "+imax).y3[i]).i++) if(i!=nr) out. ok=true.x2[i]. }// main(.println(sol[i]). else out.println(0).x3[imax].println(0).y1[i]).x3[i].x3[imax].) static int aria(int x1.y1[imax]. if(smax!=s1+s2+s3) { ok=false.y3[imax].x3[i].y2[imax].x2[i]. else out..y3[imax].x2[i].i<=n.x2[imax]. } } if(ok) out.y1[imax]. s2=aria(x2[imax].. s2=aria(x2[imax].println(imax). int y2.y2[i]).i<=nr. } s1=aria(x1[imax]. s2=aria(x2[imax].y2[imax]. //System. if(smax!=s1+s2+s3) { ok=false.i++) { if(i==imax) continue.y2[imax]. int y1.y1[imax].18.close().out.y1[i]).. s1=aria(x1[imax]. s3=aria(x1[imax].x3[imax].y3[imax].y3[imax].y2[i]).x3[imax]. } }// class .y1[imax].y2[i]).y3[imax]. } else out.print(sol[i]+" "). out. PROBLEME REZOLVATE 491 for(i=1.y2[imax].

492 ˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ .

Capitolul 19 Teoria jocurilor 19.1 Jocul NIM 19.1 Prezentare general˘ a 19.2 Exemple 493 .1.1.

494 CAPITOLUL 19. TEORIA JOCURILOR .

v) // 3. OK=true 495 . init // 2.1 Algoritmul Belmann-Ford pentru grafuri neorientate // drum scurt in graf neorientat cu costuri negative (dar fara ciclu negativ!) // Algoritm: 1. 20.1.2 Exemple 20.1 Secvent˘ de sum˘ maxim˘ ¸a a a 20.2. repeta de n-1 ori // pentru fiecare arc (u.Capitolul 20 Alti algoritmi ¸ 20.1.2 Algoritmul Belmann-Ford Pentru grafuri cu costuri negative (dar fara cicluri negative!) se poate folosi algoritmul Bellman-Ford.v) // relax(u.1 Prezentare general˘ a 20.

i++) for(j=1. ALTI ALGORITMI ¸ 4.v. muchii static int[][] w. w[j][i]=cost. w[i][j]=cost.i<=n. cost=(int)st.nextToken(). p=new int[n+1].// initializare ! for(k=1. static int[] d. // varfuri. // matricea costurilor // d[u]=dist(nods.k. .nextToken().in"))). n=(int)st.nval. st.nval.nval.nval.m. // nod sursa. m=(int)st. class BellmanFord { static final int oo=0x7fffffff. // numai pentru graf neorientat } init(nods). return OK import java.nval. int nods=1.v) daca d[v]>d[u]+w[u][v] OK=false 5. cost.j<=n. // infinit static int n.*. st.nextToken(). StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("BellmanFordNeorientat. i=(int)st. j=(int)st.496 // // // // CAPITOLUL 20. st. w=new int[n+1][n+1]. costul arcului int u.j.u) // p[u]=predecesorul nodului u public static void main(String[] args) throws IOException { int i.k<=m. static int[] p.io.j++) w[i][j]=oo.nextToken(). d=new int[n+1]. st. pentru fiecare muchie (u.k++) { st.nextToken(). for(i=1.

System.print(nods+"-->"+k+" dist="+d[k]+" drum: ").u++) for(v=u+1. } }//main static void init(int s) { int u. }// init(.20.out.u<=n. p[v]=u.int v) // (u. } static void relax(int u.v++) if(w[u][v]<oo) // (u. ALGORITMUL BELMANN-FORD 497 for(k=1. } if(!cicluNegativ) for(k=1.v<=n.u++) { d[u]=oo.v++) if(w[u][v]<oo) relax(u. for(u=1.v)=muchie if(d[u]<oo) // atentie !!! oo+ceva=??? if(d[v]>d[u]+w[u][v]) { cicluNegativ=true. for(u=1.v).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].u<n. break. // // // // de n-1 ori !!! vectorii muchiilor erau mai buni ! lista de adiacenta era mai buna ! (u.2.out.. d[s]=0.k<=n-1. } } .v)=muchie si u<v boolean cicluNegativ=false.u++) for(v=u+1.) p[u]=-1.v<=n.k++) for(u=1.k<=n.u<n.println()..k++) { System. drum(k).

out.2.2 Alg 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.v) // 3.*. } static void afisv(int[] x) { int i. repeta de n-1 ori // pentru fiecare arc (u.. System.i<=n. ALTI ALGORITMI ¸ static void drum(int k) // s --> . muchii . pentru fiecare arc (u. // varfuri. --> k { if(p[k]!=-1) drum(p[k]).print(x[i]+" ").io.v) // daca d[v]>d[u]+w[u][v] // OK=false // 5. init // 2.out. static int n.m.498 CAPITOLUL 20. class BellmanFord { static final int oo=0x7fffffff.print(k+" "). return OK import java.v) // relax(u.out. for(i=1.i++) System. System.println(). } }//class /* 6 7 1 2 1 3 2 3 3 4 4 5 5 6 4 6 */ -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 20.. OK=true // 4.

nextToken(). cost. m=(int)st.u<=n.j++) w[i][j]=oo. w[i][j]=cost.k<=m.out.v++) if(w[u][v]<oo) relax(u.j<=n.i++) for(j=1. // matricea costurilor static int[] d.v.nval. costul arcului int u.k<=n-1. n=(int)st.nextToken().u++) for(v=1.k++) { st.println(cicluNegativ). st. // d[u]=dist(nods.2. break. i=(int)st.u++) for(v=1.u) static int[] p. st.nextToken(). for(i=1.v<=n.nval. d=new int[n+1].nval.j.nextToken().v).i<=n.20.v<=n. ALGORITMUL BELMANN-FORD static int[][] w.nextToken().nval. w=new int[n+1][n+1]. } System.v)=arc boolean cicluNegativ=false. // initializare ! for(k=1. j=(int)st. cost=(int)st. for(k=1. st.in"))). for(u=1.k++) for(u=1. // nod sursa. int nods=1. // p[u]=predecesorul nodului u 499 public static void main(String[] args) throws IOException { int i.k. st. // // // // de n-1 ori !!! vectorii arcelor erau mai buni ! lista de adiacenta era mai buna ! (u. p=new int[n+1]. .v++) if(w[u][v]<oo) // (u. } init(nods).v)=arc if(d[u]<oo) // atentie !!! oo+ceva=??? if(d[v]>d[u]+w[u][v]) { cicluNegativ=true.u<=n. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("BellmanFordNeorientat.nval.

System. --> k { if(p[k]!=-1) drum(p[k]).k++) { System. if(d[k]<oo) drum(k).u++) { d[u]=oo.out.. }// drum(.. for(u=1. for(i=1. p[v]=u.print(nods+"-->"+k+" dist="+d[k]+" drum: "). System... System. } d[s]=0.500 CAPITOLUL 20.k<=n.out.int v) // (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].) static void afisv(int[] x) { int i.out.print(k+" ").println().) static void drum(int k) // s --> . } }//main static void init(int s) { int u.i++) System. ALTI ALGORITMI ¸ if(!cicluNegativ) for(k=1.println().. } }// relax(.out.u<=n.out. } }//class /* 6 8 1 2 -3 1 3 1 false 1-->1 dist=0 drum: 1 1-->2 dist=-4 drum: 1 3 2 .out.i<=n. p[u]=-1.. else System. }// init() static void relax(int u.print(x[i]+" ").print("Nu exista drum!").

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

print(k+": oo .out.nextToken(). d=new int[n+1]. else System.k++) { if(d[k]<oo) System.out. } topoSort().print("Distante : ").v).nval.nextToken().j++) w[i][j]=oo. System.out. } : "). . w[i][j]=cost. ").v<=n. // lista de adiacenta era mai buna ! System. ALTI ALGORITMI ¸ for(i=1..k++) { st. afisv(lista). drum(k). gi[j]++. System.print(k+" : "+d[k]+" . i=(int)st.k<=n.nval.out. st.println(). } }//main static void init(int s) { int u. p=new int[n+1]. j=(int)st.nval.502 lista=new int[n+1].nextToken().i++) for(j=1. st. // initializare ! for(k=1.v++) if(w[u][v]<oo) relax(u.. for(i=1.j<=n. afisv(d). gi=new int[n+1].print("Lista init(nods). ").. cost=(int)st. CAPITOLUL 20. for(k=1.i<=n.i<=n.i++) { u=lista[i].out. for(v=1..k<=m.

for(v=1. dar . for(k=1. for(i=1. pozl=1.pozl... } d[s]=0.int v) // (u.k++) // pun cate un nod in lista { u=nodgi0(). color[u]=BLACK. } }// topoSort() static int nodgi0() // nod cu gradul interior zero { int v.) static void relax(int u. !!! color[i]=WHITE.) static void drum(int k) // s --> . } }// relax(.v<=n.} return nod..print(k+" "). break.out..2..nod=-1.k. micsorezGrade(u).i++) // oricum era initializat implicit. p[v]=u. p[u]=-1.. ALGORITMUL BELMANN-FORD for(u=1.u<=n. lista[pozl++]=u.i<=n. }// nodgi0() . --> k { if(p[k]!=-1) drum(p[k]).u++) { d[u]=oo..v++) // coada cu prioritati (heap) este mai buna !!! if(color[v]==WHITE) if(gi[v]==0) {nod=v. } 503 static void topoSort() { int u.20. if(d[k]<oo) System.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(..i.k<=n.

) }//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 ... for(i=1..out.print(x[i]+" "). 1 3 4 6 .. for(v=1.. 1 3 2 3 : 1 .out. 1 3 4 : 2 .i<=n. }// afisv(.v++) // lista de adiacenta este mai buna !!! if(color[v]==WHITE) if(w[u][v]<oo) gi[v]--.print("oo ").i++) if(x[i]<oo) System.) static void afisv(int[] x) { int i.out..v<=n.. 6 : 4 .. else System.... 1 2 : -4 .... ALTI ALGORITMI ¸ static void micsorezGrade(int u) { int v. 1 3 4 5: oo .504 CAPITOLUL 20. System..println().. }// micsorezGrade(.

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

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

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

Sign up to vote on this title
UsefulNot useful