ALGORITMI SI STRUCTURI DE DATE ¸ Note de curs

(uz intern - draft v2.3)

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

ALGORITMICA (model virtual)

PROGRAMARE

Gandire algoritmica

Experienta (rezolvarea de probleme)

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

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

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

2 Aprobat˘ a

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

3.3

3.4

3.5

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. . . . .

. . . . .

. . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

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

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

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1.1. PROGRAME CIUDATE

3

1.1.3

Un program ciudat ˆ Java ın

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

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

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

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

4

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.1.4

Structura unui program Java

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

1.2. CONVERSII ALE DATELOR NUMERICE

5

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

1.2

Conversii ale datelor numerice

1.2.1

Conversia din baza 10 ˆ baza 2 ın

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

6

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.2.2

Conversia din baza 2 ˆ baza 10 ın

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

1.2.3

Conversii ˆ ıntre bazele 2 ¸i 2r s

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

Capitolul 2

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

2.1

Date ¸i structuri de date s

2.1.1

Date

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

8

CAPITOLUL 2. STRUCTURI DE DATE

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

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

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

2.1. DATE SI STRUCTURI DE DATE ¸

9

2.1.2

Structuri de date

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

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

10

CAPITOLUL 2. STRUCTURI DE DATE

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

2.2

Structuri ¸i tipuri de date abstracte s

2.2.1

Structuri de date abstracte

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

2.2.2

Tipuri de date abstracte

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

2.3. STRUCTURI DE DATE ELEMENTARE

11

2.3

Structuri de date elementare

2.3.1

Liste

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

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

12

CAPITOLUL 2. STRUCTURI DE DATE

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

2.3.2

Stive ¸i cozi s

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. B2 .. 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.. B0 = 1. . . S˘ se afi¸eze B1 .50 CAPITOLUL 3.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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. un program recursiv de cˆteva a a a a linii poate rezolva o problem˘ apriori complicat˘. RECURSIVITATE Aceste cˆteva linii de program arat˘ foarte bine cum generalizˆnd problema. Aceasta este forta recursivit˘¸ii a a ¸ at ¸i a rationamentului prin recurent˘.76 CAPITOLUL 5.

pentru n ≥ 0. x1 .1 Relatii de recurent˘ ¸ ¸a O ecuatie ˆ care necunoscutele sunt termenii xn .. xn+1 ..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).. apelurile recursive trebuie folosite cu discern˘mˆnt. xk−1 .1) atunci ea se nume¸te relatie de recurenta de ordinul k cu coeficienti constanti. a0 . C0 = 1 este de ordinul 1. ın ¸ 6. ¸ ¸ ın a s 77 .. a as a Pe de alt˘ parte.. Aceast˘ ecuatie poate s ¸ ¸˘ a ¸ fi satisf˘cut˘ de o infinitate de ¸iruri. a ¸ Vom vedea ˆ continuare cum pot fi rezolvate astfel de recurente..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. s ¸ ¸˘ ¸ ¸ Coeficientii sunt constanti ˆ sensul c˘ nu depind de valorile ¸irului xn . Ca s˘ putem rezolva ecuatia (relatia de a a s a ¸ ¸ recurent˘) mai avem nevoie ¸i de conditii initiale. ai ∈ R. . k ≥ 1. ak = 0 (6. as Analiza unui algoritm recursiv implic˘ rezolvarea unui sistem de recurente. De exemplu relatia de recurent˘ ¸ ¸a (n + 2)Cn+1 = (4n + 2)Cn .. .1. Cel mai important cˆ¸tig al exprim˘rii recursive este ın as a faptul c˘ ea este natural˘ ¸i compact˘. adic˘ de valorile termenilor ¸a s ¸ ¸ a x0 . Dac˘ un ¸ir xn de numere satisface o formul˘ de forma a s a a0 xn + a1 xn+1 + . + ak xn+k = 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. introducˆnd expresiile ri ˆ relatia de recurent˘. ..78 CAPITOLUL 6.. ck se pot determina din conditiile initiale... atunci relatia de recurent˘ a a a ¸ ¸a are solutia general˘ ¸ a n n n xn = c1 r1 + c2 r2 + . a adic˘ relatia de recurent˘ care define¸te ¸irul numerelor lui Fibonacci.1. Atunci a a a a ¸ rn ...1..1. . F0 = 0.. F˘cˆnd substitutia ¸ ¸˘ a a ¸ xn = rn obtinem urm˘toarea ecuatie.. 2.4) unde coeficientii c1 .. + ak rk = 0 (6. c2 .. + cp−1 np−1 rn (6. Ea este o a ¸ ¸a s s relatie de recurent˘ de ordinul 2 cu coeficienti constanti. + ak ri = 0 + . + ak ri + a2 ri a0 ri + a1 ri unde xn )|i ∈ {1. + ck rk (6. atunci ri sunt a a a ¸ solutii ale relatiei de recurent˘....2) 6.1.1. k) sunt distincte.. ¸ ¸ ¸ • Ecuatia caracteristic˘ admite r˘d˘cini reale multiple ¸ a a a Fie r o r˘d˘cin˘ multipl˘ de ordinul p a ecuatiei caracteristice. ¸ ¸ ¸a n ˆ ın ¸ ¸a ¸ Intr-adev˘r.5) . np−1 rn sunt solutii liniar independente ale relatiei de recurent˘ ¸i ¸ ¸ ¸a s xn = c1 + c2 n + . ¸ ¸a ¸ ¸ 6. . 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 ...1. 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 + .. k} sunt solutii liniar independente ale relatiei de recurent˘ ¸ ¸ ¸a Dac˘ r˘d˘cinile ri (i = 1... nrn .. obtinem: a a n+k n+2 n+1 2 k n n = ri a0 + a1 ri + a2 ri + . ..3) (se mai numesc ¸i sistem fundamental de solutii). r2 ..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. n2 rn . 2. rk sunt r˘d˘cini reale ale ecuatiei caracteristice... F1 = 1.

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

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

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

