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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

86 CAPITOLUL 6. ajungem la recurenta a tk = 4tk−1 + 4k cu ecuatia caracteristic˘ ¸ a ¸i solutia general˘ tk = c1 42 + c2 k42 . tk = c1 4k + c2 2k . T (n) = c1 n2 + c2 n2 lg n ¸i obtinem s ¸ ˆ sfˆr¸it. 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 . s ¸ a Atunci. ANALIZA ALGORITMILOR RECURSIVI ˆ care ˆ ın ınlocuim pe n cu 2k . n > 1 Procedˆnd la fel. 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. ˆ 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 . s˘ consider˘m ¸i exemplul In a s a a s T (n) ∈ O(n2 log n|n este o putere a lui 2) T (n) = 3T (n/2) + cn. n > 1 c fiind o constant˘.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

34. 1597.3.8..3) (8. Primele numere Fibonacci sunt: 0. 144. Dac˘ nu se folosesc ˆ descompunere numerele F0 ¸i F1 ¸i nici dou˘ a ın s s a numere Fibonacci consecutive.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. Fn = Fn−1 + Fn−2 pentru n ≥ 2. 1.3.3.10) (8.. 233. Numerele lui Fibonacci satisfac multe identit˘¸i interesante.3.3.7) (8..3.. 89.2) (8.11) F1 F2 + F2 F3 + .. + Fn F1 F2 + F2 F3 + . + F2n F1 + F3 + .3. + F2n F2n+1 Teorema 2 Orice num˘r natural n se poate descompune ˆ a ıntr-o sum˘ de numere a Fibonacci.1) − 1− √ 5 n . 2. 1. NUMERE REMARCABILE 115 8. 610. 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. In a a a .1 Numerele lui Fibonacci Numerele lui Fibonacci se pot defini recursiv prin: F0 = 0. ca de exemplu: at 1 1 1 0 n = = Fn+1 Fn (−1)n Fn Fn−1 (8. 5. 13. 8.. F1 = 1. 21.3. a a Folosind aceast˘ descompunere...3.5) (8. Se poate ar˘ta c˘ a a Fn = 2n 1 √ 5 1+ √ 5 n (8. 3. + F2n−1 F2n = F2n+1 − 1 = F2n = Fn Fn+1 2 = F2n 2 = F2n+1 − 1 (8..9) (8. + F2n−1 2 2 2 F1 + F2 + .4) (8. 987. 377.3. 55.3. .3..6) 2 Fn+1 Fn−1 − Fn Fn+m Fnk = Fm Fn+1 + Fm−1 Fn = multiplu de Fk ¸i s F2 + F4 + ..8) (8.

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

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

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

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

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

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

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

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

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

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

ALGORITMI COMBINATORIALI .126 CAPITOLUL 8.

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

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

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

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

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

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

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

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

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

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

¸ ın a static void sortBule() { int t.10. a[j1] = a[j]. j <= i. } } 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.2. SORTARE PRIN SELECTIE ¸ 137 Se poate calcula de asemenea foarte u¸or num˘rul de operatii ¸i se obtine un s a ¸ s ¸ num˘r de ordinul O(N 2 ) comparatii ¸i. eventual interschimb˘ri (dac˘. ++j) if (a[j1] > a[j]) { t = a[j1]. de exemplu. i) for (int j = 1. i >= 0. a ¸ s a a tabloul este initial ˆ ordine descresc˘toare). 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 . a[j] = t.

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

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

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

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

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

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

144 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

dac˘ exist˘ mai multe sub¸iruri la care se ¸ a a s poate ad˘uga xi . bn ]..3. b2 ]. b1 ]. 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˘. ıncˆ a a Metoda de rezolvare este urm˘toarea: se sorteaz˘ intervalele cresc˘tor dup˘ a a a a cap˘tul din dreapta. EXEMPLE 1 2 modificat → 1 2 final → 1 2 rezolvat → × × Acum vom c˘uta o cutie a cutia goal˘.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. 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 ... se va alege acela care are cel mai mare ultim element. se parcurg intervalele. dac˘ nu exist˘ un astfel de sub¸ir se creeaz˘ un sub¸ir nou a as a a s a s care va contine ca prim element pe xi .3.8 Problema intervalelor disjuncte Se consider˘ n intervale ˆ a ınchise pe axa real˘ [a1 . a a 14. Se a cere selectarea unor intervale disjuncte astfel ˆ at num˘rul acestora s˘ fie maxim. ¸ ¸ 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. Acest fapt permite utilizarea s a s a c˘ut˘rii binare pentru determinarea sub¸irului potrivit pentru elementul xi a a s atunci cˆnd prelucr˘m acest element. s s a s a Metota de rezolvare este urm˘toarea: se parcurg elementele ¸irului initial unul a s ¸ dup˘ altul ¸i pentru fiecare element xi se caut˘ un sub¸ir existent la care xi se a s a s poate ad˘uga la sfˆr¸it. [an . [a2 . . se selecteaz˘ primul interval.3.14.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

cost. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("dijkstraOrientat.nextToken(). for(k=1.k<=m.k. static int n.nextToken().nval. } nodSursa=1.m.nval. for(k=1.i++) for(j=1. static int[] hnod. i=(int)st.k<=n.nextToken().k++) { if(d[k]<oo) { . j=(int)st. st. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("dijkstraOrientat.nextToken().costa=0.nextToken(). METODA OPTIMULUI LOCAL .k++) { st. w[i][j]=cost.GREEDY static final int oo=0x7fffffff.j<=n.out"))). static int[]p.238 { CAPITOLUL 14. w=new int[n+1][n+1]. static int[]d. st. cost=(int)st. static int[][]w.j++) w[i][j]=oo. st. st.nval. static int[] hd. for(i=1.j. n=(int)st.nval. dijkstra(nodSursa). m=(int)st.nodSursa. static int[] pozh. // matricea costurilor // distante de la nod catre arbore // predecesorul nodului in arbore // hd[i]=distanta din pozitia i din heap // hnod[i]= nodul din pozitia i din heap // pozh[i]=pozitia din heap a nodului i public static void main(String[]args) throws IOException { int i.nval.i<=n.in"))).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GREEDY .268 CAPITOLUL 14. METODA OPTIMULUI LOCAL .

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

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

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

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

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

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

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

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

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

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

o afi¸am ¸i revenim pe nivelul anterior. Mai precis. . ın ¸ a s a − cˆnd am g˘sit un succesor.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. A2 .k++) x[k]=gol. A2 . An . a a ¸ • pornind de la restrictiile problemei se stabilesc conditiile de validitate ale ¸ ¸ solutiilor partiale (conditiile de continuare). ˆ caz afirmativ. ¸ ¸ s a 15. ¸ • se identific˘ multimile A1 . 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). 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. –k.. a − dac˘ nu avem succesor.. verific˘m dac˘ este valid. Principiul de functionare al functiei a a a a ¸ f (primul apel este f (1)) corespunz˘tor unui nivel k este urm˘torul: a a − ˆ situatia ˆ care avem o solutie.2. 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 . .2. ˆ caz contrar urmˆnd a se continua a ın a c˘utarea succesorului.. ¸ ¸ ¸ ˆ 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).2.. a a ¸ ¸ a 15. initiarizarea vectorului solutie ¸ ¸ k=1.15. altfel m˘resc ¸i r˘mˆn pe pozitie a s a a else x[k–]=gol. a . a a a a In procedura se autoapeleaz˘ pentru k + 1. se initializeaz˘ nivelul ¸i se caut˘ un succesor.. ¸ ¸ ¸ x[k] = i ˆ ınseamn˘ c˘ pe pozitia k din solutia x se afl˘ ai din Ak .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].

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

METODA BACKTRACKING .342 CAPITOLUL 15.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4 3.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.close(). A hot˘rˆt ca banii s˘ fie distribuiti conform celei de-a N -a posibilit˘¸i din aa a ¸ at ordinea lexicografic˘. s a at ımp˘ ¸ . precum ¸i modul ˆ care se face a at ımp˘ ¸ s ın aceast˘ ˆ artire conform cu a N -a posibilitate din ordinea lexicografic˘. Fiind un tip original ¸i pasionat de matematic˘ a scris un testaa s a ment inedit. 5 2.2. System. a Cerint˘ ¸a Scrieti un program care pentru numerele S. sau at s at • 6 (unit˘¸i primului mo¸tenitor) 1 (unitate celui de-al doilea).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. Pentru exemplul de mai sus: ımp˘ ¸ ın a 4 2 1. 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 • 4 (unit˘¸i primului mo¸tenitor) 2 (unit˘¸i celui de-al doilea) 1 (unitate celui at s at de-al treilea). out. ˘ CAPITOLUL 16.println("Timp = "+(t2-t1)). sau at s • 5 (unit˘¸i primului mo¸tenitor) 2 (unit˘¸i celui de-al doilea). N date s˘ calculeze ¸i s˘ afi¸eze ¸ a s a s num˘rul total de posibilit˘¸i de ˆ artire a averii. Testamentul contine dou˘ numere naturale: S reprezentˆnd averea ce ¸ a a trebuie ˆ artit˘ mo¸tenitorilor ¸i N reprezentˆnd alegerea sa pentru ˆ artirea ımp˘ ¸ a s s a ımp˘ ¸ averii. a ın a De exemplu dac˘ averea ar fi 7 unit˘¸i monetare. Italag le-a scris ˆ ordine lexicografic˘.412 out. }// main() }// class 16. sau at s • 7 (unit˘¸i doar primului mo¸tenitor). 6 1.println(minsol). 7. a a ¸ a Datele de ie¸ire s Fi¸ierul de ie¸ire avere. 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. PROGRAMARE DINAMICA t2=System. 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.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.currentTimeMillis(). Italag decide s˘-¸i ˆ a s ımpart˘ toat˘ averea.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

PROGRAMARE DINAMICA .428 ˘ CAPITOLUL 16.

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

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

2.KMP Algoritmul KMP (Knuth-Morris-Pratt) determin˘ potrivirea ¸irurilor folosind a s informatii referitoare la potrivirea sub¸irului cu diferite deplasamente ale sale.2 Un algoritm eficient . ¸ Dorim s˘ avans˘m cu mai mult de un pas la o nou˘ ˆ a a a ıncercare. f˘r˘ s˘ risc˘m aa a a s˘ pierdem vreo solutie! a ¸ .17. UN ALGORITM EFICIENT .KMP 431 17. ¸ s Pentru t: 1 a ¸i s 1 a 2 b 3 a 1 a 3 a 4 b 2 b 4 b 5 a 3 a 5 a a 1 5 a 6 b 4 a 6 b b 2 6 b 7 a 5 b 7 a a 3 7 a 8 a 6 a 8 a a 4 8 a 9 b 7 b 9 b b 5 9 b 10 a 11 b 12 a 13 a 14 b 15 a 16 b 17 a p: 2 b exist˘ dou˘ potriviri: a a 10 a a 6 10 a a 1 11 b b 7 11 b b 2 12 a 13 a 14 b 15 a 16 b 17 a t: p: t: p: 1 a 2 b 3 a 4 b 12 a a 3 13 a a 4 14 b b 5 15 a a 6 16 b b 7 17 a Sirul ineficient de ˆ ¸ ıncerc˘ri este: a 1 a a 2 b b a 3 a a b a 4 b a a b a 5 a b a a b a 6 b a b a a b a 7 a b a b a a b a 8 a b a b a a b a 9 b 10 a 11 b 12 a 13 a 14 b 15 a 16 b 17 a t: p: p: p: p: p: p: p: p: p: p: p: b a b a a b a b a b a a b a b a b a a b a 1 * b a b a a b 2 b a b a a 3 b a b a 4 b a b 5 b a 6 * b 7 Prima nepotrivire din fiecare ˆ ıncercare este evidentiat˘ prin caracter boldat ¸ a iar solutiile sunt marcate cu *.

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

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

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

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

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

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

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

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

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

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

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

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

444 CAPITOLUL 17. POTRIVIREA SIRURILOR ¸ .

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

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

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

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

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

y[k]=(int)st.io.k++) { st. static int [] y. // urmator .. }// infasurareJarvis() public static void main(String[] args) throws IOException { int k. i1=i2.nextToken().readLine(). afisv(p. x[k]=(int)st.nextToken().nval.out. // npic=nr puncte pe infasuratoarea convexa static int [] x.print("u : "). System. st.nval. } infasurareJarvis(). !!! { static int n.println(npic+" --> "+i1).n). // infasuratoare convexa class Jarvis2 // pe frontiera coliniare ==> iau numai capetele . //br. p=new int[n+1].out.*. GEOMETRIE COMPUTATIONALA ¸ } u[i1]=i2.in"))).print("p : ").n). x=new int[n+1]. npic++. n=(int)st. y=new int[n+1]. } while(i2!=i0). npic--. StreamTokenizer st= new StreamTokenizer( new BufferedReader(new FileReader("jarvis.1. afisv(u. System. st. // apare de doua ori primul punct ! System. p[i2]=i1. static int [] p. u=new int[n+1].450 ) i2=i.k<=n.nval. for(k=1.npic=0.1.nextToken(). // 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..out. ˘ CAPITOLUL 18.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GEOMETRIE COMPUTATIONALA ¸ .492 ˘ CAPITOLUL 18.

2 Exemple 493 .1 Prezentare general˘ a 19.1.1.1 Jocul NIM 19.Capitolul 19 Teoria jocurilor 19.

494 CAPITOLUL 19. TEORIA JOCURILOR .

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

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

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

v) // 3..v) // relax(u.m. muchii . --> k { if(p[k]!=-1) drum(p[k]).2 Alg Belmann-Ford pentru grafuri orientate // drumuri scurte in graf orientat cu costuri negative (dar fara ciclu negativ!) // Dijkstra nu functioneaza daca apar costuri negative ! // Algoritm: 1. pentru fiecare arc (u.out. static int n.io. } }//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. repeta de n-1 ori // pentru fiecare arc (u. } static void afisv(int[] x) { int i. class BellmanFord { static final int oo=0x7fffffff..out. for(i=1.print(k+" ").2. OK=true // 4.498 CAPITOLUL 20.i<=n. System.out.print(x[i]+" "). System.i++) System. return OK import java.*.v) // daca d[v]>d[u]+w[u][v] // OK=false // 5. // varfuri. ALTI ALGORITMI ¸ static void drum(int k) // s --> . init // 2.println().

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

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

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

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

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

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

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

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

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