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

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

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

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

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

1.1. PROGRAME CIUDATE

3

1.1.3

Un program ciudat ˆ Java ın

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

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

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

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

4

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.1.4

Structura unui program Java

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

1.2. CONVERSII ALE DATELOR NUMERICE

5

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

1.2

Conversii ale datelor numerice

1.2.1

Conversia din baza 10 ˆ baza 2 ın

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

6

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.2.2

Conversia din baza 2 ˆ baza 10 ın

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

1.2.3

Conversii ˆ ıntre bazele 2 ¸i 2r s

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

Capitolul 2

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

2.1

Date ¸i structuri de date s

2.1.1

Date

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

8

CAPITOLUL 2. STRUCTURI DE DATE

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

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

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

2.1. DATE SI STRUCTURI DE DATE ¸

9

2.1.2

Structuri de date

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

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

10

CAPITOLUL 2. STRUCTURI DE DATE

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

2.2

Structuri ¸i tipuri de date abstracte s

2.2.1

Structuri de date abstracte

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

2.2.2

Tipuri de date abstracte

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

2.3. STRUCTURI DE DATE ELEMENTARE

11

2.3

Structuri de date elementare

2.3.1

Liste

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

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

12

CAPITOLUL 2. STRUCTURI DE DATE

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

2.3.2

Stive ¸i cozi s

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

c=new int[n][nc]. 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. Coloana 0 reprezint˘ a ¸ a a multimea vid˘. j. coloana F reprezint˘ ˆ ¸ a a ıntreaga multime. i--.4. ¸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. coloana 5 ¸ s reprezint˘ submultimea {a2 . a4 } iar coloana 7 reprezint˘ submultimea {a2 . for(i=1.i<=n. for(i=1.i<=n.i++) nc*=2.i++) c[j][i-1]=v[i].i++) v[i]=0. s a1 a2 a3 a4 0 0 0 0 0 1 0 0 0 1 2 0 0 1 0 3 0 0 1 1 4 0 1 0 0 5 0 1 0 1 6 0 1 1 0 7 0 1 1 1 8 1 0 0 0 9 1 0 0 1 A 1 0 1 0 B 1 0 1 1 C 1 1 0 0 D 1 1 0 1 E 1 1 1 0 F 1 1 1 1 0 1 2 3 Ultima coloan˘ contine num˘rul liniei din matrice. int[][] c.7. a a static int[][] submultimi(int n) { int i. } return c. } . i=n. ¸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. a3 . } for(i=1. de exemplu. while(v[i]>1) { v[i]=v[i]-2. while(j<nc) { v[n]=v[n]+1. j=0. int[] v=new int[n+1]. j++. ¸i. a4 }. ¸i trecem la ordinul urm˘tor s a 0 care nu este permis. nc=1. v[i-1]=v[i-1]+1.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

126 CAPITOLUL 8. ALGORITMI COMBINATORIALI .

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

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

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

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

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

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

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

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

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

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

a[j1] = a[j]. ++j) if (a[j1] > a[j]) { t = a[j1]. eventual interschimb˘ri (dac˘. } } 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. j <= i.2. 6 7 14 30 ↑ i 6 7 14 30 ↑ i 6 23 0 7 1 4 3 30 0 7 1 4 2 12 3 3 4 3 ↑ j 4 30 5 4 6 23 0 7 1 4 0 7 1 4 2 12 3 3 4 4 5 4 ↑ j 5 30 6 23 0 7 1 4 0 7 1 4 2 12 3 3 4 4 5 23 6 23 ↑ j 6 30 0 7 1 4 0 7 1 4 2 12 3 3 4 4 5 23 0 7 0 4 Dup˘ prima parcurgere 30 a 1 2 3 4 5 6 4 12 3 4 23 14 ↑ ↑ j i 1 2 3 4 5 6 7 7 12 3 4 23 14 30 ↑ ↑ j i locul s˘u (ultimul loc ˆ a ın 0 1 2 3 4 5 4 7 12 3 4 23 ↑ j 0 1 2 3 4 5 4 7 3 12 4 23 ⇒ ↑ j . a[j] = t. i >= 0. 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.10. i) for (int j = 1. ¸ ın a static void sortBule() { int t. a ¸ s a a tabloul este initial ˆ ordine descresc˘toare). de exemplu.

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

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

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

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

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

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

ALGORITMI ELEMENTARI DE SORTARE .144 CAPITOLUL 10.

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

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

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

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

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

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

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

int continut[]. int inceput. } static void facVida (Coada c) { c. plina= false.vida. boolean plina.152 CAPITOLUL 11. } . int sfarsit.sfarsit = 0. c. Se spune c˘ este un tablou (sau tampon) a circular. c. LISTE Fiecare client care se prezint˘ obtine un num˘r ¸i clientii sunt apoi chemati de c˘tre a ¸ a s ¸ ¸ a functionarul de la ghi¸eu ˆ ordinea cresc˘toare a numerelor de ordine primite la ¸ s ın a sosire. sfarsit = 0. } static boolean esteVida(Coada c) { return c. Coada () { inceput = 0.inceput = 0. vida = true. dac˘ coada a ¸ a nu este vid˘. s a as a • atunci cˆnd sose¸te un nou client. 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. c.vida = true. incrementeaz˘ inceput ¸i cheam˘ posesorul acestui num˘r. 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. vida. Pentru gestionarea acestui sistem. a a class Coada { final static int MaxC = 100. info = new int[MaxC].plina = false. } static Coada vida(){ return new Coada(). sau coad˘ circular˘. a • atunci cˆnd functionarul este liber el poate servi un alt client. 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. se incrementeaz˘ sf arsit ¸i se d˘ acest a s a s a num˘r clientului respectiv.

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

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

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

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

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

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

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

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

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

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

Capitolul 12 Algoritmi divide et impera 12.. ˆ englez˘ ın a 163 ... • Rezolvarea ˆ acela¸i mod (recursiv) a tuturor subproblemelor.. s a a 1 divide-and-conquer.. . divideEtImpera(Pa . . S1 ) . 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˘... n1 . Metoda poate fi descris˘ astfel: a procedure divideEtImpera(P.. ¸ Descompunerea problemei (subproblemelor) se face pˆn˘ n momentul ˆ care a aˆ ın se obtin subprobleme de dimensiuni atˆt de mici ˆ at au solutie cunoscut˘ sau ¸ a ıncˆ ¸ a pot fi rezolvate prin tehnici elementare.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) if (n ≤ n0 ) then rezolv˘ subproblema P prin tehnici elementare a else ˆ ımparte P ˆ P1 . n. Sk ) combin˘ S1 .. .. ın s • Compunerea subsolutiilor pentru a obtine solutia problemei (subproblemei) ¸ ¸ ¸ initiale. nk . Pk de dimensiuni n1 .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

s Dac˘ exist˘ mai multe solutii.ˆ ı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. METODA OPTIMULUI LOCAL .242 CAPITOLUL 14. 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˘. iM jM pM .ˆ ı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... a Un grup de puncte poate contine ˆ ¸ ıntre 1 ¸i N puncte inclusiv.GREEDY priorit˘¸i ˆ functie de important˘.. Date de intrare Fi¸ierul de intrare URGENTA.gravitatea maxim˘ a C . ˆ 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˘. ˆ care punctele de interes strategic s˘ fie ˆ artite ˆ a ın a ımp˘ ¸ ıntr-un num˘r de K a grupuri.. programul va determina una singur˘.ˆ ıntre punctele k2 ¸i h2 a fost ˆ s ıntrerupt˘ calea de acces a . kC hC .num˘rul de c˘i de acces ˆ a a ıntrerupte de calamitate k1 h1 .ˆ ıntre punctele i2 ¸i j2 exist˘ o cale de acces de prioritate p2 s a . Ca urmare pot rezulta mai a a a multe grupuri de puncte ˆ a¸a fel ˆ at ˆ ın s ıncˆ ıntre oricare dou˘ puncte din acela¸i grup a s s˘ existe m˘car un drum ¸i ˆ a a s ıntre oricare dou˘ puncte din grupuri diferite s˘ nu a a existe drum. a a ¸ a Exemplu .ˆ ıntre punctele i1 ¸i j1 exist˘ o cale de acces de prioritate p1 s a i2 j2 p2 . a a ˆ cazul unei calamit˘¸i unele c˘i de acces pot fi temporar ˆ In at a ıntrerupte ¸i astfel s ˆ ıntre anumite puncte de interes nu mai exist˘ leg˘tur˘.ˆ ıntre punctele k1 ¸i h1 a fost ˆ s ıntrerupt˘ calea de acces a k2 h2 .OUT va avea urm˘torul format: s s a gravmax .IN are urm˘torul format: s a N M K i1 j1 p1 .

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

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

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

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

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

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

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

16 Pal . Algorel poate evada dac˘ g˘se¸te combinatia magic˘ cu care poate deschide a a s ¸ a poarta turnului. Date de ie¸ire s Prima linie a fi¸ierului de ie¸ire pal. iar aceast˘ sum˘ este combinatia magic˘ ce va ıncˆ a a a a ¸ a deschide u¸a. Acum interveniti dumneavoastr˘ ˆ poveste.GREEDY 14. ¸ Restrictii ¸i preciz˘ri ¸ s a • ˆ total vor fi cel mult 100 de cifre In • 1 ≤ L < 100 ¸i L va fi mai mic decˆt num˘rul total de cifre s a a • Pentru datele de test va exista ˆ ıntotdeauna solutie: se vor putea forma din ¸ cifrele scrise pe u¸a turnului dou˘ numere care ˆ s a ıncep cu o cifr˘ diferit˘ de 0. iar cel de-al doilea a a a ¸ poate avea orice lungime diferit˘ de 0.250 } } CAPITOLUL 14. s Primul num˘r palindrom trebuie s˘ aib˘ cel putin L cifre.out contine primul num˘r palidrom. Dac˘ exist˘ mai ¸ a a a multe solutii se va scrie doar una dintre ele. Numerele palindroame formate nu pot a ˆ ıncepe cu cifra 0. METODA OPTIMULUI LOCAL . ¸ ¸ a Date de intrare Prima linie a fi¸ierului pal. ın Prin noul super-telefon al s˘u. 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. pentru i cu valori de la a a ¸ 0 la 9. 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.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. 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.3. 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.in contine un num˘r ˆ s ¸ a ıntreg L reprezentˆnd luna gimea minim˘ a primului num˘r. fiind prietenul s˘u ¸ a ın a cel mai priceput ˆ algoritmi. aflati dou˘ numere palindroame cu care se poate a ¸ a obtine combinatia magic˘. iar s s a a dumneavoastr˘ trebuie s˘-i trimiteti cˆt mai repede numerele cu care poate obtine a a ¸ a ¸ combinatia magic˘. ¸ a Cerint˘ ¸a Avˆnd datele necesare.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GREEDY .268 CAPITOLUL 14. METODA OPTIMULUI LOCAL .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

342 CAPITOLUL 15. METODA BACKTRACKING .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2. ””) = d(””. (16.5 Distanta minim˘ de editare ¸ a Fie d(s1. PROGRAMARE DINAMICA * - O solutie oarecare 1346acdf Toate solutiile 1 1346acdf 2 1246acdf 3 1345acdf 4 1245acdf 5 1346abdf 6 1246abdf 7 1345abdf 8 1245abdf 9 1346acde 10 1246acde 11 1345acde 12 1245acde 13 1346abde 14 1246abde 15 1345abde 16 1245abde SOLmin = 1245abde SOLmax = 1346acdf 16. s) = |s|. a ın ¸ a s la sfˆr¸itul acestuia (dar dup˘ modific˘rile efectuate deja). s2) distanta de editare (definit˘ ca fiind num˘rul minim de operatii ¸ a a ¸ de ¸tergere.1) (16.360 5 6 a b c d e f | | | | | | | | | | | | | | | | | | | | | | | | * | | | | | | * * | | | | | * | | | * * | | * ˘ CAPITOLUL 16.2. obtinem: as a a ¸ . ””) = 0 d(s.2) Avˆnd ˆ vedere ultima operatie efectuat˘ asupra primului ¸ir de caractere. Atunci: s s s s d(””. inserare ¸i modificare) dintre ¸irurile de caractere s1 ¸i s2.2.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

PROGRAMARE DINAMICA .428 ˘ CAPITOLUL 16.

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

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

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

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

n]=text.k ′ ]. ≥ 0. . . .... .. ..... .. ..*.. acest algoritm se termin˘ ˆ a ıntr-un num˘r finit de pa¸i pentru c˘ a s a j > k > k′ > . next[j]. p: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx j+1-k' ... Astfel.k]. ...2 pentru a urm˘ri mai u¸or rationamentul! ˆ aceast˘ a a s ¸ In a figur˘ next[j] = k ¸i next[k] = k ′ . a Obtinem: ¸ dac˘ p[j + 1] = p[next(next(j)) + 1] atunci next[j + 1] = next(next(j)) + 1.. class KMP { static int na=0. .. a ¸ a Obtinem: ¸ dac˘ p[j + 1] = p[next(j) + 1] atunci next[j + 1] = next(j) + 1. next[2]. UN ALGORITM EFICIENT .2: Functia next ¸ Ne ajut˘m de Figura 17. 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. j 1 k' Figura 17. Dac˘ ajungem la 0. .. import java. ..p..2...j]! Fie acesta p[1.. .io.m]=pattern static int[] next.. // t[1. p[1.. .. a Ordinul de complexitate al algoritmului KMP este O(n + m). j+1-k . a Dac˘ nici acum nu avem egalitate de caractere.17... a Cum determin˘m next[j + 1]? a xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 433 1 k+1-k' k p: p: 1 .KMP Initializ˘m next[1] = 0.. atunci vom avea next(j + 1) = 0. a s Dac˘ p[j+1] = p[k+1] (folosind notatiile din figur˘) atunci next[j+1] = k+1. a Presupunem c˘ au fost determinate valorile next[1].. Dar acest sufix a a mai mic este cel mai lung sufix pentru p[1.. .... k+1 j+1 k'+1 . dac˘ p[j + 1] = p[k ′ + 1] atunci next(j + 1) = k ′ + 1. // nr aparitii static char[] t. Evident. cu alte cuvinte k ′ = next(k) = next(next(j)).. 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++.. // ultima pozitie a sufixului while(i<=n) // t[1. }// calcNext() static void kmp(char[] t.i++) p[i]=sc[i-1]. sc=s.readLine().in")). for(i=1. next=calcNext(p).n. p=new char[m+1]..m] { int n=t. next[j]=k. for(i=1.. int j=0. sc=s. p[1.i<=n.n] { . // nr caractere potrivite deja int i=1. // initializare int k=0. BufferedReader br=new BufferedReader(new FileReader("kmp.length-1. char[] sc.n]. s=br. while(j<=m) { while(k>0&&p[k+1]!=p[j]) k=next[k].length.length. char[] p) // t[1. // nr caractere potrivite int j=2. n=sc. int i. m=sc.i++) t[i]=sc[i-1].toCharArray().. if(p[k+1]==p[j]) k++.m. t=new char[n+1].434 CAPITOLUL 17. // next[1. POTRIVIREA SIRURILOR ¸ static void readData() throws IOException { String s.m] next[1]=0. s=br.i<=m.. int[] next=new int[m+1]. }//readData() static int[] calcNext(char[] p) { int m=p.length-1.length-1. } return next.toCharArray(). m=p.m] pentru p[1..readLine().

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

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

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

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

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

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

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

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

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

444 CAPITOLUL 17. POTRIVIREA SIRURILOR ¸ .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

464

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸

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

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

18.9. PROBLEME REZOLVATE Indicatii de rezolvare * ¸

465

Solutia oficial˘, lect. Ovidiu Dom¸a ¸ a s Num˘rul mic al punctelor permite generarea tuturor posibilit˘¸ilor de a conecta a at o gr˘din˘ cu o fˆntˆn˘ neconectat˘ la un moment dat. a a a a a a Pentru fiecare astfel de combinatie g˘sit˘ se calculeaz˘ suma distantelor ¸ a a a ¸ (Gi, F j), ˆ linie dreapta, folosind formula distantei dintre dou˘ puncte ˆ plan, ın ¸ a ın studiat˘ la geometrie. (d(A(x, y), B(z, t) = (x − z)2 + (y − t)2 ). a Acest˘ solutie implementat˘ corect asigur˘ 60 − 70 de puncte. a ¸ a a Pentru a obtine punctajul maxim se tine cont de urm˘toarele aspecte: ¸ a 1. Se construie¸te ˆ prealabil matricea distantelor d(i, j) cu semnificatia s ın ¸ ¸ distantei dintre gr˘dina i ¸i fˆntˆna j. Aceasta va reduce timpul de calcul la ¸ a s a a variantele cu peste 9 perechi. 2. Pentru a elimina cazuri care nu pot constitui solutii optime se folose¸te ¸ s proprietatea patrulaterului c˘ suma a doua laturi opuse (conditie care asigur˘ a ¸ a unicitatea conect˘rii unei singure fˆntˆni la o singur˘ gr˘din˘) este mai mic˘ decˆt a a a a a a a a suma diagonalelor. De aceea nu se vor lua ˆ considerare acele segmente care se ın intersecteaz˘. Conditia de intersectie a dou˘ segmente care au capetele ˆ punctele a ¸ ¸ a ın de coordonate A(a1, a2), B(b1, b2), C(c1, c2), D(d1, d2) este ca luˆnd segmentul a AB, punctele C ¸i D s˘ se afle de aceea¸i parte a segmentului AB ¸i respectiv s a s s pentru segmentul CD, punctele A ¸i B s˘ se afle de aceea¸i parte (se ˆ s a s ınlocuie¸te s ˆ ecuatia dreptei ce trece prin dou˘ puncte, studiat˘ ˆ clasa a 9-a). ın ¸ a a ın Observatie: Pentru cei interesati, problema are solutie ¸i la un nivel superior, ¸ ¸ ¸ s folosind algoritmul de determinare a unui flux maxim de cost minim. Variant˘ cu determinarea intesectiei segmentelor. a ¸ import java.io.*; // cu determinarea intesectiei segmentelor class Seceta1 // Java este "mai incet" decat Pascal si C/C++ { // test 9 ==> 2.23 sec static int nv=0; static int n; static int[] xg, yg, xf, yf, t, c; static int[] a; // permutare: a[i]=fantana asociata gradinii i static double costMin=200*1.42*12*100; static double[][] d; static PrintWriter out; static StreamTokenizer st; public static void main(String[] args) throws IOException { long t1,t2; t1=System.currentTimeMillis(); citire();

466

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

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

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

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

18.9. PROBLEME REZOLVATE

467

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

468

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ if((x1==x2)&&(x3==x4)) // ambele segmente verticale if(x1!=x3) return false; else if(intre(y1,y3,y4)||intre(y2,y3,y4)) return true; else return false; if((y1==y2)&&(y3==y4)) // ambele segmente orizontale if(y1!=y3) return false; else if(intre(x1,x3,x4)||intre(x2,x3,x4)) return true; else return false; if((y2-y1)*(x4-x3)==(y4-y3)*(x2-x1)) // au aceeasi panta (oblica) if((x2-x1)*(y3-y1)==(y2-y1)*(x3-x1)) // au aceeasi dreapta suport if(intre(x1,x3,x4)||intre(x2,x3,x4)) return true; else return false; else return false;// nu au aceeasi dreapta suport else // nu au aceeasi panta (macar unul este oblic) { x=(double)((x4-x3)*(x2-x1)*(y3-y1)x3*(y4-y3)*(x2-x1)+ x1*(y2-y1)*(x4-x3))/ ((y2-y1)*(x4-x3)-(y4-y3)*(x2-x1)); if(x2!=x1) y=y1+(y2-y1)*(x-x1)/(x2-x1); else y=y3+(y4-y3)*(x-x3)/(x4-x3); if(intre(x,x1,x2)&&intre(y,y1,y2)&&intre(x,x3,x4)&&intre(y,y3,y4)) return true; else return false; }

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

18.9. PROBLEME REZOLVATE

469

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

470 {

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸

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

18.9. PROBLEME REZOLVATE System.out.println(" ok=false; break; } if(!ok) continue; a[k]=i; if(k<n) f(k+1); else verificCostul(); } } static void verificCostul() { int i; double s=0; for(i=1;i<=n;i++) s=s+d[i][a[i]]; if(s<costMin) costMin=s; afisv(n); System.out.println(" "+s+" "+costMin+" "+(++nv)); } static void afisv(int nn) { int i; for(i=1;i<=nn;i++) System.out.print(a[i]); } // de ce parte a dreptei [(xa,ya);(xb,yb)] se afla (xp,yp) static int s(int xp,int yp,int xa,int ya,int xb,int yb) { double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya; if(s<-0.001) return -1; // in zona "negativa" else if(s>0.001) return 1; // in zona "pozitiva" else return 0; // pe dreapta suport } ("+xg[j]+","+yg[j]+") "+"

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

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

472

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸

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

18.9. PROBLEME REZOLVATE { st.nextToken(); xf[k]=(int)st.nval; st.nextToken(); yf[k]=(int)st.nval; } } static void rezolvare() throws IOException { int i,j; int s; for(i=1;i<=n;i++) // gradina i for(j=1;j<=n;j++) // fantana j { s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]); d[i][j]=Math.sqrt(s); } f(1); // generez permutari }

473

static void f(int k) { boolean ok; int i,j; for(i=1;i<=n;i++) { ok=true; // k=1 ==> nu am in stanga ... for nu se executa ! for(j=1;j<k;j++) if(i==a[j]) {ok=false; break;} if(!ok) continue; for(j=1;j<k;j++) if((s(xg[k], yg[k], xg[j],yg[j],xf[a[j]],yf[a[j]])* s(xf[i], yf[i], xg[j],yg[j],xf[a[j]],yf[a[j]])<0)&& (s(xg[j], yg[j], xg[k],yg[k],xf[i], yf[i])* s(xf[a[j]],yf[a[j]],xg[k],yg[k],xf[i], yf[i])<0)) { ok=false; break; } if(!ok) continue; a[k]=i; if(k<n) f(k+1); else verificCostul(); } } static void verificCostul()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

492 ˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ .

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

494 CAPITOLUL 19. TEORIA JOCURILOR .

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

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

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

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

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

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

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

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

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

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

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

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

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