dac˘ f (n) = O nlogb (a−ε) cu ε > 0 atunci T (n) = Θ nlogb a a 2.2. Solutia dat˘ de teorema master este: ¸ a 1. ≤ c·f (n) cu c < 1 atunci T (n) = Θ (f (n)). 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 ).2. 6. ¸i ea are noduri ¸ a a a ¸ s descendente care sunt noduri r˘d˘cin˘ pentru arborele provenit din T (n/b).82 cu solutia: ¸ CAPITOLUL 6. a a a . ¸i a ¸ a ¸ s multe recurente utile nu sunt de forma (6. teorema Master nu functioneaz˘ pentru toate functiile 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˘). dac˘ f (n) = Θ nlogb a atunci T (n) = Θ nlogb a lg n a 3.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+ε) ¸i a·f a s n b Din p˘cate. R˘d˘cina arborelui contine valoarea 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.6). Pentru a rezolva astfel de ecuatii recurente vom reprezenta arborele generat ¸ de ecuatia recursiv˘. 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.

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

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

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

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.86 CAPITOLUL 6. T (n) = c1 n2 + c2 n2 lg n ¸i obtinem s ¸ ˆ sfˆr¸it. 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. s ¸ a Atunci. 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 . ˆ 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 . 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 . tk = c1 4k + c2 2k . n > 1 c fiind o constant˘. ANALIZA ALGORITMILOR RECURSIVI ˆ care ˆ ın ınlocuim pe n cu 2k .

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

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

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

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

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

. ANALIZA ALGORITMILOR RECURSIVI ˆ cazul doi din teorema Master. 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. a a la fel ca ˆ cazul doi din teorema master. Se obtine ¸ T (n) = Θ(4lg lg n n) = Θ(n log2 n). Avem o serie geometric˘ cresc˘toare.92 CAPITOLUL 6. deci nu trebuie decˆt s˘ avem grij˘ de ın a a a aceste niveluri. Suma nodurilor de pe nivelul i este 4i n. Se obtine: a ¸ T (n) = Θ(2lg lg n n) = Θ(n log n). 13.

. Atunci a ¸ T (n) = 2T (n/2) + 2 ¸i T (2) = 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. x[i]) Evident se fac 2n − 2 comparatii. for i=2. 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.Capitolul 7 Algoritmi elementari 7. vmax = x[1]. La fel pentru vmax.n. s Cum rezolv˘m aceast˘ relatie de recurent˘? B˘nuim c˘ solutia este de forma a a ¸ ¸a a a ¸ T (n) = an + b. Apar cˆte dou˘ comparatii ˆ plus de fiecare s a a ¸ ın dat˘. Prelucrarea se repet˘ pentru cele dou˘ s a a zone (deci se folose¸te recursivitatea). Se poate mai repede? Da! ˆ artim ¸irul ˆ ¸ Imp˘ ¸ s ın dou˘ ¸i determin˘m vmin ¸i vmax ˆ cele dou˘ zone. x[i]) vmax = maxim(vmax. n vmin = minim(vmin. Proced˘m astfel: a vmin = x[1]. Compar˘m vmin1 cu vmin2 as a s ın a a ¸i stabilim vminm.1 Operatii cu numere ¸ 7.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 .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1.3.2) (8.1) − 1− √ 5 n . Primele numere Fibonacci sunt: 0.3) (8.3.3.9) (8. F1 = 1. 21..3. a a Folosind aceast˘ descompunere.3... + F2n F2n+1 Teorema 2 Orice num˘r natural n se poate descompune ˆ a ıntr-o sum˘ de numere a Fibonacci. Se poate ar˘ta c˘ a a Fn = 2n 1 √ 5 1+ √ 5 n (8. + F2n−1 F2n = F2n+1 − 1 = F2n = Fn Fn+1 2 = F2n 2 = F2n+1 − 1 (8.10) (8. NUMERE REMARCABILE 115 8.6) 2 Fn+1 Fn−1 − Fn Fn+m Fnk = Fm Fn+1 + Fm−1 Fn = multiplu de Fk ¸i s F2 + F4 + . 1.3.3.8) (8. 610.3.. 3. + F2n F1 + F3 + . 8. 144. Fn = Fn−1 + Fn−2 pentru n ≥ 2. ca de exemplu: at 1 1 1 0 n = = Fn+1 Fn (−1)n Fn Fn−1 (8. 377. 89. 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. 233. 1597... + Fn F1 F2 + F2 F3 + . Dac˘ nu se folosesc ˆ descompunere numerele F0 ¸i F1 ¸i nici dou˘ a ın s s a numere Fibonacci consecutive.7) (8. Numerele lui Fibonacci satisfac multe identit˘¸i interesante. 34.11) F1 F2 + F2 F3 + .3. atunci aceast˘ descompunere este unic˘ abstractie a a ¸ f˘cˆnd de ordinea termenilor. numerele naturale pot fi reprezentate asem˘n˘tor a a a reprezent˘rii ˆ baza 2.5) (8. 5.3. 987. .3.. 55.4) (8. 13.. + F2n−1 2 2 2 F1 + F2 + .8.. In a a a ..1 Numerele lui Fibonacci Numerele lui Fibonacci se pot defini recursiv prin: F0 = 0. 2.3.3...

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

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

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

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

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

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

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

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

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

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

126 CAPITOLUL 8. ALGORITMI COMBINATORIALI .

De exemplu. return 1. else return 1. 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. } 127 . nume. ++i) if (x. while (i < N && !x. } se poate scrie ¸i sub forma: s static int cauta (String x) { int i = 0.equals(nume[i])) ++i.equals(nume[i])) return telefon[i]. o tabel˘ cu a a a trei informatii: num˘r curent. s a 9. 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. if (i < N) return telefon[i].2 C˘utarea secvential˘ a ¸ a static int cauta (String x) { for (int i = 0.Capitolul 9 Algoritmi de c˘utare a 9. i < N.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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ALGORITMI ELEMENTARI DE SORTARE .144 CAPITOLUL 10.

prin ad˘ug˘ri sau ¸tergeri de elemente pe parcursul ¸ a a s prelucr˘rii. ¸ a independent de natura elementelor sale. Lista urmator. etc. obiecte informatice complexe. ¸iruri de caractere. 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. numit ¸i pointer. 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. 145 . ˆ sensul c˘ num˘rul elementelor variaz˘ ˆ ın a a a ın cursul executiei programului. a class Lista { int continut. 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. 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.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. Mai precis. Nu suntem acum interesati s ¸ de elementele acestui ansamblu ci de operatiile care se efectueaz˘ asupra acestuia.

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

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

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

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

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

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

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

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

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

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

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

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

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

} static Lista cons (Object x. Lista a) { info = x. a a .info. } Procedurile asupra listelor construiesc o list˘ plecˆnd de la alte dou˘ liste. Functia Tail este o primitiv˘ clasic˘.5 Operatii asupra listelor ¸ ˆ aceast˘ sectiune sunt prezentati cˆ¸iva algoritmi de manipulare a listelor. Lista a) { return new Lista (x. next = a. cele dou˘ liste a a In a a nu sunt modificate. ˆ prima procedur˘ append. 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. partajeaz˘ finalul listei rezultat cu al doilea argument. ˆ a doua procedur˘. dac˘ append copiaz˘ ¸ a a a primul s˘u argument.5. Totusi. a).4: Suprimarea primului element din list˘ a class Lista { Object info. a a a pun o list˘ la cap˘tul celeilalte liste.11.")."). return a. Lista next.next. } static Lista tail (Lista a) { if (a == null) erreur ("Tail d’une liste vide. concat. se poate remarca faptul c˘. return a. } static Object head (Lista a) { if (a == null) erreur ("Head d’une liste vide. Lista(Object x. ea suprim˘ primul element al listei. OPERATII ASUPRA LISTELOR ¸ 159 11.

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

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

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

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

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

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

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

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

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

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

readLine()). nrDiscuri=Integer. char tijaIntermediara. char tijaSursa.parseInt(br. BufferedReader br = new BufferedReader(new InputStreamReader(System. 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. solutiaHanoi(nrDiscuri. System. ’B’.io. public static void main(String[] args) throws IOException { int nrDiscuri. class Hanoi { static int nrMutare=0. ’C’).*. ’A’. char tijaDestinatie) { if(nrDiscuri==1) .print("Introduceti numarul discurilor: ").out. a • un disc poate fi a¸ezat doar peste un disc de diametru mai mare decˆt s a al s˘ sau pe o tij˘ goal˘. 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˘). }// main() static void solutiaHanoi(int nrDiscuri.in)).170 CAPITOLUL 12.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pentru a ajunge la musc˘. a s a ¸ − pe linia a patra. reprezentˆnd coordonatele capetelor celor k portiuni a ¸ a ¸ de plas˘ deteriorate (linia ¸i apoi coloana pentru fiecare cap˘t). a ¸ reprezentˆnd linia ¸i respectiv coloana pe care se afl˘ initial musca.out va contine pe prima linie un num˘r natural s s ¸ a min reprezentˆnd lungimea drumului minim parcurs de p˘ianjen. separate printr-un spatiu. ¸ s a . a a s a − pe a doua linie dou˘ numere naturale lp cp. cˆte un nod pe o linie. reprezentˆnd num˘rul de portiuni a a a ¸ de plas˘ deteriorate. cˆte patru valori naturale l1 c1 l2 c2. a ¸ reprezentnd linia ¸i respectiv coloana nodului ˆ care se afl˘ initial p˘ianjenul. Datele de intrare Fi¸ierul de intrare paianjen. a − pe fiecare dintre urm˘toarele k linii. a ¸ reprezentˆnd num˘rul de linii ¸i respectiv num˘rul de coloane ale plasei.in contine: s ¸ − pe prima linie dou˘ numere naturale m n. 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. exprimat ˆ a a ın num˘r de segmente de lungime 1. a a separate prin cˆte un spatiu. Dac˘ problema are mai ¸ a multe solutii. se va afi¸a una singur˘. De a ¸ a asemenea. Pe urm˘toarele min + 1 linii sunt scrise nodurile a a prin care trece p˘ianjenul.198 0 1 2 3 4 5 6 CAPITOLUL 13. Pentru fiecare nod sunt scrise a a linia ¸i coloana pe care se afl˘. separate printr-un spatiu. s a ¸ Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ m. s ın a ¸ a − pe linia a treia dou˘ numere naturale lm cm separate printr-un spatiu. un num˘r natural k. folosind doar portiunile sigure ale plasei. a s a Datele de ie¸ire s Fi¸ierul de ie¸ire paianjen. separate printr-un spatu. 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[i.1 secunde ¸ a s pentru Linux. De asemenea. PROBLEME REZOLVATE 199 • Portiunile nesigure sunt specificate ˆ fi¸ierul de intrare ˆ ¸ ın s ıntr-o ordine oarecare. C. Indicatii de rezolvare * ¸ prof. Carmen Popescu. j): bitul 0 este 1 dac˘ ¸ ın a p˘ianjenul se poate deplasa ˆ sus. a iar portiunile nesigure sunt desenate punctat. utilizˆnd. a • Se acord˘ 30% din punctaj pentru determinarea lungimii drumului minim a ¸i 100% pentru rezolvarea ambelor cerinte. ”Gh.in 97 23 74 8 2425 2333 3031 3335 4454 6465 6575 7273 paianjen. ¸ s a de asemenea un algoritm de tip BF. N.ˆ jos. . cm] va ın a contine lungimea drumului minim. s ¸ Exemplu paianjen. ¸ Timp maxim de executie/test: 1 secund˘ pentru Windows ¸i 0. ın a Rezolvarea se bazeaz˘ pe parcurgerea matriciei ¸i retinerea nodurilor parcurse a s ¸ ˆ ıntr-o structur˘ de date de tip coad˘ (parcurgere BF . a a Drumul minim al p˘ianjenului se retine ˆ a ¸ ıntr-o alt˘ matrice B. Laz˘r” Sibiu a Plasa de p˘ianjen se memoreaz˘ ˆ a a ıntr-o matrice A cu M linii ¸i N coloane. Deci elementul B[lm.2. s fiecare element reprezentˆnd un nod al plasei.la stˆnga. respectiv o valoare pozitiv˘ reprezentˆnd pasul a a a la care a ajuns paianjenul ˆ drumul lui spre musc˘. 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. j] va codifica pe patru biti a ¸ directiile ˆ care se poate face deplasarea din punctul (i.13.algoritm Lee). Oricare dou˘ portiuni nesigure orizontale se pot intersecta cel mult ˆ a ¸ ıntr-un cap˘t.out 8 23 22 32 42 52 62 63 73 74 Explicatie ¸ Problema corespunde figurii de mai sus. j] este a 0 dac˘ nodul (i. bitul 1 este 1 dac˘ se poate deplasa la dreapta. Traseul optim este desenat cu linie groas˘. a ın a bitul 2 . bitul 3 . oricare dou˘ portiuni nesigure verticale se pot intersecta cel a a ¸ mult ˆ ıntr-un cap˘t. ¸ Reconstituirea drumului minim se face pornind de la pozitia mu¸tei. unde B[i. j) nu este atins.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GREEDY Cerint˘ ¸a Cunoscˆndu-se num˘rul n de detinuti ¸i pretentiile lor.256 CAPITOLUL 14. iar pe linia s a 2j + 1 vor fi scrise numerele de ordine ale detinutilor care sap˘ ˆ aceast˘ zon˘. METODA OPTIMULUI LOCAL . ¸ ¸ ın ¸ Date de intrare Fi¸ierul de intrare sant.in are pe prima linie num˘rul n de detinuti. 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. ¸ ¸ a ın a a separate prin cˆte un spatiu. a ¸ a Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ n ≤ 10000 • 0 ≤ ai ≤ bi ≤ 250. ordonate cresc˘tor. a ¸ ¸ a Date de ie¸ire s Fi¸ierul de ie¸ire sant. s˘ se determine locul a a ¸ ¸ s ¸ a (locurile) unde vor fi pu¸i detinutii s˘ sape ˆ s ¸ ¸ a ıntr-o zi de munc˘. ai bi . Intervalul ˆ care poate p˘zi un paznic nu poate contine dou˘ sau a ın a ¸ a mai multe zone disjuncte dintre cele exprimate de detinuti ˆ preferintele lor. 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 . pentru orice i. care reprezint˘ pretentia detinutului. Pe fiecare s a ¸ ¸ dintre urm˘toarele n linii exist˘ cˆte dou˘ numere naturale. 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. respectˆndu-se a a pretentiile lor. astfel ˆ at num˘rul de paznici necesari pentru a p˘zi cei n detinuti ¸ ıncˆ a a ¸ ¸ s˘ fie minim. 1 ≤ i ≤ n • 0 ≤ xj ≤ yj ≤ 250.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. 2j+1) va contine pe linia 2j trei numere ¸ ¸ ¸ naturale p xj yj . pentru orice j. astfel: fiecare pereche de linii (2j.

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

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

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

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

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

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

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

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

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

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

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

268 CAPITOLUL 14. METODA OPTIMULUI LOCAL .GREEDY .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

v . } }//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. lista[pozLista--]=u.." in lista ("u se termina inaintea lui v") Algoritm: 1.. //"at" //if((a[lista[j]][u]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]).v<=n. ctc[u]=nctc. . METODA BACKTRACKING for(v=1. color[u]=BLACK..*. color[u]=GRAY.13 Sortare topologic˘ a Folosind parcurgerea ˆ adˆncime ın a // // // // // // // // Sortare Topologica = ordonare lineara a varfurilor (in digraf) (u. u=nod 2..v)=arc ==> ".. cand u=terminat ==> plasaz in lista pe prima pozitie libera de la sfarsit catre inceput Solutia nu este unica (cea mai mica lexicografic = ???) O(n*n)= cu matrice de adiacenta O(n+m)= cu liste de adiacenta import java.io.3.j<=n.j++) if((at[u][lista[j]]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]). for(j=1. //"a" color[u]=BLACK. } static void dfsat(int u) // se poate folosi "a" inversand arcele ! { int j. u .316 CAPITOLUL 15. DFS pentru calcul f[u]. f[u]=++t.v++) if((a[u][v]==1)&&(color[v]==WHITE)) dfsa(v)..

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

342 CAPITOLUL 15. METODA BACKTRACKING .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

372 }// for h

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

16.2. PROBLEME REZOLVATE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 2 0 0 0 0 0 2 3 3 0 0 0 0 3 4 4 4 0 0 0 4 4 4 4 5 0 0

373

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

16.2.10

Triangularizarea poligoanelor convexe

Consider˘m un poligon convex cu n vˆrfuri numerotate cu 1, 2, ..., n (ˆ figur˘ a a ın a n = 9) ¸i dorim s˘ obtinem o triangularizare ˆ care suma lungimilor diagonalelor s a ¸ ın trasate s˘ fie minim˘ (ca ¸i cum am dori s˘ consum˘m cˆt mai putin tu¸ pentru a a s a a a ¸ s trasarea acestora!).
1 2 9 8 7 3 4 6 5 3 4 5 5 5 6 4 555 2 1 9 9 8 7 3 6 6 2 11 9 9

1

8 8

8 7

7

Evident, orice latur˘ a poligonului face parte dintr-un triunghi al triangulatiei. a ¸ Consider˘m la ˆ a ınceput latura [1, 9]. S˘ presupunem c˘ ˆ a a ıntr-o anumit˘ triangulatie a ¸ optim˘ latura [1, 9] face parte din triunghiul [1, 5, 9]. Diagonalele triangulatiei a ¸ optime vor genera o triangulatie optim˘ a poligoanelor convexe [1, 2, 3, 4, 5] ¸i ¸ a s [5, 6, 7, 8, 9]. Au ap˘rut astfel dou˘ subprobleme ale problemei initiale. a a ¸ S˘ not˘m prin p(i, k, j) perimetrul triunghiului [i, k, j] (i < k < j) i¸i prin a a s c[i][j] costul minim al triagulatiei poligonului convex [i, i + 1, ..., j] (unde i < j). ¸ Atunci: c[i][j] = 0, dac˘ j = i + 1 a

374 ¸i s

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

16.2.11

Algoritmul Roy-Floyd-Warshall

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

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

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

// drumuri intre i si j care trec

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

375

3 1 2 1 3 2

2 3 1 * * *

3 4 2 * * *

1 2 2 * * *

* * * 2 1 2

* * * 1 2 3

* * * 2 3 4

16.2.12

Oracolul decide - ONI2001 cls 10

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

376

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

377

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

378

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

379

380

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

381

16.2.13

Pav˘ri - ONI2001 clasa a X-a a

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

428 ˘ CAPITOLUL 16. PROGRAMARE DINAMICA .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

444 CAPITOLUL 17. POTRIVIREA SIRURILOR ¸ .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

492 ˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ .

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

494 CAPITOLUL 19. TEORIA JOCURILOR .

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful