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

18 Avere ONI2005 cls 10 . . . . . 18. . . . . . . . .2. . . . . . . . . . . . . 20. 18. . . . . . . . . . . . . .9. . . . . . . . . . . . . . . .OJI2005 clasa a X-a a 16. . . . .1. . . . . . . . . . . . . . . . . . . . . a 20. .2 Alg Belmann-Ford pentru grafuri orientate .7 Dreptunghi minim de acoperire a punctelor . . . . . . . .3 Mo¸ia lui P˘cal˘ . . . . ¸a a a 20. 18. .2. . .2. . . . . . . . . ¸ 16. . . .6. . . . . . . . Runda 6 . . .2. . . . .5 Triunghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Potrivirea ¸irurilor s 17. . . .1 Un algoritm ineficient . . . 18. .1. . .2. . . . . . . . 493 19. . . . . . . . . . . . . . . 493 a 19. . . . . . . . . . . .ONI2005 cls 10 . . . . .1 Prezentare general˘ . . . . . 17. . . . . .1 Prezentare general˘ . . . .ONI2006 baraj . . . . . . . . 18. . . . .4 Pozitia unui punct fat˘ de un poligon convex . .ONI2003 cls 10 . . . . . . . . ¸ ¸a 18.1 Algoritmul Belmann-Ford pentru grafuri neorientate 20. . . . . . . . . . . . . . .6. . . . . . . . . . . . . . ¸ 18. . . . . . .9 Probleme rezolvate . 493 20 Alti algoritmi ¸ 20. . . . .3. .2 Exemple . . . . . . . . .16 Munte .4 Partitie . . . . . . . . . .1 Secvent˘ de sum˘ maxim˘ . . . . . . . . . 18. . . . . . . . . . .2 Algoritmul Belmann-Ford . . . . . . ¸ ¸a 18. .9. . . . . . . . . . . . . . . .19 Suma . . . . . . . .1 Determinarea orient˘rii . .ONI2007 cls 9 . . . . . . .KMP . . . . . . . . . . . . . . .8 Cerc minim de acoperire a punctelor . 18. . . . . 19 Teoria jocurilor 493 19. . . . . . . . . . . . .2 Cifru . . . . . . . .ONI2006 baraj . . . . . . . . .1 Jocul NIM . . .17 L˘custa . . . . . . . . . . . . . . . . . .2 Un algoritm eficient . . . . . . . . .5 Pozitia unui punct fat˘ de un poligon concav . . .Campion 2003-2004 17. .9. . . . . . . .2. . . . . . . . . . . . . . . . . . .2 Antena . . . . . . . . . .OJI2004 clasa a XI-a s a a 18.3 Aria poligoanelor convexe . . . 18 Geometrie computational˘ ¸ a 18. . . at 18.14 Balanta ONI2002 clasa a X-a . . . . . . . . . . .2 Scanarea Craham . . . . . . . . . . . . . . . . . . . . 17. . . . . 20. . . . . . . . .xi 16. .2 Exemple . . . . . . . . . . . . . . 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 . 16. .1 Circular . . . . . . . . . . . . . . 495 495 495 495 495 495 498 . . . . . . . . . . . . . .1. . . .2 Testarea convexit˘¸ii poligoanelor . . . . . . . . . . . .1 ˆ Impachetarea Jarvis . . . . . . Inf˘s a a 18. . . . . . . . 17. . . a 18. . . . . . . . . . . . . . . . . .ONI2005 clasa a IX-a . . .3 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . .ONI2005 clasa a X-a . . . . . .2. . . . . . . . . . . .6 ˆ a¸ur˘toarea convex˘ . . .9. . . . . .2. . . . 18. . . . . . . . . . . . . . . .9. .1 Seceta . . . . . . . . . . . . . . . . . . . . 16. . . . 16. .3. . .1. . . . . . . .15 Aliniere ONI2002 clasa a X-a . . . . . . . . . . . . .

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

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

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

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

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

14 CAPITOLUL 2.. Un vˆrf terminal (sau frunz˘) este un vˆrf f˘r˘ descendenti. . STRUCTURI DE DATE 2.. a s a ˆ altimea arborelui este ˆ altimea r˘d˘cinii. In˘ ¸ ın˘ ¸ a a ˆ Intr-un arbore binar.. Sau. ı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. dac˘ este cazul. a vˆrfurilor ın˘ ¸ a a numerotate cu n + 1. n + 2. 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. Pentru acela¸i ın s nivel. iar vˆrful j se nume¸te a s a s descendent al lui i. 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. echivalent.4 Arbori binari Un arbore este un graf neorientat.3. atunci vˆrful i se nume¸te ascendent al lui j. Vˆrful s a a a a plasat pe nivelul 0 se nume¸te r˘d˘cina arborelui. aciclic ¸i conex. numerotarea se face ˆ arbore de la stˆnga la dreapta. dac˘ se obtine a s ın˘ ¸ a ¸ din arborele binar plin de ˆ altime k.2: Arbore binar plin Un arbore binar cu n vˆrfuri ¸i de ˆ altime k este complet. 2k+1 − 1. un arbore s este un graf neorientat ˆ care exist˘ exact un drum ˆ ın a ıntre oricare dou˘ vˆrfuri. 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. ˆ Intr-un arbore cu r˘d˘cin˘ (reprezentat pe niveluri). Vˆrfurile care a a a aa ¸ a nu sunt terminale se numesc neterminale. 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. se nume¸te arbore plin. a a Un arbore reprezentat pe niveluri se nume¸te arbore cu r˘d˘cin˘. prin eliminarea. num˘rul maxim de vˆrfuri aflate pe nivelul k este 2k . a a Un arbore binar de ˆ altime k are cel mult 2k+1 − 1 vˆrfuri. . a s Varfurile unui arbore plin se numeroteaza ˆ ordinea nivelurilor. iar dac˘ are exact ın˘ ¸ a a 2k+1 − 1 vˆrfuri. Un arbore ˆ care orice vˆrf are cel mult doi descendenti se nume¸te arbore ın a ¸ s binar.

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

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

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

Leapunov. P. V. M e) constituit din urm˘toarele elemente: a M .memorie intern˘ format˘ din locatii de memorie ¸i utilizat˘ pentru a a ¸ s a stocarea temporar˘ a valorilor variabilelor.2 Algoritmi 3. parcurgere a a ˆ adˆncime. R. iar analiza ¸ a semnului discriminantului (Pasul 2) este un exemplu de operatie logic˘. 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”. De. cicluri. ¸ 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. c˘utare binar˘. etc) ori ¸ este asem˘n˘tor cu ceva ˆ a¸at mai ˆ a a ınv˘t ınainte (sortare. ˆ [35] notiunea de algoritm se define¸te astfel: ın ¸ s Un algoritm este sistemul virtual A = (M. aceast˘ descriere apare sub a a a denumirea de algoritm ˆ ”Elementele lui Euclid”. ¸ a Descrierea unui algoritm presupune precizarea datelor initiale ¸i descrierea ¸ s prelucr˘rilor efectuate asupra acestora. S-a demonstrat c˘ aceste definitii sunt ¸ a ¸ echivalente din punct de vedere matematic. M i.2. ALGORITMI 3. ma¸ina ¸ s Turing. Markov. ˆ informatic˘ exist˘ de asemenea mai multe definitii pentru notiunea de In a a ¸ ¸ algoritm. A.1 Ce este un algoritm? Un algoritm este o succesiune de operatii aritmetice ¸i/sau logice care. ¸ s aplicate asupra unor date. functii recursive. De exemplu. ın a ˆ matematic˘ notiunea de algoritm a primit mai multe definitii: algoritmul In a ¸ ¸ normal al lui A. primul a algoritm cunoscut ˆ matematic˘. recursivitate. a . algoritmii nu au a a a ın ¸ a neap˘rat leg˘tur˘ cu calculatorul. Mai tˆrziu. a ın ¸ a Calculul efectuat la Pasul 1 este un exemplu de operatie aritmetic˘. etc). algoritmul operational al lui A.18 CAPITOLUL 3. Astfel. ı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. permit obtinerea rezultatului unei probleme ¸ din clasa celor pentru care a fost conceput. A. Totu¸i. S˘ observ˘m c˘ nu apare ˆ definitie cuvˆntul ”calculator”. Algoritmul lui Euclid pentru ın calculul celui mai mare divizor comun a dou˘ numere naturale este. ˆ acest curs ne vom concentra aproape a a a s ın exclusiv pe algoritmi care pot fi implementati rezonabil pe calculator. Altfel spus. sisteme POST. se pare. Di.

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

C/C++ sau Java.2 Propriet˘¸ile algoritmilor at Principalele propriet˘¸i pe care trebuie s˘ le aib˘ un algoritm sunt: at a a • Generalitate. 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˘. Sunt secvente de prelucr˘ri simple sau structurate ¸ a care sunt efectuate ˆ ordinea ˆ care sunt specificate. ¸a ın Pe de alt˘ parte. Algoritmii au o serie de structuri .3 Descrierea algoritmilor Algoritmii nu sunt programe.2. nu numai pentru o problem˘ particular˘. modul de solutionare a tuturor situatiilor care pot s˘ aa at ¸ ¸ a apar˘ ˆ rezolvarea problemei. Un algoritm trebuie s˘ prevad˘. Dac˘ ˆ cadrul algoritmului nu intervin a ın a ın elemente aleatoare. ın ın − Alternative. eventual prin a evaluarea unor expresii. deci ei nu trebuie specificati ˆ ¸ ıntr-un limbaj de programare. 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.20 CAPITOLUL 3.ˆ special a ın . 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. • Prelucr˘rile structurate pot fi de unul dintre tipurile: a − Liniare. a • Determinism. a • Finitudine. ALGORITMI 3. Din a a a aceast˘ cauz˘. Din aceast˘ cauz˘. o metod˘ de rezolvare a unei ecuatii particulare nu poate a a a ¸ fi considerat˘ algoritm. a a ¸ 3. a ¸ a s 3. f˘r˘ ambiguit˘¸i ¸i a a aa at s f˘r˘ neclarit˘¸i.2. Detaliile sintactice. Un algoritm trebuie s˘ poat˘ fi utilizat pentru o clas˘ a a a ˆ ıntreag˘ de probleme. 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. nu au nici o important˘ ˆ elaborarea/proiectarea algoritmilor. • Prelucr˘rile simple sunt atribuiri de valori variabilelor. a − Repetitive. Orice algoritm trebuie s˘ permit˘ obtinerea rezultatului a a ¸ dup˘ un num˘r finit de prelucr˘ri (pa¸i). 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. de exemplu din Pascal.

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

. Un limbaj algoritmic mai este denumit ¸i pseudocod. ALGORITMI Valorile cn−1 ..3. ın ¸ ın 3. sunt schemele logice ¸i limbajele s s algoritmice. ˆ care apar enunturi de prelucr˘ri. ˆ at ıntre limbajul natural sau cel matematic ¸i un limbaj de programare. n − 2. fac ca schemele logice s˘ fie s ¸ a din ce ˆ ce mai putin folosite (ˆ favoarea limbajelor algoritmice). Schemele logice sunt descrieri grafice ale algoritmilor ˆ care fiec˘rui ın a pas i se ata¸eaz˘ un simbol grafic.. ˆ aceast˘ ordine. iar modul de ˆ antuire s a ınl˘ ¸ a blocurilor este specificat prin segmente orientate. cn−2 . este dificil˘ ˆ ¸ ın a ıntrucˆt nu a sunt pu¸i ˆ evident˘ foarte clar pa¸ii algoritmului. 1.3. s ın ¸a s Modalit˘¸i intermediare de descriere a algoritmilor. 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.2 Scheme logice Scrierea unui program pornind de la un algoritm descris ˆ ıntr-un limbaj mai mult sau mai putin riguros. c0 reprezint˘ coeficientii cˆtului. ca ˆ aa a ın cazul limbajelor de programare. predecesor[v]=-1. 2. 0. a ın a 3. c1 . distanta[v] = infinit..22 CAPITOLUL 3.. .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. numit bloc. ca ˆ exemplele de mai sus. iar ultima valoare a ¸ a calculat˘ reprezint˘ valoarea restului (valoarea polinomului calculat˘ ˆ b). valorile n − 1. fiecare a ın programator putˆnd s˘ conceap˘ propriul pseudocod. 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. De exemplu: ın ¸ a for fiecare vˆrf v din V a { culoare[v] = alb. Se poate folosi sintaxa lima as a bajului de programare preferat. a ın a dar ¸i evolutia modului de concepere a programelor.. cu conditia ca acesta s˘ a a a ¸ a permit˘ o descriere clar˘ ¸i neambigu˘ a algoritmilor. Acest dezavantaj. } . . 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. ın Nu exist˘ un anumit standard ˆ elaborarea limbajelor algoritmice.

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’; } }

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

out"))). sub forma: s PrintWriter out = new PrintWriter( new BufferedWriter( new FileWriter("fis.5. sub forma: <nume>[i] unde i poate lua orice valoare ˆ ıntre 0 ¸i n − 1.print(v). 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.out. specific˘ o matrice cu m linii ¸i n coloane. int vi = StreamTokenizer( BufferedReader( FileReader("fis. ALGORITMI Elementele vectorului pot fi accesate cu ajutorul unui indice.nextToken().readLine()).parseInt(br. Scrierea valorii unei variabile v pe ecran se poate face sub forma: System.in)). (int) st. s 3.nval. sau <tip> <nume> [ ] [ ] = new <tip>[m][n]. . iar ˆ ıntr-un fi¸ier (de exemplu fis. s ˆ cazul tablourilor bidimensionale. out.in).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.out). int vi=Integer.34 CAPITOLUL 3. iar dintr-un fi¸ier (de exemplu fis. sub forma: s StreamTokenizer st = new new new st.close(). out.in"))).print(v). o declaratie de forma: In ¸ <tip>[ ] [ ] <nume> = new <tip>[m][n].

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

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

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

b. return k-1.k++) if (f[k]>nr) break. x=x-y.c=2. ALGORITMI System.println(nrt+" : "+x+" f["+iy+"] = "+y).println("x = "+x). . 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. long[] ss=new long[n+1]. y=f[iy].b=1. a a s ın Rezolvare: 2 class e02 { public static void main(String[] args) { int a. nnp=0. } } De exemplu. nrt++. c. 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. a=1. while(x>0) { iy=maxFibo(x).k<=n. s=-b/a. for(k=1. } } static int maxFibo(long nr) { int k.38 CAPITOLUL 3. n=10. s. p. iar dac˘ nu s a ın a a a a este num˘r prim s˘ se afi¸eze descompunerea ˆ factori.out. 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!).out. k. System.

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

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

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

3. int moda0=Math. while((nr%d!=0)&&(d*d<=nr)) d=d+2.modan=Math.-2.out.42 d=3. 3. if((nr==2)||(nr==3)) return true.alfa++) . ALGORITMI static boolean esteprim(long nr) { if((nr==0)||(nr==1)) return false. R˘d˘cini rationale. int[] a=genPol(p.alfa.q).print(d+" ").out.2. } if(nr!=1) System. } }// class 4.2. q={2. } d=d+2. public static void main(String[] args) { int[] p={1. nr=nr/d. else System.abs(a[0]). while((d*d<=nr)&&(nr!=1)) { while(nr%d==0) { System. Programul care urmeaz˘ genereaz˘ coeficientii ecuatiei. for(alfa=1.1.println(). ¸i apoi determin˘ r˘d˘cinile rationale a a s a a a ¸ class RadaciniRationale // generez p_i/q_i { static int k=0. 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. plecˆnd de la fractii date a a ¸ ¸ a ¸ (ca r˘d˘cini). 1}. if(nr%d==0) return false.3. long d=3.println(nr).-1}.out. int n=a. S˘ se determine toate r˘d˘cinile rationale ale unei a a ¸ a a a ¸ ecuatii cu coeficienti ˆ ¸ ¸ ıntregi.alfa<=moda0.abs(a[n]). if(nr%2==0) return false.length-1. } CAPITOLUL 3.3.beta. else return true.

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

length-1. int n) { int p=1.44 CAPITOLUL 3.println(). ˆ a a ¸ ınmultire ¸i ˆ artire la 2.r.k++) p*=a. for(k=0. return p.k.c. ¸i apoi se fac n ˆ artiri succesive la 2. d=i. // ca sa inceapa while !!! while (r > 0){c=d/i. i=b. ALGORITMI int n=a. for(int k=1. Suma ¸ a a a s trebuie calculat˘ simulˆnd operatiile de adunare.i>=0.n-k).k++) s+=a[k]*putere(alfa.k<=n.k)*putere(beta. a s ımp˘ ¸ class e05 . i=r.} else {d=b.k<=n. System.out.} r=123.} return d.out. }// afisv() }// class 5.i.s=0. int b) { int d.i--) System. cu ¸ s ımp˘ ¸ numere mari. i=a. } static int cmmdc(int a. ¸i anume 2n .print(a[i]+" "). return s. if (a>b) {d=a. } static void afisv(int[] a) { for(int i=a. } static int putere(int a.length-1. Rezolvare: Functia se pune sub forma: ¸ 1 f (n) = n 2 n n 2n−k Cn+k k=0 Se calculeaz˘ suma. 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.

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

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

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

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

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

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

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

Suma ¸ a a a s trebuie calculat˘ simulˆnd operatiile cu numere mari. return rez. ¸i anume nn .. for(i=1.5.i<=k.. 1 − 1 pm .} } 3. a a a s ın 2. 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˘.int b) {. x[i]=x[i]/d. if(y[j]==1) break.i++) rez=inm(rez.nr2v(x[i])).. ¸i anume nn . Suma ¸ a a a s trebuie calculat˘ simulˆnd operatiile cu numere mari.. ALGORITMI } rez=nr2v(1).x[i]). 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. Fie Sn = xn +xn +xn unde x1 . S˘ se calculeze a f (n) = n 1 − 1 p1 1− 1 p2 . x2 ¸i x3 sunt r˘d˘cinile ecuatiei cu coeficienti 3 2 1 ˆ ıntregi ax3 +bx2 +cx+d = 0 (vom considera a = 1!). iar s s a ın a a a dac˘ nu este num˘r prim s˘ se afi¸eze descompunerea ˆ factori. CAPITOLUL 3. a a ¸ 3. y[j]=y[j]/d.52 { d=cmmdc(y[j]. 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˘.8 Probleme propuse s a a ¸ ¸ 1. a a ¸ 4. } static int cmmdc(int a.

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

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

limbaj de programare etc). O important˘ deosebit˘ ˆ rezolvarea acestei ¸ a ¸a a ın probleme o are principiul invariantei. a ın ¸˘ a a Implementarea unui algoritm presupune elementele legate de calculatorul folosit. Datorit˘ principiului invariantei vom exprima a a ¸ eficienta unui algoritm ˆ limitele unei constante multiplicative. ci numai num˘rul lor.Capitolul 4 Analiza complexit˘¸ii at algoritmilor 4. avem o In ıns˘ dependent˘ de dimensiunea cazului studiat. a ¸ ¸ a dar ne intereseaz˘ care ¸i ce sunt operatiile elementare. care la rˆndul lor sunt ¸ a compuse din mai multe operatii elementare. 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. Datorit˘ principiului invariantei nu ¸ a ¸ ne intereseaz˘ timpul de executie a unei operatii 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. ¸a Se pune problema de alegere a unei unit˘¸i de m˘sur˘ pentru a exprima at a a eficienta teoretic˘ a unui algoritm. ˆ ambele cazuri ˆ a. de limbajul de programare si ˆ ındemˆnarea programatorului (cu conditia ca a ¸ acesta s˘ nu modifice algoritmul). Principiul invariantei: dou˘ implement˘ri diferite ale aceluia¸i algoritm ¸ a a s nu difer˘ ˆ eficienta cu mai mult de o constant˘ multiplicativ˘. a 55 . Exist˘ mai multe moduri ˆ care ¸ a ın putem exprima eficienta: prin timpul necesar pentru executia algoritmului sau ¸ ¸ prin alte resurse necesare (de exemplu memoria).1 Scopul analizei complexit˘¸ii at ˆ general exist˘ mai multi algoritmi care rezolv˘ aceea¸i problem˘. 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.

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

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

ˆ t(n) ≤ cf (n). Atunci n→∞ lim f (n) ∈ R+ ⇒ O(f ) = O(g) g(n) f (n) = 0 ⇒ O(f ) ⊂ O(g). ANALIZA COMPLEXITATII ALGORITMILOR 4.1) Rezult˘ c˘ O(f ) este multimea tuturor functiilor m˘rginite superior de un a a ¸ ¸ a multiplu real pozitiv al lui f . 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. acest algoritm necesit˘ un timp ˆ ordinul lui ın a ın f pentru orice functie f : N → R+ pentru care t ∈ O(f ). In Vom c˘uta s˘ g˘sim cea mai simpl˘ functie astfel ˆ at t ∈ O(f ). Principiul invariantei ne asigur˘ c˘ orice implementare a algoritmului necesit˘ ¸ a a a un timp ˆ ordinul lui t. (4. multimea de functii ¸ ¸ ¸ O(f ) = {t : N → R+ |∃c > 0. Mai mult. 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 . t(n) = n2 + 3n + 2. g)) Pentru calculul multimilor O(f ) ¸i O(g) este util˘ proprietatea urm˘toare: ¸ s a a Proprietatea 4 Fie f. g : N → R+ . ın a Fie de exemplu. ∀n > n0 } ı.58 ˘¸ CAPITOLUL 4. pentru valori suficient de mari ale argumentului. ˆ particular t ∈ O(t). g(n) n→∞ lim Reciproca nu este ˆ general valabil˘.2.1 Definire ¸i propriet˘¸i s at Definitia 2 Numim ordinul lui f .2 Notatia asimptotic˘ ¸ a 4. Dac˘ t(n) ∈ O(f ) vom spune c˘ t este de ordinul lui f sau ˆ ordinul lui f . a a ın Fie un algoritm dat ¸i o functie t : N → R+ . ∃n0 ∈ N a. 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.2.

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

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

d∈D .probabilitatea aparitiei datei d ∈ D la intrarea algoritmului ¸ • TA (d) . num˘rul de operatii elementare efectuate de algoritm poate ¸ s a ¸ varia considerabil pentru diferite seturi de date de intrare. 1]. Numero¸i algoritmi ın a ¸ s foarte utili au o comportare convenabil˘ ˆ practic˘.spatiul datelor de intrare ¸ • p(d) .. 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. putem caracteriza exact datele de intrare. Se poate ar˘ta u¸or c˘ Θ(f (n)) = O(f (n)) ∩ Ω(f (n)). Dar chiar dac˘ este cunoscut cazul cel mai defavorabil. Cel mai cunoscut exemplu este algoritmul de sortare rapid˘ (quicksort) care a are complexitatea ˆ cazul cel mai defavorabil de O(n2 ). ak > 0 atunci TA (n) ∈ Θ(nk ). Totu¸i.˘ 4. ın ın Complexitatea ˆ cazul cel mai defavorabil este num˘rul maxim de operatii ın a ¸ elementare efectuate de algoritm. dar pentru datele ˆ alnite ın ıntˆ ˆ practic˘ functioneaz˘ ˆ O(n · log n). + a1 n + a0 . dac˘ a s a a TA (n) = ak nk + ak−1 nk−1 + .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. 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. Dac˘ not˘m: a a • D . Pentru cazuri simple. De asemenea. ı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..2.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. dar foarte proast˘ ˆ cazul a ın a a ın cel mai defavorabil. 4. 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. Din acest motiv Θ se poate numi ordin ¸ exact.

.. g2 (n)}).. atˆt ˆ medie cˆt ¸i ˆ cazul cel mai a ın a s ın defavorabil. Vom estima timpul de executie al algoritmului ˆ functie de n.. 4. an . care are n componente.3. pozmax=1. for i = 2 to n do if a[i] > max then max = a[i]. ¸ ¸ a Deci complexitatea algoritmului este O(n)..2. A2 . s Atunci structura va avea ordinul de complexitate O(max{g1 (n).3 Exemple 4.j { if a[i]>max { a[i]=max. 1 ≤ i ≤ n.n-1.. ¸ a as a Presupunem c˘ structura secvential˘ este constituit˘ din prelucr˘rile A1 . Ak ¸i fiecare dintre acestea are ordinul de complexitate O(gi (n)).2 { max=a[1].2 Sortarea prin selectia maximului ¸ Sort˘m cresc˘tor vectorul a. gk (n)}). a2 .1 Calcularea maximului Fiind date n elemente a1 .. alternativ˘ ¸i repetitiv˘. 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). .. ˆ cazul unei structuri repetitive pentru a determina ordinul de complexitate In ˆ cazul cel mai defavorabil se consider˘ num˘rul maxim de iteratii. ANALIZA COMPLEXITATII ALGORITMILOR 4. 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).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˘... . an }. Fiecare iteratie a ciclului for o vom considera operatie elementar˘. for i=2..3.. a2 . a a for j=n. num˘rul de ¸ ın ¸ a date de intrare.62 ˘¸ CAPITOLUL 4.. 4.3... .. pozmax=i. a max = a[1]... s˘ se calculeze max{a1 . } . a ¸ a a a ...

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

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

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

= +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.. a altfel. pentru fiecare vˆrf din digraf calcul˘m gradul interior a a ¸i gradul exterior.... complexitatea algoritmului este de O(n log n). 0. out=0.n { in=in+a[j][p].. s a a ˆ ıntr-un grup de n persoane pentru care relatiile dintre persoane sunt cunoscute. ’ este o celebritate. dac˘ persoana i cunoaste persoana j.2.. 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 = + = + + = . ¸ Putem reformula problema ˆ limbaj de grafuri astfel: fiind dat un digraf cu ın n vˆrfuri. 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). ˆ medie.n { in=0.3. dar nu a a a cunoa¸te pe nimeni. Dac˘ g˘sim o persoan˘ pentru care out = 0 ¸i in = n−1.. dac˘ exist˘. ın 4. . verificati dac˘ exist˘ un vˆrf cu gradul exterior 0 ¸i gradul interior n − 1. a ¸ a a a s Reprezent˘m graful asociat problemei prin matricea de adiacent˘ an×n a ¸a ai... Se pune problema de a identifica o celebritate.’).5 Problema celebrit˘¸ii at Numim celebritate o persoan˘ care este cunoscut˘ de toat˘ lumea.2. aceasta s a a a s va fi celebritatea c˘utat˘.j = 1. } if celebritate=0 writeln(’Nu exista celebritati !’) else writeln(p.66 ˘¸ CAPITOLUL 4. } if (in=n-1) and (out = 0) celebritate=p. out=out+a[p][j]. for p=1. Cu alte cuvinte. for j=1.. a a celebritate=0.

out=0. f ∈ O(n) =⇒ f 2 ∈ O(n2 ) f ) ∀f : N → R∗ . ˆ acest caz algoritmul a devenit liniar. ˆ final vom avea o singur˘ persoan˘ candidat a a ın a a la celebritate. 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. for i=2.4. y] = 0 ¸i ˆ acest caz y nu are nici o ¸ans˘ s˘ fie celebritate. ¸ a a n3 b) Afirmatia este fals˘ pentru c˘: limn→∞ n2 = ∞ ¸ a a . } if (out=0) and (in=n-1) write(candidat. ’ este o celebritate . y] = 1 ¸i ˆ acest caz x nu poate fi celebritate. s ın Deci la un test elimin˘m o persoan˘ care nu are ¸anse s˘ fie celebritate. PROBLEME 67 Se poate observa cu u¸urint˘ c˘ algoritmul este de O(n2 ). In 4.n if a[candidat][i]=1 candidat=i. 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 Probleme 4.’).’) else write(’Nu exista celebritati. for i=1. singura celebritate posibil˘.4.4. in=0.n { in=in+a[i][candidat]. f ∈ O(n) =⇒ 2f ∈ O(2n ) Rezolvare: n2 a) Afirmatia este adevarat˘ pentru c˘: limn→∞ n3 = 0 =⇒ n2 ∈ O(n3 ). a a s a F˘cˆnd succesiv n − 1 teste.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∗ . out=out+a[candidat][i]. sau s ın s a a a[x. ıl a candidat=1.

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

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

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

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

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

i <= n.1. } return u. v = v0. obtinem c˘ Rn = Rn−1 + Rn−2 pentru n > 1. int i. u = u0 + v0. Rezult˘ Rn = 2 · f ibo(n) ¸i de aici obtinem c˘ Rn = 2 · f ibo(n) − 1. v0 = v. a ¸ a ′ ′ ′ ′ a s ¸ a ¸i R1 = R0 = 2.5. 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 u0. for (i = 2. } . v0. S˘ not˘m prin Rn num˘rul apelurilor functiei a a a ¸ f ibo pentru calculul lui f ibo(n). FUNCTII RECURSIVE ¸ argumentului.. ¸i Rn = 1 + Rn−1 + Rn−2 s ′ ′ ′ pentru n > 1. Evident R0 = R1 = 1. ++i) { u0 = u. v = 1. u = 1. = u0 v0 1 1 1 0 n × 1 0 static int fibo(int n) { int u. v. 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. 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)). Punˆnd Rn = Rn + 1..

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

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. este a ¸ a s a interesant˘ pentru c˘u este evident c˘ o astfel de definitie d˘ d˘ acela¸i rezultat. 6-(i+j).. Pe 3 tije din fata noastr˘. Exemplul clasic este cel al turnurilor din Hanoi. Aceast˘ functie anecdotic˘.2 Proceduri recursive Procedurile. Dac˘ n ≤ 1. 0)? Efectul acestui apel de functie se poate observa din ¸ definitia ei: g(1. 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. Se declan¸eaz˘ la nesfˆr¸it apelul g(1. 0)). j ≤ 3). g(1. 6-(i+j)). Atunci. pot fi recursive ¸i pot suporta apeluri recurs ¸ s sive. la fel ca ¸i functiile.println (i + " -> " + j). exist˘ o solutie foarte simpl˘ pentru mutarea celor n a ¸ a discuri de pe tija i pe tija j: 1. a 3. PROCEDURI RECURSIVE Pentru aceast˘ functie. calculul lui f (96) d˘ a ¸ a f (96) = f (f (107)) = f (97) = . i. Se dore¸te mutarea discurilor pe tija 3. se mut˘ cel mai mare disc de pe tija i pe tija j. Un rationament recura a ¸ siv permite scrierea solutiei ˆ cˆteva rˆnduri. System. g(m-1. a static void hanoi(int n. ¸ s a as calculul nu se va termina niciodat˘! a 5. se mut˘ primele n−1 discuri (cele mai mici) de pe tija i pe tija k = 6−i−j. 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.2. n)). int j) { if (n > 0) { hanoi (n-1. hanoi (n-1. care folose¸te recursivitatea imbricat˘. g(m. ¸ a numerotate 1. ¸ ın a a a a Presupunem problema rezolvat˘ pentru mutarea a n − 1 discuri de pe tija i pe a tija j (1 ≤ i. int i. 2 ¸i 3 de la stˆnga la dreapta. Ce valoare are g(1.5. problema este trivial˘. j). Deci. 0). } } . 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. se mut˘ cele n − 1 discuri de pe tija k pe tija j. a 2. int n) { 0) 1.out. 0) = g(0.. = f (100) = f (f (111)) = f (101) = 91.

un program recursiv de cˆteva a a a a linii poate rezolva o problem˘ apriori complicat˘.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. 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.

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

.. k) sunt distincte.78 CAPITOLUL 6.4) unde coeficientii c1 . k} sunt solutii liniar independente ale relatiei de recurent˘ ¸ ¸ ¸a Dac˘ r˘d˘cinile ri (i = 1. Atunci a a a a ¸ rn . a adic˘ relatia de recurent˘ care define¸te ¸irul numerelor lui Fibonacci....... 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 . .. r2 . F0 = 0..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. nrn . n2 rn .. F1 = 1. introducˆnd expresiile ri ˆ relatia de recurent˘. ¸ ¸a ¸ ¸ 6.... atunci relatia de recurent˘ a a a ¸ ¸a are solutia general˘ ¸ a n n n xn = c1 r1 + c2 r2 + . obtinem: a a n+k n+2 n+1 2 k n n = ri a0 + a1 ri + a2 ri + .1.3) (se mai numesc ¸i sistem fundamental de solutii). + ak rk = 0 (6. 2. ck se pot determina din conditiile initiale.. c2 .1. + cp−1 np−1 rn (6.2) 6. + ak ri = 0 + . + ak ri + a2 ri a0 ri + a1 ri unde xn )|i ∈ {1. + ck rk (6. 2.. ANALIZA ALGORITMILOR RECURSIVI O astfel de formul˘ este de exemplu Fn+2 = Fn+1 + Fn . numit˘ ecuatie caracteristic˘: ¸ a ¸ a ¸ a a0 + a1 r + a2 r2 + . rk sunt r˘d˘cini reale ale ecuatiei caracteristice.....1.1. Ea este o a ¸ ¸a s s relatie de recurent˘ de ordinul 2 cu coeficienti constanti. .. ¸ ¸ ¸ • Ecuatia caracteristic˘ admite r˘d˘cini reale multiple ¸ a a a Fie r o r˘d˘cin˘ multipl˘ de ordinul p a ecuatiei caracteristice.. F˘cˆnd substitutia ¸ ¸˘ a a ¸ xn = rn obtinem urm˘toarea ecuatie.. np−1 rn sunt solutii liniar independente ale relatiei de recurent˘ ¸i ¸ ¸ ¸a s xn = c1 + c2 n + .1.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..5) . . . ¸ ¸ ¸a n ˆ ın ¸ ¸a ¸ Intr-adev˘r..1. . atunci ri sunt a a a ¸ solutii ale relatiei de recurent˘.

+pt = k). RELATII DE RECURENTA ¸ ¸˘ 79 este o solutie a relatiei de recurent˘. r = ae−ib b = 0 ¯ de ordin de multiplicitate k. P ′′ (x). c1 . 4. .. 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. .. atunci solutia general˘ a relatiei de recurent˘ este ¸ a ¸ ¸a xn n n n = c1 r1 + c2 r2 + . x(k) = nk−1 an cos bn... ¸ ¸ • 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˘... rs+2 . . s ¸ ¸ a .. 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.... 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 ..... 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). x(2) = nan cos bn.. Dac˘ sunt precizate conditiile initiale atunci se determin˘ constantele a ¸ ¸ a ¸i se obtine o solutie unic˘.. Se ˆ ınsumeaz˘ ¸i se obtine solutia general˘ ˆ functie de n constante as ¸ ¸ a ın ¸ arbitrare. n n n (k+2) (2k) x(k+1) = an sin bn. Se scrie contributia fiec˘rei r˘d˘cini la solutia general˘.. + cpt −1 npt −1 + unde c1 . xn = nan sin bn. rs ¸i r˘d˘cinile mula ¸ a a a s a a tiple rs1 .... .. + cp1 −1 np1 −1 + . pt (s+p1 +p2 +. x(2) = an sin bn.. ¸ a a a ¸ a 3. cs .. 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. rs+t de multiplicitate p1 .. cp1 −1 ..6. atunci solutiile corespunz˘toare acestora ˆ sistemul ¸ a ın fundamental de solutii sunt ¸ x(1) = an cos bn.. Se determin˘ r˘d˘cinile ecuatiei caracteristice a a a ¸ 2. . cpt −1 sunt constante.. P (p−1) (x).. care se pot determina din conditiile initiale.. ... .1. . xn = nk−1 an sin bn.. (t) (t) (1) c1 + c2 n + .. deci ¸i conjugata r = ae−ib = a(cos b − i sin b) este r˘d˘cin˘ ¸ s ¯ a a a pentru ecuatia caracteristic˘. + cs rs + (1) (1) (1) c1 + c2 n + . p2 .... .. Dac˘ ecuatia caracteristic˘ are r˘d˘cinile simple r1 . Ecuatia caracteristic˘ a a a a ¸ a are coeficienti reali.. . r2 ... c1 .

Intuitiv. a a Generalizˆnd acest procedeu. a a a De exemplu. ˆ timp ce factorul (x − 3) a ap˘rut ca rezultat al a ¸ ¸ ın a calculelor efectuate pentru a sc˘pa de partea dreapt˘. 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. Rescriem recurenta astfel ¸ tn − 2tn−1 = 1 .. o astfel de recurent˘ poate fi: ¸a tn − 2tn−1 = 3n ˆ acest caz.. iar p(n) este un polinom ˆ n de grad d. ˆ In s Inmultim recurenta cu 3. Ideea general˘ a ın a este s˘ reducem un astfel de caz la o form˘ omogen˘. 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. a ¸ a ¸ a ın Vom rezolva acum recurenta corespunzatoare problemei turnurilor din Hanoi: ¸ tn = 2tn−1 + 1. Ecuatia caracteristic˘ este: ¸ ¸a a ¸ a x2 − 5x + 6 = 0 adic˘ (x − 2)(x − 3) = 0.80 CAPITOLUL 6. se poate ar˘ta c˘.2 Ecuatii recurente neomogene ¸ 6. ¸i obtinem ¸ ¸ s ¸ 3tn − 6tn−1 = 3n+1 ˆ Inlocuind pe n cu n + 1 ˆ recurenta initial˘. se procedeaz˘ ca ˆ cazul omogen. + ak tn−k = bn p(n) unde b este o constant˘. b = 3 ¸i p(n) = 1. ANALIZA ALGORITMILOR RECURSIVI 6. observ˘m c˘ factorul (x − 2) corespunde p˘rtii a a a a¸ stˆngi a recurentei initiale. pentru a rezolva ecuatia a a a ¸ initial˘.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 + . n = 1 iar t0 = 0.

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 · . cu b = 1 si p(n) = 1. deducem c˘ c2 > 0. cu solutiile 1 ¸i 2.2.. ¸ ¸ a 6. p1 (n) = n. T0 = 0. Solutia general˘ a a ¸ s ¸ a recurentei este: ¸ tn = c1 1n + c2 2n Avem nevoie de dou˘ conditii initiale. Avem atunci ın a tn ∈ Ω(2n ) ¸i deci. deoarece avem ˆ mod evident tn ≥ n. = 0 . ıi s d2 = 0. 2 1 ˆ care ın pd (n) = nd + c1 nd−1 + .2. d1 = 1 ¸i b2 = 2. Dac˘ ne intereseaz˘ doar ordinul lui tn . tn ∈ Θ(2n ). + cd Ecuatia caracteristic˘ complet˘ este: ¸ a a   k Exemplul 3: Tn = 2T (n − 1) + n + 2n . pentru a g˘si cea de-a a ¸ ¸ ¸ a a doua conditie calcul˘m ¸ a t1 = 2t0 + 1 = 1.. c1 este deci −1... rezult˘ tn ∈ O(2n ). nu este necesar s˘ calcul˘m efectiv a a a a constantele ˆ solutia general˘. Putem obtine chiar ceva mai mult. Ecuatia ¸ caracteristic˘ este atunci (x − 2)(x − 1) = 0.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) + . n ≥ 1. Din conditiile initiale. Stim c˘ t0 = 0.6. p2 (n) = 1. Substituind s ¸ solutia general˘ ˆ ¸ a ınapoi ˆ recurenta initial˘. Acestui caz ˆ corespund b1 = 1. ı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. Dac˘ ¸tim c˘ tn = c1 1n + c2 2n . obtinem ¸ ¸ ¸ tn = 2n − 1. g˘sim ın ¸ ¸ a a 1 = tn − 2tn−1 = c1 + c2 2n − 2(c1 + c2 2n−1 ) = −c1 Indiferent de conditia initial˘. ECUATII RECURENTE NEOMOGENE ¸ 81 care este de forma general˘ prezentat˘ la ˆ a a ınceput...

6. ≤ c·f (n) cu c < 1 atunci T (n) = Θ (f (n)).2. 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 ).6). Solutia dat˘ de teorema master este: ¸ a 1. a a a . dac˘ f (n) = Ω nlogb (a+ε) ¸i a·f a s n b Din p˘cate.3 Teorema master De multe ori apare relatia de recurent˘ de forma ¸ ¸a T (n) = aT (n/b) + f (n) (6.2. dac˘ f (n) = Θ nlogb a atunci T (n) = Θ nlogb a lg n a 3. 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. Pentru a rezolva astfel de ecuatii recurente vom reprezenta arborele generat ¸ de ecuatia recursiv˘.82 cu solutia: ¸ CAPITOLUL 6. dac˘ f (n) = O nlogb (a−ε) cu ε > 0 atunci T (n) = Θ nlogb a a 2. ¸i ea are noduri ¸ a a a ¸ s descendente care sunt noduri r˘d˘cin˘ pentru arborele provenit din T (n/b). ¸i a ¸ a ¸ s multe recurente utile nu sunt de forma (6.2. R˘d˘cina arborelui contine valoarea f (n).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˘). teorema Master nu functioneaz˘ pentru toate functiile f (n). aceasta este o ¸ ıns˘ tehnic˘ de rezolvare a celor mai multe relatii de recurent˘ provenite din metoda a ¸ ¸a Divide et Impera. Din fericire ˆ a.

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

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

deci b = 2. p(k) = k.6. vom nota cu T (n) termenul general al In a recurentei si cu tk termenul noii recurente obtinute printr-o schimbare de variabil˘. 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. 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˘. ¸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. putem rezolva recurente mult mai a ¸ complicate. ¸ ¸ ¸ a Presupunem pentru ˆ ınceput c˘ n este o putere a lui 2. a Deci. n > 1 . ˆ exemplele care urmeaz˘. ecuatiile recurente pot fi aduse la aceast˘ form˘ a ¸ a a printr-o schimbare de variabil˘. n > 1 Facem schimbarea de variabil˘ t(k) = T (2k ) ¸i obtinem: a s ¸ t(k) − 2 · t(k − 1) = k · 2k .2. a Un prim exemplu este recurenta T (n) = 4T (n/2) + n. 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. pentru c˘ cele dou˘ subprobleme s a a au dimensiuni diferite. printr-o schimbare de variabil˘. Aceasta nu se potrive¸te cu teorema master. s Exist˘ ˆ geometria computational˘ o structur˘ de date numit˘ arbore pliat. a s Dac˘ nu au forma standard. O schimbare de variabil˘ aplicabil˘ pentru ecuatii a a a ¸ de recurent˘ de tip multiplicativ este: ¸a n = 2k ⇔ k = log n De exemplu. ¸i √ and metoda arborelui de recursivitate nu obtinem s utilizˆ ¸ decˆt ni¸te margini slabe n << T (n) << n. ECUATII RECURENTE NEOMOGENE ¸ 85 Un argument similar d˘ o ajustare a marginii inferioare T (n) = Ω(n log n). fie T (n) = 2 · T (n/2) + n · log n.

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. n > 1 Procedˆnd la fel. ajungem la recurenta a tk = 4tk−1 + 4k cu ecuatia caracteristic˘ ¸ a ¸i solutia general˘ tk = c1 42 + c2 k42 . tk = c1 4k + c2 2k .86 CAPITOLUL 6. s ¸ a Atunci. ˆ 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 . 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 . 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. T (n) = c1 n2 + c2 n2 lg n ¸i obtinem s ¸ ˆ sfˆr¸it. n > 1 c fiind o constant˘.

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

S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+2 = 2xn+1 − 2xn . c2 = ¸ ¸ a 5 Solutia general˘ este: ¸ a xn = n 1 + 2 5 1 2n − (−3)n . x2 = 4. ¸ s Deci. c2 = ¸ ¸ a Solutia general˘ este: ¸ a xn = 3 2 x0 = 0. 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. S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+3 = 6xn+2 − 12xn+1 + 8xn . s 5 3. x2 = 3. Din conditiile initiale rezult˘ constantele: c1 = 1 . 5 1 2 x0 = 0. ANALIZA ALGORITMILOR RECURSIVI 1 1 obtinem c1 = √5 ¸i c1 = − √5 . ¸i c3 = − 1 . Ecuatia caracteristic˘ corespunz˘toare este: ¸ a a r3 − r2 − 8r + 12 = 0 ¸i are solutiile: r1 = r2 = 2 ¸i r3 = −3. x0 = 0. ¸i c3 = − 1 . x1 = 2. Ecuatia caracteristic˘ corespunz˘toare este: ¸ a a r3 − 6r2 + 12r − 8 = 0 ¸i are solutiile: r1 = r2 = r3 = 2. S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+3 = xn+2 + 8xn+1 − 12xn . 2 2 4. x1 = 2. x1 = 1. 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 . s ¸ Solutia general˘ este de forma: ¸ a xn = (c1 + c2 n + c3 n2 )2n .88 CAPITOLUL 6. Din conditiile initiale rezult˘ constantele: c1 = 0.

. ¸ ¸ a 2 Solutia general˘ este: ¸ a √ n 2 nπ nπ n−1 xn = −2 + cos . 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. ¸ ¸ a Solutia general˘ este: ¸ a xn = √ 2 n sin nπ . c2 = 1 si c3 = 2 .6. xn = 4 n sin nπ . T (0) = 0. + c2 sin 4 4 Din conditiile initiale rezult˘ constantele: c1 = 0 si c2 = 1. s ¸ s Solutia general˘ este de forma: ¸ a xn = c1 2n + c2 √ 2 n x0 = 0. n ≥ 2. x2 = 1. S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+3 = 4xn+2 − 6xn+1 + 4xn . 4 5.3. cos √ nπ 2 + c3 4 n sin nπ . 4 Solutia general˘ este de forma: ¸ a xn = √ 2 n c1 cos nπ nπ . r2 = 2 cos − i sin . x1 = 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 . 4 3 1 Din conditiile initiale rezult˘ constantele: c1 = − 2 . + 3 sin 2 4 4 6. r2 = 1 + i ¸i r3 = 1 − i. Ecuatia caracteristic˘ corespunz˘toare este: ¸ a a r3 − 4r2 + 6r − 4 = 0 ¸i are solutiile: r1 = 2. T (1) = 1.

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

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

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

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. vmax = x[1]. Atunci a ¸ T (n) = 2T (n/2) + 2 ¸i T (2) = 1.1.. Atunci a ¸i b trebuie s˘ satisfac˘ sistemul de ecuatii s a a ¸ 2a + b = 1 an + b = 2(an/2 + b) + 2 93 .Capitolul 7 Algoritmi elementari 7. Se poate mai repede? Da! ˆ artim ¸irul ˆ ¸ Imp˘ ¸ s ın dou˘ ¸i determin˘m vmin ¸i vmax ˆ cele dou˘ zone. 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. Prelucrarea se repet˘ pentru cele dou˘ s a a zone (deci se folose¸te recursivitatea). 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. x[i]) vmax = maxim(vmax. n vmin = minim(vmin.1 Operatii cu numere ¸ 7. x[i]) Evident se fac 2n − 2 comparatii. for i=2.n. Apar cˆte dou˘ comparatii ˆ plus de fiecare s a a ¸ ın dat˘. Compar˘m vmin1 cu vmin2 as a s ın a a ¸i stabilim vminm.

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

} if(n!=1) p=p*2. d=d+2. if(nr%2==0) return false. b = c. Descompunerea ˆ factori a unui num˘r natural n poate necesita ˆ ın a√ ıncercarea tuturor numerelor naturale din intervalul [2.7. Un algoritm eficient pentru calculul cmmdc(a. return p. if(nr<=1) return false. d=3. } while((c=a%b) != 0) { a = b.2 Algoritmul lui Euclid 7.2. b) = 2 ∗ 7 = 14.} return b. } . } 7. ALGORITMUL LUI EUCLID p=p*(1+nd). if(d*d>nr) return true. De exemplu dac˘ a = 1134 = 2 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 7 ¸i b = 308 = a s 2 ∗ 2 ∗ 7 ∗ 11 atunci cmmdc(a. if (a < b) { c = a.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. static int cmmdc(int a. if(nr==2) return true.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.1. b) este algoritmul lui Euclid. while((d*d<=nr)&&(nr%d!=0)) d=d+2. } 95 7.2. int b} { int c. a = b. else return false. b = c. n].

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

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

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

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

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

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

k=0. a2 .102 CAPITOLUL 7. 3}. ın Pentru a genera adunarea ˆ binar. a a ¸ a a pozitia ¸ 1 2 3 4 De exemplu. an }. ¸ unde fiecare component˘ poate avea valori 0 sau 1.. 4} ¸i B = {1.j<n. 2. } return c.i<m. ¸ ¸ O submultime se poate memora sub forma unui vector cu n componente. int[][] c=new int[2][m*n]. int[] b) { int i. este identic˘ cu ¸ a generarea submultimilor multimii de indici {1.j++) { c[0][k]=a[i].4. n=b. 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. 2. ALGORITMI ELEMENTARI Produs cartezian a doua multimi este multimea: A × B = {(x. n}.. Fiecare coloan˘ a matricei contine cˆte un element al produsului a ¸ a cartezian.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. Componenta i are valoarea 1 a dac˘ elementul ai apartine submultimii ¸i 0 ˆ caz contrar. for(i=0. s Generarea tuturor submultimilor ˆ ¸ ınseamn˘ generarea tuturor combinatiilor a ¸ de 0 ¸i 1 care pot fi retinute de vectorul caracteristic V . j. adic˘ 1 + 1 = (10)2 . 2. k++. 3. . pentru A = {1..5 Generarea submultimilor unei multimi ¸ ¸ Generarea submultimilor unei multimi A = {a1 .. static int[][] prodc(int[] a. } De exemplu. m=a.. k.. adic˘ a tuturor numerelor s ¸ a ˆ baza 2 care se pot reprezenta folosind n cifre. vom folosi un vector v valoarea · · · · . . pentru n = 4. 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. O astfel de reprezentare a ¸ ¸ s ın se nume¸te reprezentare prin vector caracteristic.i++) for(j=0. c[1][k]=b[j].length.

i<=n.i++) v[i]=0. for(i=1. de exemplu.i<=n. 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 }. v[i-1]=v[i-1]+1. ¸i. i=n. coloana 5 ¸ s reprezint˘ submultimea {a2 . ¸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. int[] v=new int[n+1]. a a static int[][] submultimi(int n) { int i. } . while(j<nc) { v[n]=v[n]+1. j=0. 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.7. ¸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. ¸i trecem la ordinul urm˘tor s a 0 care nu este permis. while(v[i]>1) { v[i]=v[i]-2. a3 . for(i=1. } return c. i--. Coloana 0 reprezint˘ a ¸ a a multimea vid˘.4.i<=n.i++) c[j][i-1]=v[i]. } for(i=1. j. a4 } iar coloana 7 reprezint˘ submultimea {a2 . coloana F reprezint˘ ˆ ¸ a a ıntreaga multime.i++) nc*=2. int[][] c. c=new int[n][nc]. j++. nc=1.

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

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

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

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

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

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

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

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

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

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

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

. 2... 8. 55.2) (8.3.11) F1 F2 + F2 F3 + . 3.3.. 21. 1597. atunci aceast˘ descompunere este unic˘ abstractie a a ¸ f˘cˆnd de ordinea termenilor.3... + F2n−1 2 2 2 F1 + F2 + . Se poate ar˘ta c˘ a a Fn = 2n 1 √ 5 1+ √ 5 n (8.3. ..3. + F2n−1 F2n = F2n+1 − 1 = F2n = Fn Fn+1 2 = F2n 2 = F2n+1 − 1 (8. Dac˘ nu se folosesc ˆ descompunere numerele F0 ¸i F1 ¸i nici dou˘ a ın s s a numere Fibonacci consecutive.. In a a a .. ca de exemplu: at 1 1 1 0 n = = Fn+1 Fn (−1)n Fn Fn−1 (8. Primele numere Fibonacci sunt: 0.8.3. NUMERE REMARCABILE 115 8. 377. + F2n F2n+1 Teorema 2 Orice num˘r natural n se poate descompune ˆ a ıntr-o sum˘ de numere a Fibonacci. 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.5) (8. 610. numerele naturale pot fi reprezentate asem˘n˘tor a a a reprezent˘rii ˆ baza 2.3.3.8) (8. 987.3) (8. 1. + F2n F1 + F3 + .3.7) (8.3.1 Numerele lui Fibonacci Numerele lui Fibonacci se pot defini recursiv prin: F0 = 0.. Numerele lui Fibonacci satisfac multe identit˘¸i interesante.9) (8. 13. 5. a a Folosind aceast˘ descompunere.. 1.4) (8. Fn = Fn−1 + Fn−2 pentru n ≥ 2. 144.10) (8.3. 89.6) 2 Fn+1 Fn−1 − Fn Fn+m Fnk = Fm Fn+1 + Fm−1 Fn = multiplu de Fk ¸i s F2 + F4 + . 233. + Fn F1 F2 + F2 F3 + .1) − 1− √ 5 n .3.3.. F1 = 1. 34.

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

ca de exemplu: ın num˘rul arborilor binari. n=x. 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. t=a[j][i+j]/10. 1} ¸i x1 + x2 + .i++) { a[j][i+j]=y[j]*x[i]+t. Ele apar ˆ multe probleme. for(i=0. 2n − 1.. t. ..3. m=y. for(n=1.. System.... } } static int[] inm(int[]x. + xi ≥ 0 pentru orice i = 1. for(j=0. NUMERE REMARCABILE 117 se numesc numerele lui Catalan. . int[] x. x2n ) ˆ care xi ∈ {−1. s Numerele lui Catalan sunt solutie a urm˘toarei ecuatii de recurent˘: ¸ a ¸ ¸a Cn+1 = C0 Cn + C1 Cn−1 + .j++) { t=0.length.n++) { x=Catalan(n). 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.. .out.. int[][]a=new int[m][n+m].print(n+" : "). afisv(x). num˘rul de parantez˘ri corecte. num˘rul a ın ¸ a a a segmentelor care unesc 2n puncte ˆ plan f˘r˘ s˘ se intersecteze. ¸i multe altele.. 0) ¸i (n. 2.i<n.j<m. num˘rul ¸irurilor ın aa a a s (x1 .. j. num˘rul modurilor de a a triangulariza un poligon. pentru n ≥ 0 ¸i C0 = 1. n) formate din segmente orizontale ¸i a s s verticale.. x2 .length.int[]y) { int i. + Cn C0 .n<=10. num˘rul drumurilor sub a a a a diagonal˘ care unesc punctele (0.8. + x2n = 0 cu proprietatea ın s x1 + x2 + .. int[]z=new int[m+n].

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

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

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

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

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

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

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

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

ALGORITMI COMBINATORIALI .126 CAPITOLUL 8.

o tabel˘ cu a a a trei informatii: num˘r curent.equals(nume[i])) return telefon[i]. else return 1. if (i < N) return telefon[i]. S˘ se determine p astfel ˆ at x = a[p] sau −1 dac˘ s a ıncˆ a nu exist˘ un element cu valoarea v ˆ a. telefon poate fi organizat˘ cu ajutorul a doi ¸ a a vectori (nume ¸i telefon) iar num˘rul curent este indicele din vector. De exemplu.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. } se poate scrie ¸i sub forma: s static int cauta (String x) { int i = 0.equals(nume[i])) ++i. while (i < N && !x. nume. i < N.2 C˘utarea secvential˘ a ¸ a static int cauta (String x) { for (int i = 0.Capitolul 9 Algoritmi de c˘utare a 9. return 1. } 127 . s a 9. ++i) if (x. 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.

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

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

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

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

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

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

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

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

. a2 . Dup˘ prima parcurgere...136 CAPITOLUL 10. ai+2 . Se fac deci a s a N − i comparatii. aN ) inversˆnd toate perechile de elemente consecutive s a (aj − 1. ın s a . elementul maxim se va afla pe a pozitia N .. 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. (a1 .. Deci. a2 ). . aj ) neordonate. se s a ¸ ¸ pleac˘ de la elementul ai ¸i se compar˘ succesiv cu ai+1 . Se reˆ ¸ ıncepe cu prefixul (a.. ¸i un indice j care permite deplasarea c˘tre marginea i. Procedura corespunz˘toare utilizeaz˘ un indice i care marcheaz˘ sfˆr¸itul a a a as prefixului ˆ sortare. . aN .... a2 . Se ˆ ¸ ıncepe cu i = 1 ¸i se termin˘ cu i = N − 1. + 2 + 1 = N (N − 1)/2 comparatii ¸i N − 1 interschimb˘ri. se fac s a (N − 1) + (N − 2) + . La fiecare iteratie. Principiul ei este de a a ¸ a parcurge ¸irul (a1 .. .. ¸ s a Sortarea prin selectie execut˘ un num˘r de comparatii de ordinul N 2 . ¸ a a ¸ O variant˘ a sort˘rii prin selectie este metoda bulelor... . aN −1 )..

2. 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. for (int i = N1. eventual interschimb˘ri (dac˘. i >= 0. j <= i.10. } } 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). ¸ ın a static void sortBule() { int t. a ¸ s a a tabloul este initial ˆ ordine descresc˘toare). de exemplu. a[j1] = a[j]. i) for (int j = 1. 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 . ++j) if (a[j1] > a[j]) { t = a[j1]. a[j] = t.

de asemenea. f˘r˘ s a a aa s˘ se execute nici o interschimbare de elemente. k. for (int i = N1. k++.138 0 4 1 7 2 3 CAPITOLUL 10. i >= 0. 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 c˘ urm˘toarele parcurgeri se fac. a[j] = t. i) { k=0. s-au mai aranjat totu¸i cˆteva elemente!). la aceast˘ parcurgere s-au mai aranjat a cˆteva elemente!). } } .} if(k==0) break. a a Vectorul este deja sortat. for (int j = 1. O idee bun˘ este introducerea unei a a variabile care s˘ contorizeze num˘rul de interschimb˘ri din cadrul unei parcurgeri. oricum. 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 La urm˘toarea parcurgere nu se efectueaz˘ nici o interschimbare de elemente. j <= i. 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). a[j1] = a[j]. 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. ++j) if (a[j1] > a[j]) {t = a[j1]. Programul modificat este: ¸ a static void sortBule() { int t.

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

spre deosebire de a ¸ s ¸ ¸ primele dou˘ metode de sortare. unde num˘rul de a s a a inversiuni este inv(π). ¸i drept ¸ a In ın s urmare exist˘ putine inversiuni ¸i sunt necesare putine operatii. Deci. ALGORITMI ELEMENTARI DE SORTARE Inserarea lui 30 nu necesit˘ deplas˘ri de elemente. 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. Cea mai bun˘ metod˘ de sortare necesit˘ a a ¸ a a a n log2 n comparatii. j < i} Pentru o permutare π corespunz˘toare unui ¸ir de sort˘ri. ea are o proprietate foarte bun˘: num˘rul de operatii depinde puternic In a a ¸ de ordinea initial˘ din tablou. 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. a Fie ci num˘rul de comparatii. 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(π).140 CAPITOLUL 10. aceast˘ metod˘ de sortare este mai s s a a eficient˘ decˆt sortarea prin selectie. a a De¸i ordinul de cre¸tere este tot N 2 . ¸ ˆ plus. Atunci a ¸ ci = 1 + card{aj |aj > ai . 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. ˆ cazul ˆ care tabloul este aproape ordonat. s .

int x) { int i=u+1.out. } static int pozitiaCautBin(int p. j++) System. else return i.3. if (x>a[i]) p=i+1. Un program complet este: a import java.10. int u. } } . int i) { if (i != k) { int x=a[k].4}. } return p. class SortInsBinApl { static int[] a={3. System. c˘utarea pozitiei pe care se face inserarea se face prin c˘utare a ¸a a ¸ a binar˘.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˘. static void afiseaza() { int j.9.5. j>=i+1.4.*.length. SORTARE PRIN INSERTIE ¸ 141 10.1. j--) a[j]=a[j-1].println().8. } static void deplasare(int k. for(j=0. j<a. while (p <= u) { i=(p+u)/2. else if (x<a[i]) u=i-1.3.io.6. a[i]=x.print(a[j] + " "). for(int j=k. iar ˆ loc s˘ se fac˘ insertia direct˘ ˆ ın a ın a a ¸ a ın aceast˘ secvent˘.out.

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

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

144 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE .

Mai precis. Listele sunt obiecte dinamice.1 Liste liniare Fiecare element al listei este continut ˆ ¸ ıntr-o celul˘ care contine ˆ plus adresa a ¸ ın elementului urm˘tor. Referinta c˘tre prima celul˘ a ¸ ¸a a a ¸ a a este continut˘ ˆ ¸ a ıntr-o variabil˘. Elementele acestui ansamblu pot fi numere ˆ ıntregi sau reale. numit ¸i pointer. obiecte informatice complexe. prin ad˘ug˘ri sau ¸tergeri de elemente pe parcursul ¸ a a s prelucr˘rii. ¸ a independent de natura elementelor sale. ¸iruri de caractere.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. etc. 145 . ˆ sensul c˘ num˘rul elementelor variaz˘ ˆ ın a a a ın cursul executiei programului. 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. a class Lista { int continut. Nu suntem acum interesati s ¸ de elementele acestui ansamblu ci de operatiile care se efectueaz˘ asupra acestuia. Lista urmator. 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.

} .urmator. a). } } CAPITOLUL 11. Lista a) { while (a != null) { if (a. } return false. a = a. } Procedura adauga insereaz˘ un element ˆ capul listei. Func tia Lista(x. ¸i prin absenta numelui de identificare). Lista a) { return new Liste (x.continut == x) return true. 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. Lista a) { continut = x. // capul vechi se va regasi dupa x } cap cap e4 e3 e2 e1 e4 e3 e2 e1 Figura 11.a) construie¸te o nou˘ celul˘ cu cˆmpurile x ¸i ¸ s a a a s a.146 Lista (int x. este suficient˘ a a a a a modificarea valorii capului listei. new Lista(7. urmator = a. a a a s a a a s as static boolean cauta (int x. 7.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. Obiectul null apartine tuturor s ¸ ¸ claselor ¸i reprezint˘ ˆ cazul listelor marcajul de sfˆr¸it de list˘. ceea ce se face simplu prin: static Lista adaug (int x.null))) reprezint˘ lista 2. LISTE Instructiunea new Lista(x. 11. new Lista(11. De asemenea s a ın as a new Lista(2. Variabila a este modificat˘ iterativ prin a=a. care verific˘ dac˘ elementul x este ˆ lista a. efectueaz˘ o parcurgere ¸ a a ın a a listei.1: Ad˘ugarea unui element ˆ list˘ a ın a Functia cauta. a static boolean esteVida (Lista a) { return a == null.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).

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

urmator = elimina (x. return a.continut == x) a = a. else a. este o problem˘ foarte putin critic˘.urmator = b.urmator. pentru calculatoarele de ¸ a a ast˘zi. acest lucru este s ¸ a ¸ mai putin adev˘rat. a a a ¸ static Lista elimina (int x. ocuparea de memorie suplimentar˘.urmator). Lista a) { if (a != null) if (a.continut == x) a = a. } .urmator.2: Eliminarea unui element din list˘ a O procedur˘ iterativ˘ solicit˘ un plus de atentie.urmator. Procedura recursiv˘ de eliminare este foarte a a compact˘ ˆ definitia sa: a ın ¸ static Lista elimina (int x. Lista a) { if (a != null) if (a. LISTE Alegerea ˆ ıntre maniera recursiv˘ sau iterativ˘ este o problem˘ subiectiv˘ ˆ a a a a ın general. 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.urmator != null) b.148 CAPITOLUL 11. } cap cap e4 e3 e2 e1 e4 e3 e2 e1 Figura 11.urmator. Un tratament particular trebuie f˘cut dac˘ elementul care trebuie a a eliminat este primul element din list˘. if (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.continut != x) b = b.urmator. while (b. Gratie noilor tehnici de compilare. else { Lista b = a . } return a.urmator != null && b. a.

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

Elementele a ınl˘ ¸ a a listei sunt intr˘ri care permit accesul la tabloul de nume ¸i numere de telefon. } Exemplu 2. prin ad˘ugarea numerelor de la 2 la n ˆ a a ıncepˆnd cu n ¸i terminˆnd cu a s a 2. a a ¸ a a num˘rul de coliziuni devine important.urmator) { if (x. b=b. 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˘.equals(nume[a.a). } k=a. Aceasta se realizeaz˘ prin procedura urm˘toare: s a ¸ a a static Lista Eratostene (int n) { Lista a=null. i>=2. } return a. --i) { a=adauga(i. int k.continut. for(Lista b=a. a != null.. int val) { int i = h(x).continut]. Dac˘ h este bine aleas˘. Astfel numerele vor fi ˆ list˘ ˆ ordine cresc˘toare.urmator) { k=b. c˘utarea este rapid˘. for(int i=n. ++j) a=elimina(j*k. 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. ınl˘ ¸ a s a a a a a Lista al[] = new Lista[N1]. j<=n/k. al[i]). dac˘ functia h este r˘u aleas˘.continut. } return -1.a).continut])) return telefon[a. } . } static int cauta (String x) { for (int a = al[h(x)]. Tot astfel. for(int j=k. LISTE prima faz˘. 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.n − 1] se construie¸te s o list˘ simplu ˆ antuit˘ format˘ din toate cheile care au h(x) = i. k*k<=n. static void insereaza (String x.150 CAPITOLUL 11. al[i] = adauga (x. Pentru fiecare intrare i din intervalul [0.

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

incrementeaz˘ inceput ¸i cheam˘ posesorul acestui num˘r. se incrementeaz˘ sf arsit ¸i se d˘ acest a s a s a num˘r clientului respectiv. vida = true.plina = false. } . a a class Coada { final static int MaxC = 100.vida = true. info = new int[MaxC]. Pentru gestionarea acestui sistem. int continut[].inceput = 0. sfarsit = 0. c. } static Coada vida(){ return new Coada(). 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. boolean plina. plina= false. Coada () { inceput = 0. int sfarsit.152 CAPITOLUL 11. s a as a • atunci cˆnd sose¸te un nou client.sfarsit = 0. Se spune c˘ este un tablou (sau tampon) a circular. vida. 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. sau coad˘ circular˘. c. } static void facVida (Coada c) { c.vida. int inceput. a • atunci cˆnd functionarul este liber el poate servi un alt client. c. 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. 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. } static boolean esteVida(Coada c) { return c. dac˘ coada a ¸ a nu este vid˘.

} static void adaug(int x..plina = false."). } } inceput sfarsit e1 e2 . c.2. 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˘ ¸ . c.inceput.vida = c.sfarsit] = x.").info[f. } static void elimina (Coada c) { if (c. c.vida) erreur ("Coada Vida.. } private static int succesor(int i) { return (i+1) % MaxC.inceput. Coada c) { if (c.inceput = succesor(c. c. return c.sfarsit).plina) erreur ("Coada Plina.11.sfarsit == c. en Figura 11.vida) erreur ("Coada Vida.inceput].plina = c. c. } static int valoare(Coada c) { if (c.inceput). COZI 153 static boolean estePlina(Coada c) { return c.sfarsit == c.sfarsit = succesor(c.info[c. c.").plina.vida = false.

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

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

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

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

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

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

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

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

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

n1 . nk ın divideEtImpera(P1 . . n. nk . ¸ 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... ˆ englez˘ ın a 163 . . S) if (n ≤ n0 ) then rezolv˘ subproblema P prin tehnici elementare a else ˆ ımparte P ˆ P1 .... S1 ) . Pk de dimensiuni n1 . ın s • Compunerea subsolutiilor pentru a obtine solutia problemei (subproblemei) ¸ ¸ ¸ initiale.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... s a a 1 divide-and-conquer.. divideEtImpera(Pa .Capitolul 12 Algoritmi divide et impera 12. • Rezolvarea ˆ acela¸i mod (recursiv) a tuturor subproblemelor.. 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˘.. Sk ) combin˘ S1 . . Metoda poate fi descris˘ astfel: a procedure divideEtImpera(P..

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

4 pˆn˘ cˆnd scan˘rile se vor ˆ alni pe undeva la mijlocul a s a a a a ıntˆ tabloului. se aplic˘ urm˘torul algoritm: ¸ a a a 1. Dup˘ aceasta. EXEMPLE 165 2. la stˆnga In ın a lui x se vor g˘si elemente mai mici ca ¸i x. 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 .5. 3.3. 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. ˆ acel moment. 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. public static void main(String[]args) { . se aplic˘ acela¸i proces sub¸irurilor de la stˆnga ¸i de la s a a s s a s dreapta lui x. se interschimb˘ ai cu aj a 5.4.5.6.8.1 Sortare prin partitionare . Avem T (n) = O(am ( ba )m ) = O(bkm ) = O(nk ). se repet˘ pa¸ii 2. Rezult˘ c˘ am = bkm = cnk ¸i de aici T (n) = O(nk m) = O(nk logb n).2.3.2. a < bk . la dreapta.3}.Merge sort • dreptunghi de arie maxim˘ ˆ placa cu g˘uri.1. interschimbarea se face a a ıns˘ pe distante mai mari. a a s 3. a = bk .12. elemente mai mari ca a s ¸i x. pˆn˘ cˆnd aceste sub¸iruri sunt suficient de mici (se reduc la a a a s un singur element). class QuickSort { static int x[]={3. avˆnd tabloul a[]. ˆ a din nou. tabloul a[] va fi partitionat ˆ 2 astfel. k 12.4.quicksort Se bazeaz˘ pe metoda interschimb˘rii. etc a ın a 12.3.

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

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

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

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

class Hanoi { static int nrMutare=0. 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˘).170 CAPITOLUL 12. char tijaIntermediara. System. }// main() static void solutiaHanoi(int nrDiscuri. BufferedReader br = new BufferedReader(new InputStreamReader(System.out.in)). 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.*. ’C’).io. public static void main(String[] args) throws IOException { int nrDiscuri. nrDiscuri=Integer. ’B’. ’A’.readLine()). solutiaHanoi(nrDiscuri. char tijaSursa.print("Introduceti numarul discurilor: ").parseInt(br. 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˘. char tijaDestinatie) { if(nrDiscuri==1) .

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

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

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

....’D’. 10 elimin D .’S’}. // nu folosesc d_0 . m=(st+dr)/2..’S’. unde di ∈ {S.174 CAPITOLUL 12. } else // elimin jumatatea dreapta { dr=m. 10 a ramas! 6 .dr=n. "+dr+" elimin "+d[i]). } i++. else impar=false. if((dr-st+1)%2==1) impar=true..m. pˆn˘ cˆnd r˘mˆne un singur a ın a a a a a a element.... static char[] d={’X’. . if(impar) dr--. Se cere acest element.. } }//main }//class 1 . if(d[i]==’S’) // elimin jumatatea stanga { st=m+1. ¸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. class Injumatatire1 { static int n=10. xn ¸i o succesiune de decizii a s s d1 .. x2 ..i=1. una dup˘ alta. d2 . 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˘). .println(st+" .. ALGORITMI DIVIDE ET IMPERA Se consider˘ un ¸ir de numere naturale x1 ..out. D} au urm˘toarea semnificatie: la pasul i.. 10 elimin S 6 .’S’. boolean impar. ! public static void main(String[]args) { int st=1.. a Deciziile se aplic˘ ˆ ordine. while(st<dr) { System.out.println(st+" .. System. "+dr+" a ramas! \n").

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

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

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

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

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

De a s asemenea.2. din pozitia ¸ ¸ curent˘ ˆ oricare dintre cele 8 pozitii ˆ a ın ¸ ınvecinate (pe orizontal˘.1 Romeo ¸i Julieta . . } }//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. System.out.180 CAPITOLUL 13. vertical˘ sau dia a agonale). ın a ¸ Ei se pot deplasa numai prin zonele care sunt marcate cu spatiu.2 Probleme rezolvate 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. ˆ matrice au marcat cu R locul ˆ care se afl˘ locuinta lui Romeo.print(u+" ").OJI2004 clasa a X-a s ˆ ultima ecranizare a celebrei piese shakespeariene Romeo ¸i Julieta tr˘iesc In s a ˆ ıntr-un ora¸ modern.8) = 4 drum : 2 3 4 7 8 13. ˆ 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. comunic˘ prin e-mail ¸i chiar ˆ ¸˘ s˘ programeze. iar ın ın a ¸ cu J locul ˆ care se afl˘ locuinta Julietei. ıntˆ Ei au analizat harta ora¸ului ¸i au reprezentat-o sub forma unei matrice cu s s n linii ¸i m coloane. ALGORITMI BFS-LEE static void drum(int u) // nod_sursa ---> nod_destinatie { if(p[u]!=0) drum(p[u]).

M < 101 • Liniile ¸i coloanele matricei sunt numerotate ˆ s ıncepˆnd cu 1. ¸ − pe fiecare dintre urm˘toarele N linii se afl˘ M caractere (care pot fi doar a a R. plecˆnd de acas˘. Dac˘ exist˘ mai multe solutii. care reprezint˘ num˘rul de linii ¸i a a s respectiv de coloane ale matricei. y .out 4 4 4 . ¸ a Datele de ie¸ire s Fi¸ierul de ie¸ire rj. separate prin spatiu. aa a a a ıntˆ ın a cˆt ¸i Julieta s˘ poat˘ ajunge ˆ acela¸i timp. ei au hot˘rˆt c˘ trebuie s˘ aleag˘ un punct de ˆ alnire ˆ care atˆt Romeo. 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. programul a a ın s a a ¸ trebuie s˘ determine o solutie pentru care timpul este minim.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. ei vor s˘ ˆ aleag˘ pe cel ˆ care timpul necesar pentru a ajunge la punctul a ıl a ın de ˆ alnire este minim. a • Pentru datele de test exist˘ ˆ a ıntotdeauna solutie.2.in 5 8 XXR XXX X X X J X X X XX XXX XXXX rj. a ıntˆ a a − tmin este timpul minim ˆ care Romeo (respectiv Julieta) ajunge la punctul ın de ˆ alnire. ıntˆ Restrictii ¸i preciz˘ri ¸ s a • 1 < N. Si cum probabil exist˘ mai multe puncte de ˆ alnire a a ıntˆ ¸ a ıntˆ posibile. ¸ Exemple rj. avˆnd semnificatia: a ¸ a ¸ − x y reprezint˘ punctul de ˆ alnire (x . J. Fiindc˘ la ˆ alniri a s a a ın s a a a ıntˆ amˆndoi vin ˆ a ıntr-un suflet. 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. X sau spatiu) reprezentˆnd matricea. ı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.num˘rul liniei.in contine: s ¸ − pe prima linie numerele naturale N M . a ¸ Datele de intrare Fi¸ierul de intrare rj.13.num˘rul coloanei).

5).182 CAPITOLUL 13.3). (4. a Se aplic˘ acest algoritm folosind ca puncte de start pozitia lui Romeo ¸i a ¸ s pozitia Julietei. (4. Ginfo nr. Dac˘ o a a pozitie are valoare 3. ALGORITMI BFS-LEE Explicatie: ¸ Traseul lui Romeo poate fi: (1. s a a ıntˆ iar pozitia care are valoare 2 reprezint˘ locul ˆ alnirii. (4.4). unde k reprezint˘ a momentul ˆ care cei doi se ˆ alnesc. (2. Analiza complexit˘¸ii at Ordinul de complexitate al operatiei de citire a datelor de intrare este O(M N ). ¸ Vom folosi o matrice D ˆ care vom pune valoarea 1 peste tot pe unde nu se ın poate trece.4). ˆ ¸ ınseamn˘ c˘ la un moment de timp anterior Julieta se putea a a afla ˆ pozitia respectiv˘. 14/4 aprilie 2004 Problema se rezolv˘ folosind algoritmul lui Lee. 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. ı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. ordinul de complexitate al algoritmului de rezolvare a acestei In probleme este O(kM N ). Dac˘ la pasul k pozitia vecin˘ uneia care are valoarea 3. Vom demonstra c˘ algoritmul este corect prin ıntˆ a metoda reducerii la absurd. ¸ ˆ concluzie. ¸ Ordinul de complexitate al acestui algoritm este O(kM N ). valoarea 3 ˆ pozitia ın ¸ ın a ¸ ın ¸ ˆ care se afl˘ Julieta initial ¸i valoarea 0 ˆ rest. Timpul necesar Julietei pentru a ajunge de acas˘ la punctul de ˆ alnire este deasemenea 4. a ıntˆ Traseul Julietei poate fi: (3. (3. atunci a ¸ a ne vom opri ¸i k reprezint˘ momentul minim de timp dup˘ care cei doi se ˆ alnesc. dac˘ acestea au ın ¸ a valoarea 0. ¸ 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. Timpul necesar lui Romeo pentru a ajunge de acas˘ la punctul de ˆ alnire este 4. valoarea 2 ˆ pozitia ˆ care se afl˘ Romeo initial. 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. are valoarea 2. dac˘ acestea au valoarea 0 sau 3. este punctul cel mai apropiat de ei cu aceast˘ proprietate.3). 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. In a Timp maxim de executare: 1 secund˘/test a Indicatii de rezolvare * ¸ Mihai Stroe.1). 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. . ın ıntˆ Ordinul de complexitate al operatiei de scriere a rezultatului este O(1).2). (4. a ıntˆ ˆ plus 4.4).

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

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

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

in 6 121041 133511 2 2 1 2 1 10 453926 113201 10 2 4 6 5 10 5 22141 sudest. • Pentru fiecare set de date de intrare se garanteaz˘ c˘ exist˘ cel putin un a a a ¸ traseu. Coordonatele scrise pe a a a aceea¸i linie vor fi separate printr-un spatiu. N ). .. • Se consider˘ c˘ robotul strˆnge recolta ¸i din p˘tratul de plecare (1.. ˆ ordine. Primul p˘trat de pe traseu va avea s ¸ a coordonatele 11.out va contine pe prima linie cantitatea maxim˘ s ¸ a de cartofi recoltat˘ de robot. cˆte un p˘trat unitate pe o linie. CK ≤ 10 • Cantitatea de cartofi dintr-un p˘trat de teren este num˘r natural ˆ a a ıntre 0 ¸i s 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 . 1) ¸i din a a a s a s cel de sosire (N. a Exemplu sudest. • Pentru determinarea corect˘ a cantit˘¸ii maxime recoltate se acord˘ 50% a at a din punctajul alocat testului respectiv. a a ın coordonatele p˘tratelor unitate ce constituie traseul pentru care se obtine cantia ¸ tate maxim˘ de cartofi. 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. ALGORITMI BFS-LEE Datele de ie¸ire s Fi¸ierul de iesire sudest. pentru cantitate maxim˘ recoltat˘ ¸i a as traseu corect se acord˘ 100%. Restrictii ¸i preciz˘ri ¸ s a • 5 ≤ N ≤ 100 • 2≤K ≤2∗N −2 • 1 ≤ C1 .. Pe urm˘toarele K + 1 linii vor fi scrise.186 CAPITOLUL 13. iar ultimul va avea coordonatele N N .

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

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

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

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

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

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

. a Se genereaz˘ fiecare din aceste variante. a Indicatii de rezolvare * ¸ 193 Mihai Stroe. 13/6 . GInfo nr.13. a a a ın a Pentru o astfel de matrice. . Ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O(M · N · 2C ). Eventual. a ¸ a aceasta nu mai este abordat˘. se cala a culeaz˘ costul ¸i se transform˘ matricea initial˘ ˆ a s a ¸ a ıntr-o matrice cu 0 ¸i 1. Algoritmul functioneaz˘ datorit˘ restrictiei impuse pentru C (cel mult 10 ¸ a a ¸ numere de marcaj). Operatia de scriere a solutiei are ordinul de complexitate O(M · N ) pentru ¸ ¸ cazul cel mai defavorabil. cea cu num˘r minim de ¸ s ın a camere. Considerˆnd 2C o constant˘. ¸inˆnd cont la fiecare pas de biletele cump˘rate. ordinul de complexitate devine a a O(M · N ). a Evident.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. 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˘). a Se rezolv˘ aceast˘ problem˘ pentru toate cele 1024 variante. problema determin˘rii celui mai scurt drum de la a (1. Pentru fiecare dintre a cele 2C variante. . ˆ caz de egalitate. dac˘ a a a a la un moment dat exist˘ o solutie cu un cost mai bun decˆt al variantei curente. g˘sirea drumului minim cu algoritmul lui Lee are complexitatea a O(M · N ). PROBLEME REZOLVATE muzeu.2.. cum algoritmul ¸ a t a a lui Lee este folosit de obicei pentru o matrice cu 0 ¸i 1. N ) se rezolv˘ cu algoritmul lui Lee. problema determin˘rii celui mai scurt drum se poate rezolva ¸i pe a s matricea initial˘. Pentru o astfel de variant˘.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.. biletul 10) este 210 = 1024. biletul 2. 1) la (M. Analiza complexit˘¸ii at Operatia de citire a datelor are ordinul de complexitate O(M · N ).in muzeu. Se alege solutia de cost minim ¸i. ¸ Fie C num˘rul de numere de marcaj diferite din matrice.

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

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

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

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

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

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

// pozitie musca(lin. . Codul surs˘ a import java.col) int[] qi=new int[qdim]. nici un fir rupt fire putine rupte.t2.n. dimensiune mare traseu ˆ spiral˘ solutie unic˘ ın a a traseu scurt. // coada pentru j din pozitia (i. stanga=8. static int[][] p=new int[140][140]. // dimensiune coada circulara static final int sus=1. sc. jos=4. // 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.cm. greu de g˘sit cu backtracking a multe fire rupte. dreapta=2. // dimensiunile plasei int lp. // coada pentru i din pozitia (i.200 CAPITOLUL 13. // costul ajungerii in (i. afisSolutia(). traseul pe frontier˘ a o ”barier˘” pe mijlocul tablei.currentTimeMillis(). // directii pentru intoarcere de la musca! static static static static static static int m. iar ˆ ¸ ¸ ımbun˘t˘¸it a at maxim 30 de puncte. drum ˆ ”serpentine” ın firele interioare rupte. matriceCosturi(). 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 lm. t1=System. // plasa static int[][] c=new int[140][140].*.j) static int[][] d=new int[140][140]. ALGORITMI BFS-LEE TESTE Obs dimensiune mic˘. // pozitie paianjen(lin.currentTimeMillis().io. foarte multe fire rupte ¸ a caz particular. dimensiune mare ¸ multe fire rupte. fire putine rupte a ¸ solutie unic˘.j) int ic. t2=System. citescDate().j) int[] qj=new int[qdim].cp. // inceput/sfarsit coada public static void main(String[] args) throws IOException { long t1.

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

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

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

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

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

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

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

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

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

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

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

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

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

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 ¸ . multimea candidatilor selectati este vid˘. Dac˘. ¸ Un algoritm greedy construie¸te solutia pas cu pas. 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.214 CAPITOLUL 14. Dac˘. elimin˘m ultimul candidat ad˘ugat. 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. nu neap˘rat optim˘. 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. s ¸ Initial. c˘ut˘m o solutie posibil˘ care s˘ a a ¸ a a optimizeze valoarea functiei obiectiv. a problemei ¸ a a a • o functie care verific˘ dac˘ o multime de candidati este fezabil˘. ¸ ¸ a a a acesta nu va mai fi niciodat˘ considerat. De fiecare dat˘ cˆnd l˘rgim multimea candidatilor selectati. ˆ ıncerc˘m s˘ ad˘ug˘m la aceast˘ multime pe cel mai promitator a a a a a ¸ ¸˘ candidat. METODA OPTIMULUI LOCAL . conform functiei de selectie. dup˘ ad˘ugare. etc.2 Algoritmi greedy Algoritmii greedy sunt ˆ general simpli ¸i sunt folositi la rezolvarea unor ın s ¸ probleme de optimizare. nu neap˘rat optim˘. Dac˘ algoritmul a a ¸ ¸ a a greedy functioneaz˘ corect. multimea de a a a a ¸ candidati selectati este fezabil˘.) ¸ ¸ a a • o functie care verific˘ dac˘ o anumit˘ multime de candidati constituie o ¸ a a a ¸ ¸ solutie posibil˘. lungimea drumului pe care l-am a g˘sit. ˆ cele mai multe situatii de acest fel avem: In ¸ • o multime de candidati (lucr˘ri de executat. prima solutie g˘sit˘ va fi considerat˘ solutie optim˘ a ¸ a ¸ a a a ¸ a problemei. dup˘ o astfel de ad˘ugare.GREEDY 14. 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˘. vˆrfuri ale grafului. multimea ¸ ¸ a a a ¸ de candidati selectati nu mai este fezabil˘. ¸ ¸ ¸ ¸ a La fiecare pas. ultimul candidat ad˘ugat va r˘mˆne de acum ¸ ¸ a a a a ˆ ıncolo ˆ ea. verific˘m ın a a a ¸ ¸ ¸ a dac˘ aceast˘ multime constituie o solutie posibil˘ a problemei.

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

. 1. ¸ s a ın ˆ Intr-o astfel de aranjare a textelor pe band˘.. Rezult˘ c˘ permutarea identic˘ corespunde unei plas˘ri optime.. ¸ a s ın 14.. Atunci cˆnd este necesar˘ a a a a a citirea unui text sunt citite toate textele situate ˆ ınaintea lui pe band˘. ≤ Ln atunci plasarea textelor corespunz˘toare ¸ a a permutarii identice este optim˘. . T2 . . a Demonstratie: Fie p = (p1 .. a a a a a f (p′ ) − f (p) = (Lp(j) − Lp(i) )(j − i) ≤ 0.GREEDY else Gp xi ← gi for j = i + 1.Tp(n) . Ln .. pe o singur˘ band˘ suficient de lung˘. METODA OPTIMULUI LOCAL . p2 . 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˘. trecem a a a ¸ de la o permutare optim˘ la alt˘ permutare optim˘ pˆn˘ ajungem la permutarea a a a a a identic˘... Aplicˆnd de un num˘r finit de ori acest rationament. pn ) o plasare optim˘. 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... 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.. L2 . ..3. 2. . n xj ← 0 Vectorul x are forma x = (1. s Propozitia 1 Procedura rucsac() furnizeaz˘ o solutie optim˘. xi . a Modalitatea de plasare a celor n texte pe band˘ corespunde unei permut˘ri p a a a multimii {1.2 Problema plas˘rii textelor pe o band˘ a a S˘ presupunem c˘ trebuie s˘ plas˘m n texte T1 ... de exemplu..216 CAPITOLUL 14. de lungimi date a a a a L1 .. 1] ¸i 1 ≤ i ≤ n. . n}... 0.. . 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. ˆ [25] la pagina 226. a Propozitia 2 Dac˘ L1 ≤ L2 ≤ . 0) cu xi ∈ (0. textele fiind a¸ezate pe band˘ ˆ ordinea Tp(1) Tp(2) ... Tn .. ¸ a ¸ a Demonstratia se g˘se¸te.. 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˘.

S˘ se ¸ a a .. deci ˆ a s a ınc˘ n−i texte (demonstratiile se pot consulta. Putem presupune acum a a ¸ c˘ a1 ≤ a2 ≤ .5 Problema statiilor ¸ Se consider˘ o multime de numere naturale A = {a1 . . a ¸i a¸a mai departe. . xn } a lui B astfel a ¸ ˆ at valoarea expresiei E = a1 · x1 + a2 · x2 + an · xn s˘ fie maxim˘. Ln ... i + 2m. ¸i a¸a a s s mai departe. xn−1 = bm−1 .... xn−n2 +1 = bm−n2 +1 ..... ıncˆ a a Vom sorta cresc˘tor elementele celor dou˘ multimi.. b2 ..3.. 14.. s xn1 = bn1 ¸i xn = bm .. L2 .4 Maximizarea unei sume de produse Se dau multimile de numere ˆ ¸ ıntregi A = {a1 .... pe m benzi suficient de lungi. x2 = b2 . x2 . . s unde n ≤ m.14.. 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. xn−1 = bm−1 .. ≤ bm . ≤ an ¸i b1 ≤ b2 ≤ . . an } care reprezint˘ a ¸ a coordonatele a n statii pe axa real˘. vor fi plasate textele i + m.... ≤ Ln . < an .. S˘ se determine o submultime B ′ = {x1 . ın a ˆ ıncepˆnd cu primul text (cu cea mai mic˘ lungime) care se a a plaseaz˘ pe prima band˘. .3. . Tn . Atunci cˆnd este necesar˘ citirea unui a a text sunt citite toate textele situate ˆ ınaintea lui pe banda respectiv˘. de exemplu. a s Dac˘ toate elementele din A sunt pozitive vom lua xn = bm . ˆ [25]). x2 = b2 . 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 . . 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. bm }. iar mai departe a a • fiecare text se plaseaz˘ pe banda urm˘toare celei pe care a a a fost plasat textul anterior... s s Dac˘ toate elementele din A sunt negative vom lua x1 = b1 .. Presupunˆnd c˘ L1 ≤ L2 ≤ . a2 ..3..... a2 . ¸ ın m 14.. . T2 . an } ¸i B = {b1 . Vom presupune a1 < a2 < . 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 . .. de lungimi date a a a a L1 . pe aceea¸i band˘ cu el.3 Problema plas˘rii textelor pe m benzi a S˘ presupunem c˘ trebuie s˘ plas˘m n texte T1 .. EXEMPLE 217 14.3.

ˆ configuratia final˘. vom repeta acest rationament pˆn˘ cˆnd a a ¸ ¸ a a a pozitiile cutiilor goale. METODA OPTIMULUI LOCAL .. O mutare se poate face dintr-o anumit˘ s ın a a pozitie numai ˆ pozitia liber˘.218 CAPITOLUL 14..3. Pentru aceste statii ¸ ¸ ¸ a ¸ ¸ repet˘m strategia sugerat˘ de propozitia anterioar˘. ai2 . ˆ cele dou˘ configuratii.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. a ¸ a ¸a a Rezolvarea problemei este urm˘toarea: a • se alege statia 1. . ¸ • 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. se afl˘ num˘rul 3. Statia i1 se poate ˆ ¸ 1 pentru c˘ |ai2 − a1 | = |ai2 − ai1 + ai1 − a1 | = |ai2 − ai1 | + |ai1 − a1 | > d. ¸ ¸ a ıl a ¸ s ıl a ¸ acum. ¸ ı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. coincid. aim } o solutie a problemei care nu contine ¸ ¸ ınlocui cu statia ¸ statia 1 (deci ai1 > a1 ). 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. a a a ¸ Propozitia 3 Exist˘ o solutie optim˘ care contine statia 1. se repet˘ acest ¸a ¸ ¸a ¸ a a pas pˆn˘ cˆnd s-au verificat toate statiile. ¸ a ¸ a ¸ ¸ ¸ ¸ Demonstratie: Fie B = {ai1 . Evident |ai2 − ai1 | ≥ d.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 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. cutia goal˘ se afl˘ pe pozitia 1.. a a ¸ a 14. ¸ ı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 → × × .

. b2 ].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.3. ıncˆ a a Metoda de rezolvare este urm˘toarea: se sorteaz˘ intervalele cresc˘tor dup˘ a a a a cap˘tul din dreapta. unul a a . . 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˘.8 Problema intervalelor disjuncte Se consider˘ n intervale ˆ a ınchise pe axa real˘ [a1 . a a 14. Se a cere selectarea unor intervale disjuncte astfel ˆ at num˘rul acestora s˘ fie maxim. b1 ]. 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...14. 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. a Observatie: Ultimele elemente din fiecare sub¸ir (care sunt ¸i elemente maxime ¸ s s din aceste sub¸iruri) formeaz˘ un ¸ir descresc˘tor.3. EXEMPLE 1 2 modificat → 1 2 final → 1 2 rezolvat → × × Acum vom c˘uta o cutie a cutia goal˘.3. bn ]. dac˘ exist˘ mai multe sub¸iruri la care se ¸ a a s poate ad˘uga xi . [an . se selecteaz˘ primul interval. se va alege acela care are cel mai mare ultim element. se parcurg intervalele. 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 . ¸ ¸ 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. [a2 .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

aux=hd[fiu].print(k+" "). fiu=tata. tata=fiu/2.out..3.) static void drum(int k) // s --> . EXEMPLE 241 nod=hnod[nodh]. } }//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..14 Urgenta . while((tata>0)&&(hd[fiu]<hd[tata])) { pozh[hnod[tata]]=fiu. ¸a ¸ at a ˆ acest scop au identificat N puncte de interes strategic ¸i le-au numerotat distinct In s de la 1 la N ..3. fiu=nodh. hd[fiu]=hd[tata]. Punctele de interes strategic sunt conectate prin M c˘i de acces avˆnd a a . } // urcInHeap(. hd[tata]=aux. System. tata=fiu/2. hnod[fiu]=hnod[tata]. } pozh[nod]=fiu.14. --> k { if(p[k]!=0) drum(p[k]).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˘.. hnod[fiu]=nod.

kC hC .ˆ ıntre punctele i2 ¸i j2 exist˘ o cale de acces de prioritate p2 s a . ˆ care punctele de interes strategic s˘ fie ˆ artite ˆ a ın a ımp˘ ¸ ıntr-un num˘r de K a grupuri.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. programul va determina una singur˘.OUT va avea urm˘torul format: s s a gravmax .num˘rul de c˘i de acces ˆ a a ıntrerupte de calamitate k1 h1 . a Un grup de puncte poate contine ˆ ¸ ıntre 1 ¸i N puncte inclusiv.ˆ ıntre punctele i1 ¸i j1 exist˘ o cale de acces de prioritate p1 s a i2 j2 p2 . 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˘. 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˘. iM jM pM . a a ¸ a Exemplu . Date de intrare Fi¸ierul de intrare URGENTA. s Dac˘ exist˘ mai multe solutii.ˆ ıntre punctele k1 ¸i h1 a fost ˆ s ıntrerupt˘ calea de acces a k2 h2 .242 CAPITOLUL 14. METODA OPTIMULUI LOCAL .ˆ ı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.GREEDY priorit˘¸i ˆ functie de important˘.ˆ ıntre punctele k2 ¸i h2 a fost ˆ s ıntrerupt˘ calea de acces a .. 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..IN are urm˘torul format: s a N M K i1 j1 p1 . ˆ 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˘...

min. // distante de la nod catre arbore static int[] a1. n=(int)st. EXEMPLE URGENTA. // a1[k]=costul muchiei k din arbore public static void main(String[]args) throws IOException { int nodStart=3. ncc=(int)st.nval. costArbore=0.14.nval.io. st. PrintWriter out= new PrintWriter( new BufferedWriter(new FileWriter("urgenta.ncc.nextToken().nval. .costmax.aux.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. j.3. st.nrm.. StreamTokenizer st= new StreamTokenizer( new BufferedReader(new FileReader("urgenta.IN 7 11 4 121 132 173 243 342 351 361 375 455 564 673 URGENTA.gravmax.*. static int n.nextToken(). // arbore minim de acoperire: algoritmul lui Prim O(n^2) class Urgenta // sortare O(n^2) . st. si una slaba merge! { static final int oo=0x7fffffff. // a2[k]=varful 2 al muchiei k din arbore static int[] ac.in"))). // predecesor in arbore static int[] d.jmin=0.m. static boolean[] esteInArbore. m=(int)st.out"))). k. // nod start int i.nextToken(). // ncc = nr componente conexe static int[][] cost. static int[] p.. // a1[k]=varful 1 al muchiei k din arbore static int[] a2.

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

aux=a1[i]. // deocamdata. } //System.i++) if(ac[i]<ac[i+1]) { aux=ac[i]..i<=n-1. . EXEMPLE p[j]=jmin. for(i=ncc.14. a2[i+1]=aux.. ac[k]=cost[i][p[i]]. aux=a2[i]. } } }//for k k=0. for(i=1.println("cost="+costArbore). a2[i]=a2[i+1].i++) { cost[a1[i]][a2[i]]=cost[a2[i]][a1[i]]=oo. ac[i+1]=aux. 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.println("gravmax ="+gravmax). a1[k]=i. // sterg muchiile ramase in arbore .out. a1[i+1]=aux.. //System. for(i=1. gravmax=costmax-costArbore. //sterg .k<=n-1.println(i+" "+p[i]+" --> "+cost[i][p[i]]).3. } } // primele ncc-1 . k++.k++) // de n-1 ori (bule) { for(i=1..out.out.i<=n. a1[i]=a1[i+1]..i<=ncc-1..i++) gravmax+=ac[i].i<=n-2.i++) if(p[i]!=0) { //System. a2[k]=p[i]. ac[i]=ac[i+1].

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

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

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

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

GREEDY 14. Acum interveniti dumneavoastr˘ ˆ poveste.in contine un num˘r ˆ s ¸ a ıntreg L reprezentˆnd luna gimea minim˘ 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. iar s s a a dumneavoastr˘ trebuie s˘-i trimiteti cˆt mai repede numerele cu care poate obtine a a ¸ a ¸ combinatia magic˘. pentru i cu valori de la a a ¸ 0 la 9. aflati dou˘ numere palindroame cu care se poate a ¸ a obtine combinatia magic˘. iar aceast˘ sum˘ este combinatia magic˘ ce va ıncˆ a a a a ¸ a deschide u¸a. s ¸ a astfel ˆ at suma lor s˘ fie minim˘.3.16 Pal . iar cel de-al doilea a a a ¸ poate avea orice lungime diferit˘ de 0. ¸ ¸ a Date de intrare Prima linie a fi¸ierului pal. ¸ a Cerint˘ ¸a Avˆnd datele necesare.out contine primul num˘r palidrom. s Primul num˘r palindrom trebuie s˘ aib˘ cel putin L cifre. Numerele palindroame formate nu pot a ˆ ıncepe cu cifra 0. fiind prietenul s˘u ¸ a ın a cel mai priceput ˆ algoritmi. ın Prin noul super-telefon al s˘u.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. 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.250 } } CAPITOLUL 14. iar a a primul num˘r s˘ aib˘ cel putin L cifre a a a ¸ . ¸ 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. iar s s ¸ a cea de-a doua linie contine cel de-al doilea num˘r palindrom. Algorel poate evada dac˘ g˘se¸te combinatia magic˘ cu care poate deschide a a s ¸ a poarta turnului. Dac˘ exist˘ mai ¸ a a a multe solutii se va scrie doar una dintre ele. 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. Date de ie¸ire s Prima linie a fi¸ierului de ie¸ire pal. METODA OPTIMULUI LOCAL .

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

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

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

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

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

2j+1) va contine pe linia 2j trei numere ¸ ¸ ¸ naturale p xj yj . 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. astfel: fiecare pereche de linii (2j.256 CAPITOLUL 14. 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. pentru orice j. pentru orice i.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. 1 ≤ i ≤ n • 0 ≤ xj ≤ yj ≤ 250. s˘ se determine locul a a ¸ ¸ s ¸ a (locurile) unde vor fi pu¸i detinutii s˘ sape ˆ s ¸ ¸ a ıntr-o zi de munc˘. 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. respectˆndu-se a a pretentiile lor. Pe fiecare s a ¸ ¸ dintre urm˘toarele n linii exist˘ cˆte dou˘ numere naturale. 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. ai bi . METODA OPTIMULUI LOCAL . separate printra a a a un spatiu (ai ≤ bi ). Pe urm˘toarele 2k linii vor fi descrise locurile unde vor fi pu¸i s˘ sape a s a detinutii. a ¸ a Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ n ≤ 10000 • 0 ≤ ai ≤ bi ≤ 250. ordonate cresc˘tor. 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 . astfel ˆ at num˘rul de paznici necesari pentru a p˘zi cei n detinuti ¸ ıncˆ a a ¸ ¸ s˘ fie minim. care reprezint˘ pretentia detinutului. ¸ ¸ a ın a a separate prin cˆte un spatiu. ¸ ¸ ın ¸ Date de intrare Fi¸ierul de intrare sant.

s iar detinutul p˘zit este 5 ¸ a !Solutia nu este unic˘! ¸ a Timp maxim de executie/test: 1 secund˘ (Windows).descriere solutie ¸ ¸ Solutia comisiei ¸ Problema cere. de fapt. 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 . iar detinutii p˘ziti sunt 1.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. 2. determinarea num˘rului minim de intersectii ˆ a ¸ ıntre segmentele determinate de kilometrul minim ¸i maxim ˆ s ıntre care sap˘ un detinut. iar detinutul p˘zit este 1. iar detinutii p˘ziti a ¸ ¸ a ¸ sunt 2 ¸i 4. ¸ ¸ a ¸ s paznicul 2 va p˘zi ˆ a ıntre borna 27 ¸i borna 28. 0. 3 ¸i 4.3.in 3 0 20 8 13 30 60 4 10 203 2 53 30 403 5 7 33 .14.5 secunde (Linux) ¸ a Indicatii de rezolvare . iar s detinutul p˘zit este 3 ¸ a sunt necesari 3 paznici: paznicul 1 va p˘zi ˆ a ıntre borna 10 ¸i borna 20. s ¸ a paznicul 2 va p˘zi la borna 5. iar detinutii p˘ziti sunt 1 ¸i 2. 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. paznicul 3 va p˘zi ˆ s a ıntre borna 30 ¸i s borna 40. EXEMPLE . s ¸ ¸ a ¸ s paznicul 2 va p˘zi ˆ a ıntre borna 30 ¸i borna 60.

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

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

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. 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˘”). cˆte una pentru fiecare In a a s a a dintre cei n senatori ai Republicii. ın s ¸ s˘ fac˘ economii cˆt mai ˆ a a a ınsemnate. ˆ calculul costului total de transport.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 s ¸a Date de intrare Fi¸ierul cezar. O leg˘tur˘ a s a a a a a este direct˘ dac˘ ea nu mai trece prin alte a¸ez˘ri senatoriale intermediare.GREEDY 14. 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.18 Cezar . ˆ plus. 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. a a La alegerea sa ca prim consul. METODA OPTIMULUI LOCAL . 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˘. ei se ¸ a s ¸ In deplaseaz˘ cu lectica. ˆ ıntre oricare dou˘ a¸ez˘ri existˆnd leg˘turi directe sau indirecte. A¸ez˘rile senatoriale sunt numerotate de la 1 s a la n.260 CAPITOLUL 14. a a Toti senatorii trebuie s˘ participe la ¸edintele Senatului. a a Date de ie¸ire s Pe prima linie a fi¸ierului cezar. 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. ˆ acest scop. metroul sau teleportarea nefiind posibile la acea vreme). Problema este de s a a alege cele k str˘zi ¸i amplasarea sediului s˘lii de ¸edinte a Senatului astfel ˆ at. a a a a ¸ reprezentˆnd numerele de ordine a dou˘ a¸ez˘ri senatoriale ˆ a a s a ıntre care exist˘ strad˘.OJI2007 cls 11 ˆ Roma antic˘ exist˘ n a¸ez˘ri senatoriale distincte. a s a s ¸ ıncˆ prin folosirea transportului gratuit. 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. s˘ poat˘ folosi lectica gratuit˘ f˘r˘ a pl˘ti. ˆ drumul lor spre sala de ¸edinte. pentru In toti senatorii. senatorii. 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. Restrictii ¸i preciz˘ri ¸ s a .3. 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˘.

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

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

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

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

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

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

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

GREEDY .268 CAPITOLUL 14. METODA OPTIMULUI LOCAL .

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

. ¸ a 0 1 2 3 4 5 0 0 0 0 1 . 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... 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 ... 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 . pe aceast˘ pozitie k. pentru n = 4). n n+1 Folosim o variabil˘ k pentru a urm˘ri pozitia din vector pe care se schimb˘ a a ¸ a valorile. . valorile se schimb˘ crescˆnd de s a ¸ a a la min c˘tre max.. pe care putem s˘-l afi¸am. Se ajunge astfel la configuratia (k = 4 = n aici!) a ¸ 0 1 2 3 4 5 0 0 0 9 0 1 ... de gaze.. n n+1 . Este a a util˘. .. .. atunci am descoperit singuri aceast˘ metod˘! a a Pe pozitia k = n... . a s˘ 0 1 2 3 4 5 0 0 0 0 0 1 . .. s 0 1 2 3 4 5 0 1 .. imaginea kilometrajelor de ma¸ini sau a contoarelor de a ın s energie electic˘. odat˘ cu aparitia valorii 9 = max. an ) ¸i vom atribui fiec˘rei componente ai valori cuprinse ˆ s a ıntre min ¸i max... .. a ın Este totu¸i util˘ initializarea gol=min-1. ... 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˘). 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. ˆ acest scop folosim variabila gol care a a ın a In poate fi initializat˘ cu orice valoare ˆ ¸ a ıntreag˘ care nu este ˆ intervalul [min.. max]..270 CAPITOLUL 15. La ˆ ınceput k = n ¸i.. n n+1 Ei bine.. s-au epuizat toate ¸ a ¸ valorile posibile. n n+1 0 1 2 3 4 5 0 0 0 2 0 1 . 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. ˆ acest moment. . METODA BACKTRACKING (de exemplu... etc.. a2 .. deci k = k − 1 (acum k = 3). de ap˘. n n+1 Trebuie s˘ construim urm˘toarele elemente ale produsului cartezian.

. se face pasul spre dreapta (k = k + 1)... deci k = k + 1 0 1 2 3 4 5 0 1 0 1 . .... n n+1 Aici (dac˘ nu am ie¸it ˆ afara vectorului... S˘ consider˘m c˘ s-a ajuns la configuratia a a a ¸ 0 1 2 3 4 5 0 0 9 9 0 1 . .. se plaseaz˘ prima valoare valid˘ (deci ın s ¸ a a a valoarea min).. deci k = k − 1 a 0 1 2 3 4 5 0 0 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 ¸ .. a s ın ın a ˆ cazul nostru k = 4 ¸i pozitia este goal˘.15.. n n+1 Aici k = 2 ¸i 0 < max. a ¸ 0 1 2 3 4 5 0 0 1 0 1 . .. deci ˆ acest caz cu 1. 0 1 2 3 4 5 0 0 1 0 0 1 . .... deci k = k − 1 a 0 1 2 3 4 5 0 0 9 0 1 .. ˆ urma pasului f˘cut spre dreapta!). .. 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. 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). 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.. .. 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 gol=-1< max. n n+1 Aici k = 4 = n ¸i 9 = max.. a a ın 0 1 2 3 4 5 0 0 1 0 1 . n n+1 Aici k = 3 ¸i 9 = max.. ˆ ¸elegem de ce este util s˘ initializ˘m ınt a ¸ a variabila gol cu valoarea min − 1... 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 Dup˘ plasarea unei valori pe pozitia k...1..

n n+1 0 1 2 3 4 5 9 se gole¸te pozitia ¸i ajungem la k = 0 s ¸ s 0 1 .. .. Aici k = 4 ¸i 0 < max... . 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 . Ajungem la k = 4 s a ¸ 0 1 ... n n+1 0 1 2 3 4 5 iar aici k = 0 ¸i am ajuns ˆ afara vectorului! s ın 0 1 . METODA BACKTRACKING • se face pasul spre dreapta.. deci k = k + 1 0 1 2 3 4 5 0 1 0 0 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. n n+1 Aici k = 4 ¸i gol=-1< max.. 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 Nu-i nimic! Aici s-a terminat generarea produsului cartezian! . .. n n+1 A ap˘rut ˆ mod evident urm˘toarea regul˘: ajungˆnd pe pozitia k ≤ n.272 CAPITOLUL 15..... .. ..... . . deci k = k + 1 0 1 2 3 4 5 0 1 0 0 1 . 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.. deci k = k + 1 0 1 2 3 4 5 0 1 0 1 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.. n n+1 0 1 2 3 4 5 9 9 se gole¸te pozitia ¸i ajungem la k = 1 s ¸ s 0 1 .... .. a a s • dac˘ nu se poate: se pune gol=min − 1 ¸i se face pasul spre stˆnga.... . 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 .... 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.

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

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

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

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

x=new char[n+1]. nu se cunoa¸te o alt˘ metod˘ mai rapid˘ de rezolvare.. ¸ a i = 1. for(i=1. xn ) cu xi ∈ Ai .j<=m[k]. ¸ 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˘..i++) m[i]=a[i].println().2 Metoda bactracking Se folose¸te pentru rezolvarea problemelor care ˆ s ındeplinesc urm˘toarele conditii: a ¸ 1. } } static void afisv() { int i. s a a a 2.. } }// class 277 15. i<=n. else afisv(). solutia poate fi pus˘ sub forma unui vector x = (x1 . m=new int[n+1]. . n=a.2.out. 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. .print(x[i]). for(j=1.. .i<=n. f(1). System.15. for(i=1. x2 . i++) System.j++) { x[k]=a[k][j].. 3.length-1.length-1..out. if(k<n) f(k+1). METODA BACTRACKING public static void main(String[] args) { int i. n. } static void f(int k) { int j.

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.. 1}4 .a. 0 1 x1 0 1 0 1 x2 0 1 0 1 0 1 0 1 x3 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 x4 0 1 x1 0 1 0 1 x2 0 1 0 1 x3 0 1 0 1 x4 . xk ) a a ¸ ¸ a verific˘ conditiile induse de restrictiile problemei (acestea sunt numite conditii de a ¸ ¸ ¸ continuare). ..d. METODA BACKTRACKING Specificul metodei const˘ ˆ maniera de parcurgere a spatiului solutiilor. la fiecare etap˘ fiind completat˘ cˆte o ¸ a a a component˘.m.. ıncˆ a a a ¸ • la completarea componentei k se verific˘ dac˘ solutia partial˘ (x1 ... • dac˘ au fost ˆ a ıncercate toate valorile corespunz˘toare componentei k ¸i ˆ a a s ınc˘ nu a fost g˘ sit˘ o solutie. 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. 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). sau dac˘e dore¸te determinarea unei noi solutii. a ˆ cazul ˆ care sunt specificate restrictii (ca de exemplu. a ın ¸ ¸ • solutiile sunt construite succesiv. × An . ¸. a ¸ ˆ figur˘ este ilustrat modul de parcurgere a spatiului solutiilor ˆ cazul In a ¸ ¸ ın gener˘rii produsului cartezian{0. x2 . 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 × .278 CAPITOLUL 15.

} 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++]. An . .. se trece la nivelul inferior k − 1 prin ie¸irea din a s procedura recursiv˘ f .. a a ¸ • pornind de la restrictiile problemei se stabilesc conditiile de validitate ale ¸ ¸ solutiilor partiale (conditiile de continuare). ¸ ¸ ¸ x[k] = i ˆ ınseamn˘ c˘ pe pozitia k din solutia x se afl˘ ai din Ak . Mai precis. ¸ • se identific˘ multimile A1 .k<=n. An ¸i se stabile¸te o ordine ntre elemente a ¸ s s ˆ care s˘ indice modul de parcurgere a fiec˘rei multimi. . a a ¸ ¸ a 15. se initializeaz˘ nivelul ¸i se caut˘ un succesor. a a a a In procedura se autoapeleaz˘ pentru k + 1. 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). ın ¸ a s a − cˆnd am g˘sit un succesor. a . 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. altfel m˘resc ¸i r˘mˆn pe pozitie a s a a else x[k–]=gol. a − dac˘ nu avem succesor.1 Bactracking iterativ Structura general˘ a algoritmului este: a for(k=1. –k. 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. ın ¸ ın ¸ s˘ s − ˆ caz contrar.2 Backtracking recursiv Varianta recursiv˘ a algoritmului backtracking poate fi realizat˘ dup˘ o a a a schem˘ asem˘n˘toare cu varianta recursiv˘. A2 . atunci m˘resc ¸i fac pas dreapta a s else ++x[k].k++) x[k]=gol.2. A2 . o afi¸am ¸i revenim pe nivelul anterior. verific˘m dac˘ este valid. ˆ caz afirmativ.. ¸ ¸ ¸ ˆ 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). ˆ caz contrar urmˆnd a se continua a ın a c˘utarea succesorului. ¸ ¸ s a 15. initiarizarea vectorului solutie ¸ ¸ k=1.2.15. altfel golesc ¸i fac pas dreapta s } Vectorul solutie x contine indicii din multimile A1 ....

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

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

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

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

while (k>0) if (k==n+1) { afis(a).max=14.i++) a[i]=gol. System.i++) System.. static int gol=min-1. k. System.284 CAPITOLUL 15. for(i=1. static int[] a=new int[n+1]. static void afis(int[] a) { int i. METODA BACKTRACKING 15. // maresc si raman pe pozitie else a[k--]=gol.out.i<=n.out.. --k.i<=n. System.nv=0. long t1.currentTimeMillis(). min=1. class GenCombI1a // 4550 ms cu 12 22 // { // 7640 ms cu 8 14 cu afis static int n=8. t1=System.out. // initializat si a[0] care nu se foloseste!! k=1.println("Timp = "+(t2-t1)+" ms"). for(i=0.t2.currentTimeMillis(). } t2=System.3.println(). else ++a[k].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!).out. } else { if(a[k]<max) if(1+a[k]>a[k-1]) ++a[k++]. // maresc si pas dreapta (k>1) . } }// class 40 ms cu 8 14 fara afis .print(++nv+" : ").print(a[i]). } public static void main (String[] args) { int i.

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

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

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

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

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

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

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

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

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

else x[++k]=0. a as a class RegineI1 { static int[] x. 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˘). if(ok) break. x[k]=0. } static void regine(int n) { int k.nv=0. } if(!ok) k--.294 CAPITOLUL 15. for(int i=0. while (k>0) { ok=false.i<=n. 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. } }//regine() . else if (k == n) afis(). while (x[k] <= n-1) // caut o valoare posibila { ++x[k]. static int n. ok=posibil(k). a a s a a Sunt prezentate o variant˘ iterativ˘ ¸i una recursiv˘.i++) x[i]=i. regine(n).3. k=1. METODA BACKTRACKING 15. x=new int[n+1].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. public static void main(String[] args) { n=5. boolean ok.

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

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

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

0}... static int n=harta. .. static int[] culoare=new int[n]. x2 . . METODA BACKTRACKING 15..1. a class ColorareHartiI1 { static int nrCulori=3.2.2..1. Multimile de valor ale a a ta ¸ elementelor sint A1 = A2 = ...0.out.println("Nu se poate colora !").2.0. if(nrVar==0) System.3 static int[][] harta= {// CoCaIaBrTu {2.1. xk ) ¸ a a ¸ ¸ a este: xk = xi pentru orice i < k cu proprietatea c˘ vi.1}. = An = {1.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 ...0.. a a ta a astfel ˆ at oricare dou˘ ¸˘ri vecine s˘ fie colorate diferit. .1}. a s a Conditia de continuare pe care trebuie s˘ o satisfac˘ solutia partial˘ (x1 .1. // indicele pentru tara boolean okk. m}. } // main static void harta() { int k.1.5 Problema color˘rii h˘rtilor a a ¸ Se consider˘ o hart˘ cu n ¸˘ri care trebuie colorat˘ folosind m < n culori.// culorile sunt 1.// Braila (3) {1.0}.1. . public static void main(String[] args) { harta(). 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.298 CAPITOLUL 15..// Calarasi (1) {1.2} // Tulcea (4) }.k = 1.j = 1.length.1.3.. m} reprezentˆnd culoarea asociat˘ ¸˘rii i. static int nrVar=0. xn ) cu ta s a xi ∈ {1. 2. . 2...// Ialomita (2) {1.2.0. x2 . 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.// Constanta (0) {1.1.

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

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

nsol=0. . ˆ s a Intre oricare doi vecini izbucnesc conflicte.. 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.3. 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. conditiile de continuare sunt: ¸ a) xj = xk . f(1).m. 2. Presupunem c˘ persoanele a sunt numerotate la ˆ ınceput. if((Math.. x=new int[n+1]. PROBLEME REZOLVATE 301 15.j<k..i<=m. else afis(). de la stanga la dreapta cu 1. } static void f(int k) { boolean ok.abs(i-x[k-1])!=2)&&(Math. } . for(i=1. x[k]=i. m=7.. a s Pentru k > 1 dat. static int n.15.j++) if(i==x[j]) {ok=false. n. a class VeciniR1 { static int[] x. pentru j = 1.abs(i-x[k-1])!=3)) continue. .i++) { ok=true.6 Problema vecinilor Un grup de n persoane sunt a¸ezate pe un rˆnd de scaune.3. if(k<n) f(k+1). for(j=1. break... a s ¸ s Vom rezolva problema prin metada backtracking. public static void main(String[] args) { n=7. int i.j. 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”.} if(!ok) continue.

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

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

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

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

dim=poz-1.min(val-i.. static int[] x=new int[n+1]. dim=poz-1.print(x[i]+" ").n.. 2.i--) { x[poz]=i. se scad pe rˆnd valorile a s a 1. . Imediat ce este ¸ a s apelat˘.3.out. } .out. return.currentTimeMillis().i<=dim.306 CAPITOLUL 15. afis2(val. S[k] − 1. t2=System.out.poz+1). METODA BACKTRACKING 15. return. } static void f(int val.i>=1.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n"). procedura este apelat˘ pentru nivelul 1 ¸i valoarea n. for(int i=maxok. for(int i=1.1). } if(val==0) { nsol++. 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). a a La revenire se reface valoarea existent˘. procedura va apela o alta pentru afi¸area vectorului (initial afi¸eaz˘ n).print("\n"+nsol+" : "). public static void main(String[] args) { long t1. t1=System. f(n.i++) System.i). } int maxok=min(maxp. System.currentTimeMillis().maxp). int maxp.val). valori cu care se apeleaz˘ procedura pentru nivelul urm˘tor.t2.. nsol=0. int poz) { if(maxp==1) { nsol++. S[k]. afis1(). s ¸ ¸ a a a Initial. } } static void afis1() { System. a class PartitieNrGenerare // n = suma de numere { static int dim=0. a s ¸ s a Din valoarea care se g˘se¸te pe un nivel. n=6. f(val-i.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.

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

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

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

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

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

f[u]=++t. System.312 CAPITOLUL 15.print(u+" "). for(v=1.print("Finalizat :\t").) static void afisv(int[] x) { int i.v<=n.print("Descoperit :\t"). afisv(d).print(x[i]+"\t").. METODA BACKTRACKING t=0.i++) System.. }//main static void dfs(int u) { int v. d[u]=++t. for(i=1.out.i<=n. } color[u]=BLACK. System.println().out.println().out.out.print("drum : ").. afisv(f)..out. System.v++) if(a[u][v]==1) if(color[v]==WHITE) { p[v]=u. }// drum(. } }//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 . drum(nodd).out.out. System. }//dfs // listele de adiacenta . !!! // v in Ad(u) !!! static void drum(int u) // nod_sursa ---> nod_destinatie { if(p[u]!=-1) drum(p[u]). color[u]=GRAY. dfs(nods). System. System. dfs(v).

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

12 Determinarea componentelor tare conexe // determinarea componentelor tare conexe (in graf orientat!) // Algoritm: 1.out. 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.314 { CAPITOLUL 15.print(i+" : ").io.f. static int[][] a.color.3.print(j+" ").v++) if((a[u][v]==1)&&(cc[v]==0)) conex(v).println(). // matricea grafului . }//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. dfs(G) pentru calculul f[u] // 2.out.j<=n. } }//main static void conex(int u) { cc[u]=ncc. class CompTareConexe { static final int WHITE=0. GRAY=1. static int [] ctc.nctc.out. static int n. BLACK=2.t=0.v<=n.lista.m.pozLista.*.j++) if(cc[j]==i) System. for(int v=1. System. METODA BACKTRACKING System. for(j=1.

out. st.i++) if(color[lista[i]]==WHITE) { nctc++.nval.j++) if(ctc[j]==i) System. f=new int[n+1].nval.i<=n. color=new int[n+1]. for(i=1. for(i=1. dfsat(lista[i]).nval.i<=n. st. i=(int)st. at[j][i]=1.k<=m. . for(i=1.out.nextToken().i++) if(color[i]==WHITE) dfsa(i).3.nval. color[u]=GRAY. lista=new int[n+1].nextToken().k. for(j=1.print(j+" ").out.i<=nctc. PROBLEME REZOLVATE static int[][] at.i<=n.println().j<=n. st. pozLista=n. nctc=0.i++) color[i]=WHITE. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("compTareConexe.i<=n.in"))).i++) { System. at=new int[n+1][n+1]. for(k=1. 315 // matricea grafului transpus (se poate folosi numai a !) public static void main(String[] args) throws IOException { int i.print(i+" : "). m=(int)st.15. } }//main static void dfsa(int u) { int v.k++) { st. } for(i=1.i++) color[i]=WHITE. j=(int)st.nextToken().nextToken(). n=(int)st. ctc=new int[n+1].j. // transpusa } for(i=1. System. a[i][j]=1. a=new int[n+1][n+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. DFS pentru calcul f[u]. u=nod 2.*. //"a" color[u]=BLACK.. .j++) if((at[u][lista[j]]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]).v++) if((a[u][v]==1)&&(color[v]==WHITE)) dfsa(v).13 Sortare topologic˘ a Folosind parcurgerea ˆ adˆncime ın a // // // // // // // // Sortare Topologica = ordonare lineara a varfurilor (in digraf) (u. for(j=1. f[u]=++t.. METODA BACKTRACKING for(v=1..io." 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. u .v)=arc ==> ". color[u]=BLACK..3. color[u]=GRAY.j<=n. ctc[u]=nctc.316 CAPITOLUL 15.v<=n. } static void dfsat(int u) // se poate folosi "a" inversand arcele ! { int j. //"at" //if((a[lista[j]][u]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j])... lista[pozLista--]=u. v .

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

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

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

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

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

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

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

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

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

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

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

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

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

. ≥ ni ≥ ..in partitie.. a a Datele de intrare Fi¸ierul partitie.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 + . (k ≥ 1) unde n1 . nk sunt numere naturale care verific˘ urm˘toarea relatie: a a ¸ n1 ≥ n2 ≥ . ¸tiind c˘ oricare num˘r ni dintr-o partitie ¸ s a a ¸ trebuie s˘ fie un num˘r impar.3. n2 ...out va contine pe prima linie num˘rul de partitii ale lui n s ¸ a ¸ conform cerintelor problemei. conform cerintelor de mai sus.in contine pe prima linie num˘rul n s ¸ a Datele de ie¸ire s Fi¸erul partitie.. s˘ se determine cˆte partitii ale lui se pot a a a ¸ scrie.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 . . + nk ..18 Partitie . ≥ nk ≥ 1 Cerint˘ ¸a Fiind dat un num˘r natural n.330 CAPITOLUL 15... METODA BACKTRACKING 15. ¸ Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ N ≤ 160 Exemplu partitie.

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

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

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

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

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

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

provocˆnd-o la un concurs de cules ciupercute. pentru a le duce acas˘ (dup˘ ce va veni. Deci Scufita ¸ Ro¸ie va fi liber˘ s˘ plece dac˘ nu va pierde jocul dup˘ 15 minute.. atˆt Scufita.b2n . Dac˘ Scufita Ro¸ie ajunge ˆ ¸ ¸ a a ¸ s ıntr-o pozitie ˆ ¸ ın care nu sunt ciupercute.bnn Date de ie¸ire s Fi¸ierul scufita. PROBLEME REZOLVATE 337 la bunicut˘. Dac˘ la sfˆr¸itul unui minut ea are mai ¸ a as putine ciupercute decˆt Lupul. Dac˘ Scufita Ro¸ie pierde jocul. Lupul se afl˘ ˆ pozitia ın ¸ ¸ a ın ¸ (1.b1n . Scufita Ro¸ie l-a sunat pe Vˆn˘tor. a at Povestea spune c˘ Scufita Ro¸ie a plecat acas˘ cu co¸uletul plin de ciupercute.out are urm˘toarea structur˘: s a a .. cunoscut pentru l˘comia sa proverbial˘... ea va pierde jocul de asemenea. a a a Lupul. ci doar dup˘ un num˘r ˆ a a ıntreg strict pozitiv de minute de la ˆ ınceput).ann b11 b12 . 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. care i-a ¸ s a a promis c˘ va veni ˆ a ıntr-un sfert de ora (15 minute) pentru a o salva... cum ar fi c˘l˘toria ˆ timp. bn1 bn2 .. 1) a unei matrice cu N linii ¸i N coloane. pentru aa ın a nu complica inutil enuntul problemei). s a a a a Din acest moment.. 1) a unei alte matrice similare.dimensiunea celor dou˘ matrice a a11 a12 . ci ¸i s˘ culeag˘ a a a a ın ¸a s a a cˆt mai multe ciupercute.. dar a decis s˘-i acorde o ¸ans˘ de ın a a a a s a sc˘pare. 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˘.15. a ¸ s a ˆ Inainte de ˆ ınceperea jocului. a a ¸ Scufita Ro¸ie se afl˘ ˆ pozitia (1.... 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. va pierde jocul. an1 an2 . ¸ s a ın ¸ s ˆ fiecare pozitie a matricei fiind amplasate ciupercute. Pe parcursul unui minut... 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.matricea din care culege Lupul b21 b22 .a1n -matricea din care culege Scufita Ro¸ie ¸ s a21 a22 . ea s-a ˆ alnit cu Lupul (fratele lupului care a p˘r˘sit povestea ıntˆ aa ˆ prima parte). Veti afla am˘nunte ˆ continuare... scopul ei este nu numai s˘ r˘mˆn˘ ˆ viat˘. Acesta dorea s˘ o m˘nˆnce.. ¸a ¸ a ın Pe drum. S˘ fi fost acest program scris de c˘tre ¸ a a dumneavoastr˘? Vom vedea. vˆn˘torul nu o a ¸ a a a a va mai l˘sa s˘ culeag˘). 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).3.a2n . Lupul o va mˆnca.in are urm˘toarea structur˘: s a a N ..

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

deci constant (constanta introdus˘ fiind totu¸i a s destul de mare). maxc=0.y. ¸ s respectiv O(M ). primele dou˘ mut˘ri ofer˘ dou˘ variante de alegere ˆ loc de trei.15.in"))). ¸ 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). ˆ multe cazuri permit un a a ın timp de executie foarte bun. a a ¸ Operatiile de citire ¸i scriere a datelor au ordinul de complexitate O(N 2 ). static int[][] a. ordinul de complexitate ın ¸ ar fi O(3M ).*. 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˘. ˆ momentul g˘sirii unei solutii. ¸ a a Cum num˘rul M este cunoscut ¸i mic. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("scufita. de a a exemplu. 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). st. ¸ Analiza complexit˘¸ii at Fie M num˘rul de mut˘ri pe care le are de efectuat Scufita. class Scufita { static int n. public static void main(String[] args) throws IOException { int i. dar limitele mici ¸i faptul c˘ num˘rul a s a a de mut˘ri disponibile ˆ a ıncepe s˘ scad˘ destul de repede. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("scufita.b.nextToken(). Ordinul de complexitate este dat de ın rezolvarea prin metoda backtracking. .nval. De fapt. n=(int)st. Codul surs˘ a import java. deci nu vor fi luate ˆ calcul.3. s-ar putea considera c˘ ordinul de a s a complexitate este limitat superior. Alte a a a a ın restrictii apar datorit˘ limit˘rii la o matrice de 10 · 10 elemente. static char[] tc=new char[16].j. static int[] x.io. static char[] tmax=new char[16]. PROBLEME REZOLVATE 339 Se urm˘re¸te respectarea restrictiilor impuse.out"))). num˘rul maxim de st˘ri examinate este mult mai mic.

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

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

342 CAPITOLUL 15. METODA BACKTRACKING .

¸ a 343 . 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. La un anumit nivel. Sn ale sistemului. . optimele a ın a partiale se vor retine treptat. ˆ literatura de specialitate exist˘ dou˘ variante de prezentare a acestei In a a tehnici. 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. stare a ¸i decizie. ¸ • Subproblemele respective se suprapun. s a a Prima variant˘ se bazeaz˘ pe conceptul de subproblem˘.. dou˘ sau mai a multe subprobleme necesit˘ rezolvarea unei aceea¸i subprobleme. S1 .. Pentru a a s evita risipa de timp rezultat˘ ˆ urma unei implement˘ri recursive.Capitolul 16 Programare dinamic˘ a 16. ˆ maniera bottom-up. A doua variant˘ de prezentare face apel la conceptele intuitive de sistem. intuitie ¸i abilit˘¸i a a ¸a ¸ s at matematice. ın Fie secventa de st˘ri S0 . De foarte multe ori rezolv˘rile date prin aceast˘ tehnic˘ au ordin de a a a complexitate polinomial.. ˆ anumite structuri de ¸ ¸ ın ın date (tabele). Prima dintre ele este mai degrab˘ de natur˘ deductiv˘ pe cˆnd a doua a a a a folose¸te gˆndirea inductiv˘. iar optimul global se obtine prin combinarea ¸ ¸ acestor optime partiale.1 Prezentare general˘ a Folosirea tehnicii program˘rii dinamice solicit˘ experient˘.

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

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

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

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

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

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

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

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

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

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

¸ import java. ın ¸ ¸ ın a din acest motiv vom memora solutia ˆ ıntr-un vector.*. ˆ ordine lexicografic˘. } else if (lcs[k][h]==lcs[k-1][h]) k--. h--. Deoarece nu am utilizat o structur˘ de date suplimentar˘ cu ajutorul c˘reia a a a s˘ memor˘m solutia optim˘. k=n. Sunt determinate cea mai mic˘ ¸i cea mai ın s a ¸ as mare solutie. 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].io. . // SubsirComunMaximal class scm { static PrintWriter out. for (k=i-1. else h--. int d[100]. k--. lcs[k][h]. k++) for (int h=1. for (int i=0. else lcs[k][h]=lcs[k][h-1]. PROGRAMARE DINAMICA for (int k=1. ) if (x[k]==y[h]) { d[i++]=x[k]. h<=m. Programul urm˘tor este ¸ ın a scris ˆ Java ¸i determin˘ toate solutiile. cout<<"\nCel mai lung subsir comun este: \n". h=m.354 ˘ CAPITOLUL 16. vom reconstitui solutia optim˘ pe baza rezultatelor a a ¸ a ¸ a memorate ˆ matricea lcs. k<=n.k>=0. h++) if (x[k]==y[h]) lcs[k][h]=1+lcs[k-1][h-1]. Secventele de cod de mai sus sunt scrise ˆ C/C++. Prin reconstituire vom obtine solutia ˆ ordine invers˘. else if (lcs[k-1][h]>lcs[k][h-1]) lcs[k][h]=lcs[k-1][h]. static int [][] a. k--) cout<< d[k] <<’ ’. 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.

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

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

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

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

length-1.length. out.i<=v.charAt(i-1)+" ").println().println(). for(j=0. System.j<m.out. m=d[i]. } System.i++) out.print(v[i]).i++) { System.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 * | | | * | * * - .length-1.print(d[i][j]+" ").println().2.out.16.print(v[i]). System.i++) System. PROBLEME REZOLVATE for(i=1.print(x. for(i=1.out.j++) System.out.println("\n"). } static void afisv(char[]v) { int i.i<n.out.out. for(i=1.

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

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

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

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

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

int b.2.c)). } }// 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 .min(b..) 365 static int min(int a.. int b) { return a<b ? a:b. int c) { return min(a.16. } static int min(int a. PROBLEME REZOLVATE }//afissol(.

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

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

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

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

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

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

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

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

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

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

a s a ˆ acest mod s-ar construi o matrice cu G linii ¸i 2 ∗ (sumamaxima) + 1 In s coloane. ¸ a at at a Indicii ¸irului vor varia ˆ s ıntre −6000 ¸i 6000. 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). ¸ ı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. cu elemente numere ˆ ıntregi pe 32 de biti. Dac˘ vom amplasa o greutate de a at ¸ a valoare g pe un cˆrlig aflat la distanta d fat˘ de centrul balantei.16. de fapt se memoreaza doar ¸ ultimele dou˘ linii (fiecare linie se obtine din precedenta). Ca urmare. Fiecare greutate a a at a poate fi amplasat˘ pe oricare dintre cˆrlige. unde g este num˘rul s a greut˘¸ilor.2. a ın ¸ respectiv O(c). dup˘ amplasarea noii greut˘¸i exist˘ ai posibilit˘¸i de a a a at a at obtine suma i+gd. valoarea bi+gd va cre¸te cu ai . pe balant˘ nu este ag˘¸at˘ nici o greutate. se pot adauga alte moduri de obtinere plasˆnd ın ¸ a greutatea i pe un alt cˆrlig ¸i folosind suma respectiv˘). iar suma este s. Dup˘ s a considerarea tuturor perechilor. d). evident. 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. Suma maxim˘ este a ¸ a 15 ∗ 25 ∗ 20 = 7500. 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. S˘ presupunem c˘ la un moment a a a a dat exist˘ ai posibilit˘¸i de a obtine suma i. a a a ˆ continuare. Dac˘ distantele sunt d1 ¸i d2 . s Initial. 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). Pentru a ˆ s ımbun˘t˘¸i viteza de a at executie a programului. Rezultatul final va fi dat de valoarea a0 obtinut˘ dup˘ considerarea tuturor ¸ a a greut˘¸ilor disponibile. . 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. Pentru fiecare pereche (i. ¸irul b va contine a at s ¸ doar zerouri. s a at a ¸ s atunci indicii vor varia ˆ ıntre −d1 s ¸i d2 s. atunci modulul sumei momentelor fortelor ˆ a a ¸a ¸ ¸ este 152020 = 6000). vom ˆ In ıncerca s˘ amplas˘m greut˘¸ile pe cˆrlige. a Citirea datelor de intrare corespunz˘toare cˆrligelor ¸i greutatilor se reala a s ¸ izeaz˘ ˆ timp liniar. iar cel al at a at cˆrligelor cu c. iar ¸irul b va fi reinitializat cu s ın s s ¸ 0. Ca urmare. ˆ coloana asociat˘ sumei 0. indicii vor varia ˆ ¸ ıntre −300g ¸i 300g. deci ordinul de complexitate al acestor operatii este O(g). PROBLEME REZOLVATE 385 ˆ M moduri (la care. at Analiza complexit˘¸ii at Pentru studiul complexit˘¸ii vom nota num˘rul greut˘¸ilor cu g. a Valorile din ¸irul b vor fi salvate ˆ ¸irul a. deci o linie se incadreaz˘ ˆ mai putin de 64K. Ca urmare. vom putea trece la o nou˘ greutate. ˆ Inainte de a testa posibilit˘ile de plasare a greut˘¸ii. a¸adar suma momentelor ¸ ¸a at a s greut˘¸ilor este 0. a ın ¸ Rezultatul final se obtine pe ultima linie.

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

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

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

Chiar dac˘ s-ar p˘rea c˘ ˆ acest mod am g˘sit as a a a a ın a solutia problemei. PROBLEME REZOLVATE 389 Se calculeaz˘. 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˘). pentru fiecare element. Ceilalti soldati vor trebui s˘ p˘r˘seasc˘ formatia. 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. cu ordinul de complexitate O(n2 ). 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. timpul de executie admis ne permite s ¸ folosirea unui algoritm simplu. majoriın a ¸ s ınalt tatea testelor se ˆ ıncadreaz˘ ˆ acest caz. ¸ Chiar dac˘ exist algoritmi eficienti (care ruleaz˘ n timp liniar-logaritmic) de a ¸ a determinare a celui mai lung sub¸ir ordonat. Acesta va fi aplicat de dou˘ ori. 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 ). 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. corespunz˘toare celui mai ˆ a a a ınalt soldat dintre cei r˘ma¸i ˆ ¸ir. 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. 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. respectiv strict descresc˘tor. ın˘ ¸ a a s nu putem alege orice soldat cu aceea¸i ˆ altime. mai exist˘ o posibilitate de a m˘ri num˘rul soldatilor care r˘mˆn ¸ a a a ¸ a a ˆ ¸ir. Primul sub¸ir se termin˘ inainte de a ˆ s a ıncepe al doilea. Soldatii din primul sub¸ir privesc spre stanga. Acesta poate privi fie spre stˆnga. Din a a s aceste motive. la stˆnga sau la dreapta sa poate s˘ se afle un soldat de aceea¸i a a s ˆ altime.2. Urmeaz˘ eventuala identificare a unui alt soldat de aceea¸i ˆ altime care a s ın˘ ¸ respect˘ conditiile referitioare la lungimile sub¸irurilor. Pentru aceasta se parcurge a ¸ s . ¸ ¸ a s ¸ ¸ a aa a ¸ Analiza complexit˘¸ii at Citirea datelor de intrare se realizeaz˘ ˆ timp liniar. dup˘ care se va c˘uta valoarea maxim˘ a sumei lungimilor a a a a ¸irurilor corespunz˘toare unui soldat. respectiv cel mai ın˘ ¸ ¸ a lung sub¸ir strict descresc˘tor de soldati care urmeaz˘ dup˘ el. deci ordinul de coma ın plexitate al acestei operatii este O(n). 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. unul dintre cei doi va privi spre dreapta. Se au in vedere cazurile particulare. vom determina soldatul pentru care suma lungima a ¸ ilor celor dou˘ ¸iruri este maxim˘.16. Totu¸i. iar cel˘lalt spre stˆnga. ceilalti spre ¸ ¸ s ¸ dreapta. s a ¸ a a Dup˘ aceast˘ operatie. fie spre dreapta ¸irului.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

currentTimeMillis(). a a ¸ a Datele de ie¸ire s Fi¸ierul de ie¸ire avere.2. Fiind un tip original ¸i pasionat de matematic˘ a scris un testaa s a ment inedit. 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. 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. out. Italag decide s˘-¸i ˆ a s ımpart˘ toat˘ averea.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. 7. s a at ımp˘ ¸ . 4 3. 5 2. System. }// main() }// class 16.412 out. ar putea fi ˆ artit˘ astfel: a at ımp˘ ¸ a • 4 (unit˘¸i primului mo¸tenitor) 3 (unit˘¸i celui de-al doilea).println(minsol).close(). N date s˘ calculeze ¸i s˘ afi¸eze ¸ a s a s num˘rul total de posibilit˘¸i de ˆ artire a averii. PROGRAMARE DINAMICA t2=System.out. a Cerint˘ ¸a Scrieti un program care pentru numerele S. Pentru exemplul de mai sus: ımp˘ ¸ ın a 4 2 1. 6 1. precum ¸i modul ˆ care se face a at ımp˘ ¸ s ın aceast˘ ˆ artire conform cu a N -a posibilitate din ordinea lexicografic˘. A hot˘rˆt ca banii s˘ fie distribuiti conform celei de-a N -a posibilit˘¸i din aa a ¸ at ordinea lexicografic˘. iar sumele pe care le acord˘ mo¸tenitorilor a a a s s˘ fie ˆ ordine strict descresc˘toare. a ın a De exemplu dac˘ averea ar fi 7 unit˘¸i monetare. sau at s at • 6 (unit˘¸i primului mo¸tenitor) 1 (unitate celui de-al doilea). ˘ CAPITOLUL 16. a ımp˘ ¸ a Datele de intrare Fi¸ierul de intrare avere.18 Avere ONI2005 cls 10 Italag a fost toat˘ viata pasionat de speculatii bursiere reu¸ind s˘ adune o a ¸ ¸ s a avere considerabil˘. Italag le-a scris ˆ ordine lexicografic˘. sau at s • 5 (unit˘¸i primului mo¸tenitor) 2 (unit˘¸i celui de-al doilea). sau at s • 7 (unit˘¸i doar primului mo¸tenitor).println("Timp = "+(t2-t1)). 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).

xm ) ¸i y = (y1 . astfel ˆ at xi = yi . ¸ 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 .1 secunde ¸ a s pentru Linux. dac˘ exist˘ k ≥ 1.out 5 43 avere. Exemple: avere.. a a ın s a a Obtinem recurenta: ¸ ¸ .. 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˘.in 72 avere. Emilian Miron.... . a Indicatii de rezolvare .2. din ordinea lexicografic˘. 2 < 3). Limita total˘ de memorie sub Linux este 3Mb din care 1Mb pentru a stiv˘. . 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˘.descriere solutie * ¸ ¸ stud. a • Fie x = (x1 . yp ) dou˘ ¸iruri...out 962056220379782044 175 68 63 58 54 45 40 36 34 32 20 18 17 14 11 9 3 2 1 avere. Elementele sale vor fi separate prin cˆte ¸ ın a a un spatiu.16. k − 1 ¸i xk < yk . s Exemple: 4 2 1 preced˘ secventa 4 3 deoarece (4 = 4.out 15 651 avere. iar 6 1 preced˘ a ¸ a 7 deoarece 6 < 7. a at a • Posibilit˘¸ile de ˆ artire a averii sunt numerotate ˆ at ımp˘ ¸ ıncepˆnd cu 1. y2 .in 700 912345678912345678 Timp maxim de executie/test: 1 secund˘ pentru Windows ¸i 0. pentru a a ıncˆ orice i = 1.in 12 5 avere. Spunem c˘ x preced˘ s as a a pe y din punct de vedere lexicografic... x2 .

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

. PROBLEME REZOLVATE 415 for(v=1. }// main(. } out. while((n>0)&&(S>0)) { v=0. } }// 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.i++) { for(j=0.v<=S. out. out. for(i=0.println(c[S][S]).out.2.s++) if(s<v) c[v][s]=c[v-1][s]. t2=System.. else c[v][s]=c[v-1][s]+c[v-1][s-v].i<=S. //afism(). System.out.) static void afism() { int i.j<=S.out. .print(v+" ").close(). while(c[v][S]<n) v++..v++) for(s=0.16.j++) System.print(c[i][j]+" ").println("Timp = "+((double)(t2-t1))/1000000000).println().nanoTime().. S=S-v.j.s<=S. System. n=n-c[v-1][S].

ˆ total 680 de galbeni. el poate opta pentru o nou˘ distributie a cifrelor ın a ¸ numerelor stabilite de marele sfat astfel: pentru prima zi cere 2 galbeni. a • Orice sum˘ dintr-o distributie trebuie s˘ fie nenul˘. ın Cerint˘ ¸a Pentru num˘rul de zile n ¸i cele n sume stabilite de sfatul ¸˘rii pentru Jibal. PROGRAMARE DINAMICA 16. El poate uni cifrele sumelor stabilite ¸i le poate desp˘rti ta s a¸ apoi.. pl˘tite cu 23. prime¸te dreptul ca. 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. dup˘ dorint˘. dar nu foarte mult.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. dac˘ are doar 5 zile de activitate. s2 . s primind astfel 1167 de galbeni ˆ total. pentru 50% din teste n ≤ 50. pentru a patra 205 ¸i pentru a cincea 540 de galbeni. Astfel.19 Suma . pentru a doua 3. pentru ziua a patra 144 de galbeni.. 417. sn a ¸ reprezentˆnd sumele atribuite de sfatul ¸˘rii. pentru orice 1 ≤ i ≤ n • ˆ orice distributie. iar pentru ziua a cincea doar 2 galbeni. a a a a ta vizirul Magir a primit pentru doar 5 zile de activitate prima total˘ de 411 galbeni. Vizirul Jibal. n ≤ 10. Exemple . la ie¸irea la pensie. ¸ s a¸ Datele de intrare Fi¸ierul de intrare suma.416 */ ˘ CAPITOLUL 16. celebru pentru contributia adus˘ la rezolvarea conflictului din ¸ a zon˘. a a a a ¸ Restrictii ¸i preciz˘ri ¸ s a • 1 < n < 501 • 0 < si < 1000. fiecare sum˘ trebuie s˘ fie o valoare proprie (s˘ nu In ¸ a a a ˆ ınceap˘ cu 0). marele vizir s˘ primeasc˘ o prim˘ stabilit˘ de marele sfat al ¸˘rii. n numere naturale separate prin spatii s1 .. a deoarece sfatul ¸˘rii a hot˘rˆt pentru ziua ˆ ai o sum˘ de 53 de galbeni. pentru a treia 417. la ie¸irea la pensie. pentru ziua a treia 12 galbeni. pentru fiecare zi de activitate ˆ slujba ¸ s ın sultanului. s a a ¸ Astfel.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. . a ¸ a a • Pentru 20% din teste.2. 205. 5 ¸i respectiv 40 de a a s galbeni. 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. astfel ˆ at. a ta Datele de ie¸ire s Fi¸ierul de ie¸ire suma. pentru ta aa ıntˆ a ziua a doua 200 de galbeni.ONI2005 cls 10 Traditia este ca. s˘ modifice sumele stabilite de sfatul a s s a ¸˘rii.

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

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

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

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

}// for }// calcul() static int max(int a.j<=n. 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. else // c[2]!=0 && c[3]!=0 s[3][2]=max(c[1]+c[2]*10+c[3]. if((c[2]!=0)&&(c[3]!=0)) s[3][3]=c[1]+c[2]+c[3]. } else // c[3]!=0 { if(c[2]==0) s[3][2]=c[1]*10+c[3]. s[3][1]=c[1]*100+c[2]*10+c[3].s[i-2][j-1]+c[i-1]*10+c[i]). PROBLEME REZOLVATE int i. if(c[3]==0) { if(c[2]!=0) s[3][2]=c[1]+c[2]. s[1][1]=c[1].i<=nc.j.j++) { smax=0. if((c[i-2]!=0)&&(s[i-3][j-1]!=0)) smax=max(smax. } for(i=4. } .s[i-3][j-1]+c[i-2]*100+c[i-1]*10+c[i]). } s[i][j]=smax. else return b.2. int b) { if(a>b) return a. s[2][1]=c[1]*10+c[2]. if((c[i-1]!=0)&&(s[i-2][j-1]!=0)) smax=max(smax.smax.c[1]*10+c[2]+c[3]).16.s[i-1][j-1]+c[i]).i++) for(j=1. if(c[2]!=0) s[2][2]=c[1]+c[2].

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

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

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

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

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

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

428 ˘ CAPITOLUL 16. PROGRAMARE DINAMICA .

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

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

17.2 Un algoritm eficient . UN ALGORITM EFICIENT .KMP 431 17. f˘r˘ s˘ risc˘m aa a a s˘ pierdem vreo solutie! a ¸ . ¸ 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 *.2. ¸ Dorim s˘ avans˘m cu mai mult de un pas la o nou˘ ˆ a a a ıncercare.KMP Algoritmul KMP (Knuth-Morris-Pratt) determin˘ potrivirea ¸irurilor folosind a s informatii referitoare la potrivirea sub¸irului cu diferite deplasamente ale sale.

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

2 pentru a urm˘ri mai u¸or rationamentul! ˆ aceast˘ a a s ¸ In a figur˘ next[j] = k ¸i next[k] = k ′ ..2: Functia next ¸ Ne ajut˘m de Figura 17.. 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 . // t[1. a s Dac˘ p[j+1] = p[k+1] (folosind notatiile din figur˘) atunci next[j+1] = k+1. a Ordinul de complexitate al algoritmului KMP este O(n + m). .. atunci vom avea next(j + 1) = 0.. k+1 j+1 k'+1 .n]=text. a Obtinem: ¸ dac˘ p[j + 1] = p[next(next(j)) + 1] atunci next[j + 1] = next(next(j)) + 1. .. 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. dac˘ p[j + 1] = p[k ′ + 1] atunci next(j + 1) = k ′ + 1.m]=pattern static int[] next... import java. ... .. // nr aparitii static char[] t.k]..j]! Fie acesta p[1.... next[j].. a ¸ a Obtinem: ¸ dac˘ p[j + 1] = p[next(j) + 1] atunci next[j + 1] = next(j) + 1... acest algoritm se termin˘ ˆ a ıntr-un num˘r finit de pa¸i pentru c˘ a s a j > k > k′ > . . ... p: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx j+1-k' . .k ′ ]. UN ALGORITM EFICIENT . .2. . j+1-k . Evident.. ... p[1... Dar acest sufix a a mai mic este cel mai lung sufix pentru p[1. Astfel.. class KMP { static int na=0.... j 1 k' Figura 17. .17.. a Dac˘ nici acum nu avem egalitate de caractere. . ≥ 0... .. .... a Presupunem c˘ au fost determinate valorile next[1].p..io. 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)).KMP Initializ˘m next[1] = 0. Dac˘ ajungem la 0. next[2]......*..

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

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

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

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

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

iar pentru n mic se a poate c˘uta permutarea pentru fiecare d = 0. p(m) separate prin cˆte un spatiu. s a ¸ Date de ie¸ire: s Fi¸ierul de ie¸ire cifru.out 63 2 213321 312 131322 Timp maxim de executie/test: 0.*... care poate fi a s ın rezolvat˘ cu un algoritm de pattern matching. 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. 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(2). . a Codul surs˘ a import java. 2. a ¸ Restrictii: ¸ n ≤ 100000 m ≤ 9999 Mesajul contine fiecare num˘r natural din intervalul [1.. s s ¸ a reprezentˆnd num˘rul de pozitii cu care s-a realizat permutarea circular˘ spre a a ¸ a dreapta. Exemplu: cifru. m] cel putin o dat˘. m} f˘cˆnd a ¸ a a pentru fiecare permutare o c˘utare (cu KMP de exemplu). s a Date de intrare: Fi¸ierul de intrare cifru.io. Dac˘ pentru d exist˘ mai multe posibilit˘¸ii se va alege valoarea minim˘. Mai exact se vor scrie valorile a a p(1).17.. dac˘ concaten˘m primul ¸ir cu el a a a s ˆ si rezultˆnd o complexitate O(n). . ınsu¸ a Pentru m mic se pot genera toate permut˘rile multimii {1. reprezentˆnd lungimea mesajului ¸i respectiv num˘rul de ¸ a s a litere din alfabetul solarian.in cifru. PROBLEME REZOLVATE 439 Date fiind un mesaj necodificat ¸i codificarea sa.3. deci pen¸a ¸ s a s tru prima aparitie a simbolului se ia distanta fat˘ de ultima aparitie a aceluia¸i ¸ ¸ ¸a ¸ s simbol)... ¸ a ¸ a Pentru teste cu m ≤ 5 se acord˘ 40 de puncte din care 20 pentru teste ¸i cu a s n ≤ 2000. determinati cifrul folosit s ¸ (permutarea p ¸i num˘rul d). 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.. 1..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. . .out va contine pe prima linie num˘rul natural d. s ¸ s separate prin spatiu.in contine pe prima linie numele naturale n ¸i m. a a at a Pe urm˘toarea linie este descris˘ permutarea p. n..

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

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

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

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

POTRIVIREA SIRURILOR ¸ .444 CAPITOLUL 17.

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

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

Pn ) = k=1 arietriunghi (P0 Pk Pk+1 ) unde punctul P (xn+1 . 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 . Dorim s˘ determin˘m dac˘ punctul P0 (x0 . a a ın O alt˘ modalitate de verificare dac˘ punctul P0 (x0 . yn+1 ) unde a a s x1 = xn+1 ¸i y1 = yn+1 . y0 ) este ˆ interiorul a a ın sau pe frontiera poligonului convex P1 (x1 . ın Pentru comoditatea prezent˘rii consider˘m ¸i punctul Pn+1 (xn+1 . a a n ≥ 3 ¸i un punct P0 (x0 . adic˘ punctul Pn+1 este de fapt tot punctul P1 . adic˘ toate valorile s a fi (xj . y2 ). y2 ). y0 ) este s a a a ˆ interiorul poligonului. y1 ). y0 ) se afl˘ ˆ interiorul poligonului convex. y2 ).Pn (xn yn ). 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 .. Dorim s˘ determin˘m dac˘ punctul P0 (x0 . valori cu semn contrar s s pentru toate punctele din cel˘lalt semiplan ¸i valoarea 0 pentru doate punctele a s situate pe dreapt˘. y0 ). s a Consider˘m o latur˘ oarecare [Pi Pi+1 ] (1 ≤ i ≤ n) a poligonului. yj ) (1 ≤ j ≤ n. y0 ) este s a a a ˆ interiorul poligonului.5.. y) are valori ın a ¸ de acela¸i semn pentru toate punctele din acela¸i semiplan.5 Pozitia unui punct fat˘ de un poligon concav ¸ ¸a Consider˘m un poligon concav cu n vˆrfuri P1 (x1 . yn+1 ) este de fapt tot punctul P1 (x1 . y0 ) sunt de aceea¸i parte a dreptei (Pi Pi+1 ).18.. POZITIA UNUI PUNCT FATA DE UN POLIGON CONCAV ¸ ¸˘ 447 Consider˘m un poligon convex cu n vˆrfuri P1 (x1 .. a a n ≥ 3 ¸i un punct P0 (x0 .Pn (xn yn ) este verificarea urm˘toarei relatii: a ¸ n ariepoligon (P1 P2 . y) = (y − yi ) · (xi+1 − xi ) − (x − xi ) · (yi+1 − yi ) Dreapta (Pi Pi+1 ) ˆ ımparte planul ˆ dou˘ semiplane. y1 )P2 (x2 . 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. Functia fi (x..Pn (xn yn ). a a ¸ Aceasta este o conditie necesar˘ dar nu ¸i suficient˘. y1 )P2 (x2 .. y1 )P2 (x2 . y0 ) pe frontiera poligonului). 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 . ın . j = i ¸i j = i + 1) au acela¸i semn cu fi (x0 .. y0 ) (sau s s sunt nule dac˘ accept˘m prezenta punctului P0 (x0 . 18. a Pentru a fi siguri c˘ punctul P0 (x0 . y0 )..

!!! { .*.. ¸ ¸ 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.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. 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. a ın P7 P6 P5 P1 P2 P3 P4 P1 P2 P3 P4 P7 P6 P5 a) b) 18.6 ˆ a¸ur˘toarea convex˘ Inf˘s a a ˆ Impachetarea Jarvis 18. Dac˘ punctul este ˆ interiorul unui poligon convex ¸ a ın obtinut prin partitionarea poligonului concav atunci el se afl˘ ˆ interiorul acestuia.io. // infasuratoare convexa class Jarvis1 // pe frontiera coliniare ==> le iau pe toate .448 ˘ CAPITOLUL 18.6..

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

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

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

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

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

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

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

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

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

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

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

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

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

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

b2 = (y1 + y2 )/2 ¸i a s a r2 = 1 (x2 − x1 )2 + (y2 − y1 )2 .. Ci ¸i a a s trebuie s˘ determin˘m cercul Ci+1 . sunt: P1 (x1 . Pi+1 dar impunˆnd a ınd s a conditia ca acest cerc s˘ treac˘ ˆ mod obligatoriu prin punctul Pi+1 (xi+1 .. yi+1 )..9 18. P2 . pas cu pas. b2 . ¸ 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. adic˘ cercul de diametru [P1 P2 ]. urmat de cel mai de a jos.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. yn ). . dup˘ ordonare. Presupunem c˘ punctele. ri ) cercul de centru (ai . Consider˘m cercul C2 (a2 .1 Probleme rezolvate Seceta . urmat de cel mai de sus. C3 .. Not˘m cu Ci (ai .Pi . CERC MINIM DE ACOPERIRE A PUNCTELOR 463 18.18.. bi ) ¸i raz˘ minim˘ ri care acoper˘ a s a a a punctele P1 . a a Dac˘ punctul Pi+1 se afl˘ ˆ interiorul cercului Ci atunci cercul Ci+1 este a a ın identic cu Ci .. . Pn (xn . y1 ). P2 (x2 .. Ovidiu Dom¸a s . . r2 ) unde a2 = (x1 + x2 )/2.. 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 . a a P3 (x3 . .ONI2005 clasa a IX-a lect.9. Pn . y2 ).. ¸ 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. cercurile C2 . urmat de cel mai din dreapta. y3 ).. dup˘ acestea urmeaz˘ celelalte puncte ˆ a a ıntr-o ordine oarecare). bi .. P2 .8. 2 S˘ presupunem c˘ am determinat..

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

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

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

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

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

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

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

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

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

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

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. semiaxa [Ox pe cateta de lungime a. pentru care se a s a a cunosc coordonatele vˆrfurilor lor. a . respectiv semiaxa [Oy pe cateta de lungime b.IN contine n + 1 linii cu urm˘toarele valori: s ¸ a n . s Pe suprafata magnetic˘ este desenat un triunghi dreptunghic cu lungimile ¸ a catetelor a.num˘rul de pari a x1 y1 d1 ./test a 18.9. respectiv b ¸i un sistem de coordonate xOy cu originea ˆ unghiul s ın drept al triunghiului. ¸ a a s Restrictii ¸i observatii: ¸ s ¸ 3 < N ≤ 200 num˘r natural a −1000 < xi .coordonatele initiale ¸i distanta pe care poate fi mutat parul 1 ¸ s ¸ x2 y2 d2 . adic˘ dac˘ sunt a ¸ a a ˆ ındeplinite conditiile: ¸ • nu exist˘ piese suprapuse.. xn yn dn . se ¸ s a s at obtine un teren avˆnd suprafata cu 30 de unit˘¸i mai mare decˆt terenul initial..18.0000 ın s s Explicatie: prin mutarea parilor 1 ¸i 2 cu cˆte 2 ¸i respectiv 3 unit˘¸i.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. Tat˘l lui Ionic˘ vrea s˘ verifice dac˘ pe tabl˘ a a a a a a piesele realizeaz˘ o partitie a triunghiului dreptunghic desenat.coordonatele initiale ¸i distanta pe care poate fi mutat parul n ¸ s ¸ Date de ie¸ire s ˆ fi¸ierul MOSIA. 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.4 Partitie . La un moment dat Ionic˘ a¸eaz˘ pe tabla magnetic˘ n piese.9.coordonatele initiale ¸i distanta pe care poate fi mutat parul 2 ¸ s ¸ . ¸ a ¸ at a ¸ Timp limit˘ de executare: 1 sec. PROBLEME REZOLVATE 483 Fi¸ierul MOSIA.

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

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

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

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

Fiecare ¸˘ran are un document prin care dovede¸te c˘ este ta s a proprietar pe o singur˘ suprafat˘ de teren de form˘ triunghiular˘.. y3 ordonate). ˆ fora ¸ ın matul: x1 y1 x2 y2 x3 y3.. 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. 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. n. adic˘: num˘rul de ¸˘rani care ˆ a a ta ındeplinesc conditia din cerint˘ ¸i apoi ¸ ¸a s codurile lor (ˆ ordine cresc˘toare). y2.. Din p˘cate. GEOMETRIE COMPUTATIONALA ¸ 1. 2. dac˘ nu exist˘ un astfel de ¸˘ran. sau cifra a a ta a 0.. ¸ Date de intrare Fi¸ierul de intrare triunghi. 2. x2. . x3 abscise. i = 1. fiind posibil ca ea s˘ fie revendiIn a a a a a a a a cat˘ de mai multi ¸˘rani. ˆ aceast˘ comun˘ exist˘ o fˆntˆn˘ cu ap˘. Pe ¸ ta linia i + 1 se afl˘ coordonatele colturilor suprafetei de teren triunghiulare detinute a ¸ ¸ ¸ de ¸˘ranul i. iar y1. pe s a ta urm˘toarele n linii cˆte 6 valori numere ˆ a a ıntregi separate prin cˆte un spatiu. adic˘: codul ¸˘ranului cu proprietatea cerut˘. n. 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 y ordonat˘). ce reprezint˘ coordonatele celor trei colturi ale suprafetei a ¸ ¸ triunghiulare detinute de un ¸˘ran (x1.in are pe prima linie num˘rul n de ¸˘rani. a a Date de ie¸ire s Fi¸ierul de ie¸ire triunghi. care include ta ¸ ¸ toate celelalte suprafete.. .488 ˘ CAPITOLUL 18. Pe linia a doua se va scrie ¸ ¸a r˘spunsul de la punctul b). 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˘.. Ultima linie a fi¸ierului (linia n + 2) va contine coordota s ¸ natele fˆntˆnii ˆ formatul x y. a a a b) Codul ¸˘ranului ce detine un document cu suprafata de teren. a ¸ Exemplu . O suprafat˘ de p˘mˆnt este dat˘ prin coordonatele celor a ¸ ta ¸a a a a trei colturi.out va contine pe prima linie r˘spunsul de la s s ¸ a punctul a). pe prima linie se va scrie cifra 0. iar fˆntˆna este considerat˘ punctiform˘ ¸i dat˘ prin coordonatele ¸ a a a a s a punctului. pentru c˘ sunt portiuni din suprafetele de p˘mˆnt care se reg˘sesc pe a a a ¸ ¸ a a a mai multe documente. Dac˘ nu exist˘ ¸˘rani a a ta cu conditia din cerint˘. doca ¸a a a a umentele dau b˘taie de cap primarului (care se ocup˘ de retrocedarea suprafetelor a a ¸ de p˘mˆnt). cu un spatiu ˆ ın a ¸ ıntre ele.

..out 212 2 489 Explicatie: ¸ La punctul a). n verific˘m dac˘ I este interior sau pe frontiera lui Ti . sunt doi ¸˘rani care detin suprafete de p˘mˆnt ce au ˆ interior ta ¸ ¸ a a ın sau pe frontier˘ fˆntˆna. T2 ..descriere solutie ¸ ¸ Descrierea solutiei (Prof. 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˘. 2. ta ¸ ¸a suprafetele de p˘mˆnt detinute de ceilalti ¸˘rani (cu codurile 1 ¸i 3).in 3 10 0 0 10 10 10 0 100 100 0 -100 0 0 0 10 0 0 10 10 5 secv... Astfel dea a a termin˘m triunghiul p de arie maxim˘. Tn triunghiurile corespunz˘toare suprafetelor ¸i cu I a a ¸ s punctul unde se g˘se¸te fˆntˆna. ¸˘ranul cu codul 2 detine o suprafat˘ de teren care include. 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 ). class Pereti { . a s a a Ti = Ai Bi Ci . . ˆ caz a a ın afirmativ nr = nr + 1 ¸i sol[nr] = i. . a a a s La punctul b). cu codurile 1 ¸i 2. a) nr = 0 Pentru i = 1.9.. Afi¸am nr ¸i vectorul sol. ˆ caz afirmativ se afi¸eaz˘ p. .io.18.1 secunde ¸ Indicatii de rezolvare . PROBLEME REZOLVATE secv. a a a ¸ ¸a a b) Dac˘ exist˘ un asemenea triunghi atunci el este de arie maxim˘. i = 1... n. Doru Popescu Anastasiu) ¸ Not˘m cu T1 .*.. Codul surs˘ a import java. a ın In s a altfel 0. ¸ a a ¸ ¸ ta s Timp maxim de executie/test: 0.

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

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

GEOMETRIE COMPUTATIONALA ¸ .492 ˘ CAPITOLUL 18.

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

TEORIA JOCURILOR .494 CAPITOLUL 19.

v) // 3.1. 20.1.1 Algoritmul Belmann-Ford pentru grafuri neorientate // drum scurt in graf neorientat cu costuri negative (dar fara ciclu negativ!) // Algoritm: 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. init // 2. repeta de n-1 ori // pentru fiecare arc (u.2.1 Secvent˘ de sum˘ maxim˘ ¸a a a 20.2 Exemple 20. OK=true 495 .Capitolul 20 Alti algoritmi ¸ 20.

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

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

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

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

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

static final int WHITE=0.nval. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("BellmanFordDAG. w=new int[n+1][n+1].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. sortare topologica O(n+m) 2.v. m=(int)st. // lista static int[] gi.2.2.j. // varfuri. muchii.nval.in"))). 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. // color[u]=BLACK ==> u in lista static int n. // culoare static int[] lista.v) OBS: O(n+m) import java.k. // d[u]=dist(nods. costul arcului int u.pozl.*.nextToken(). // p[u]=predecesorul nodului u public static void main(String[] args) throws IOException { int i.nextToken().s) 3. // grad interior static int[][] w.t.u) static int[] p.w.io. color=new int[n+1]. . // matricea costurilor static int[] d. n=(int)st.20. st. class BellmanFordDAG { static final int oo=0x7fffffff. pozitie in lista static int[] color. cost. time.m. init(G. pentru toate nodurile u in ordine topologica pentru toate nodurile v adiacente lui u relax(u. BLACK=1. st. int nods=1. // nod sursa.

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful