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

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

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

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

1.1. PROGRAME CIUDATE

3

1.1.3

Un program ciudat ˆ Java ın

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

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

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

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

4

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.1.4

Structura unui program Java

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

1.2. CONVERSII ALE DATELOR NUMERICE

5

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

1.2

Conversii ale datelor numerice

1.2.1

Conversia din baza 10 ˆ baza 2 ın

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

6

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.2.2

Conversia din baza 2 ˆ baza 10 ın

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

1.2.3

Conversii ˆ ıntre bazele 2 ¸i 2r s

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

Capitolul 2

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

2.1

Date ¸i structuri de date s

2.1.1

Date

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

8

CAPITOLUL 2. STRUCTURI DE DATE

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

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

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

2.1. DATE SI STRUCTURI DE DATE ¸

9

2.1.2

Structuri de date

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

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

10

CAPITOLUL 2. STRUCTURI DE DATE

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

2.2

Structuri ¸i tipuri de date abstracte s

2.2.1

Structuri de date abstracte

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

2.2.2

Tipuri de date abstracte

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

2.3. STRUCTURI DE DATE ELEMENTARE

11

2.3

Structuri de date elementare

2.3.1

Liste

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

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

12

CAPITOLUL 2. STRUCTURI DE DATE

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

2.3.2

Stive ¸i cozi s

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

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

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

i > 0.2. ın a a a ın s 2.5 Heap-uri Un max-heap (heap=”gramad˘ ordonat˘”. ˆ 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). 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. 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.4: Max-heap . ˆ pozitiile T [2k ]. dac˘ exist˘. ¸ punˆnd vˆrfurile de adˆncime k. ˆ T [2i] ¸i T [2i + 1]. a a a a ın ¸ . se afl˘ ˆ T [i/2]. cu urm˘toarea proprietate: valoarea fiecarui vˆrf este a a mai mare sau egal˘ cu valoarea fiec˘rui fiu al s˘u.. ¸ 1 nivelul 0 2 3 nivelul 1 4 5 6 7 nivelul 2 8 9 10 11 12 nivelul 3 Figura 2.3.. Fiii unui vˆrf a a ın a ın a reprezentat ˆ T [i] se afl˘.3: Arbore binar complet Tat˘l unui vˆrf reprezentat ˆ T [i]. T [2k +1].3. de la stˆnga la dreapta..

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

B2 . 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.. Bn ¸tiind c˘ a s s a n Bn+1 = k=0 k Cn Bk .50 CAPITOLUL 3. . S˘ se afi¸eze B1 .. . B0 = 1..

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

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

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

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

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

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

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

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

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

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

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

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

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

j. ¸ void quicksort(int st. quicksort(st. } Observatie : Vectorul a este considerat variabil˘ global˘. val. Hoare ˆ 1960 ¸i este unul dintre cei ın s mai utilizati algoritmi de sortare. while(i<j) { while((i<j) && (a[j] >= val)) j=j-1. dr). } } Initial apel˘m quicksort(1.3. return i. quicksort(m+1. a int divide(int st. ANALIZA COMPLEXITATII ALGORITMILOR Pentru a sorta cele n elemente sunt necesare n i=2 i−1 1 = 2 2 n(n + 1) − 1 − (n − 1) 2 = n 2 (n + 1) −1 2 = n(n − 1) 4 operatii elementare. m-1). dr). if st<dr { m=divide(st. int dr) { int i. } a[i]=val. ¸ 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.R. ¸ a a .A. ˆ stˆnga sa se vor g˘si numai elemente mai mici. val=a[st]. int dr) { int m. iar ˆ dreapta ın s In a a ın numai elemente mai mari decˆt el. Deci complexitatea algoritmului ˆ medie este tot O(n2 ). while((i<j) && (a[i] <= val)) i=i+1.n). j=dr. i=st.64 ˘¸ CAPITOLUL 4. a[i]=a[j]. ¸ ın 4. a[j]=a[i].

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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. while(j<nc) { v[n]=v[n]+1. j++. ¸i trecem la ordinul urm˘tor s a 0 ¸i adun˘m 1 s a 1 ¸i adun˘m 1 s a 2 care nu este permis.i<=n.i++) c[j][i-1]=v[i]. while(v[i]>1) { v[i]=v[i]-2. de exemplu. a4 } iar coloana 7 reprezint˘ submultimea {a2 . v[i-1]=v[i-1]+1. i=n. for(i=1. } return c. coloana 5 ¸ s reprezint˘ submultimea {a2 . } for(i=1. 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. coloana F reprezint˘ ˆ ¸ a a ıntreaga multime. ¸i trecem la ordinul urm˘tor s a 0 care nu este permis.4. nc=1. j=0. c=new int[n][nc]. ¸i. a3 . Coloana 0 reprezint˘ a ¸ a a multimea vid˘.i<=n. int[] v=new int[n+1]. } . i--. int[][] c. a4 }. for(i=1.i++) v[i]=0.i<=n. j. a a static int[][] submultimi(int n) { int i.7.i++) nc*=2. ¸i trecem la ordinul urm˘tor s a 0 ¸i a¸a mai departe s s · pˆn˘ cˆnd a a a 1 Aceste rezultate se pot retine ˆ ¸ ıntr-o matrice cu n linii ¸i 2n coloane.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ALGORITMI COMBINATORIALI .126 CAPITOLUL 8.

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

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

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

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

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

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

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

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

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

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

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

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

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

¸i drept ¸ a In ın s urmare exist˘ putine inversiuni ¸i sunt necesare putine operatii. Atunci a ¸ ci = 1 + card{aj |aj > ai . s . ˆ cazul ˆ care tabloul este aproape ordonat. ¸ ˆ plus. unde num˘rul de a s a a inversiuni este inv(π). ALGORITMI ELEMENTARI DE SORTARE Inserarea lui 30 nu necesit˘ deplas˘ri de elemente. aceast˘ metod˘ de sortare este mai s s a a eficient˘ decˆt sortarea prin selectie. num˘rul total de comparatii pentru sortarea prin insertie a ¸ ¸ este N 0 4 ↑ j 0 3 1 7 2 12 3 30 4 3 ↑ i 4 30 5 4 6 23 7 14 0 3 ↑ j 0 3 1 4 2 7 3 12 Cπ = i=2 ci = N − 1 + inv(π). Cea mai bun˘ metod˘ de sortare necesit˘ a a ¸ a a a n log2 n comparatii. a a De¸i ordinul de cre¸tere este tot N 2 .140 CAPITOLUL 10. 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. 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. 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. Deci. 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.

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

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

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

144 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

170 CAPITOLUL 12.print("Introduceti numarul discurilor: ").io.out.readLine()). BufferedReader br = new BufferedReader(new InputStreamReader(System. ’A’. nrDiscuri=Integer. 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˘. solutiaHanoi(nrDiscuri. ’B’. }// main() static void solutiaHanoi(int nrDiscuri.parseInt(br. class Hanoi { static int nrMutare=0. char tijaIntermediara. char tijaSursa. ’C’). public static void main(String[] args) throws IOException { int nrDiscuri.*. char tijaDestinatie) { if(nrDiscuri==1) .in)). ALGORITMI DIVIDE ET IMPERA • la un moment dat se va muta un singur disc (cel care se afl˘ deasupra a celorlalte discuri pe o tij˘). a 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

METODA OPTIMULUI LOCAL .268 CAPITOLUL 14.GREEDY .

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

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. n n+1 Trebuie s˘ construim urm˘toarele elemente ale produsului cartezian. . 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 . ¸ a 0 1 2 3 4 5 0 0 0 0 1 . n n+1 . s-au epuizat toate ¸ a ¸ valorile posibile.. pe aceast˘ pozitie k. s 0 1 2 3 4 5 0 1 . pentru n = 4).. valorile se schimb˘ crescˆnd de s a ¸ a a la min c˘tre max. Se ajunge astfel la configuratia (k = 4 = n aici!) a ¸ 0 1 2 3 4 5 0 0 0 9 0 1 . an ) ¸i vom atribui fiec˘rei componente ai valori cuprinse ˆ s a ıntre min ¸i max. de ap˘... 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˘). pe care putem s˘-l afi¸am.. ... s a ¸ Cum ˆ ıncepem generarea produsului cartezian? O prim˘ variant˘ este s˘ atribuim tuturor componentelor ai valoarea min ¸i a a a s avem astfel un prim element al produsului cartezian. n n+1 Ei bine. de gaze... a s˘ 0 1 2 3 4 5 0 0 0 0 0 1 ... atunci am descoperit singuri aceast˘ metod˘! a a Pe pozitia k = n.. ˆ acest scop folosim variabila gol care a a ın a In poate fi initializat˘ cu orice valoare ˆ ¸ a ıntreag˘ care nu este ˆ intervalul [min. n n+1 Folosim o variabil˘ k pentru a urm˘ri pozitia din vector pe care se schimb˘ a a ¸ a valorile. Folosim un vector cu n elemente a = (a1 ...... etc. 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.... . La ˆ ınceput k = n ¸i. odat˘ cu aparitia valorii 9 = max. ˆ acest moment... Este a a util˘.. ... ... max]... a2 . METODA BACKTRACKING (de exemplu. imaginea kilometrajelor de ma¸ini sau a contoarelor de a ın s energie electic˘.. .. deci k = k − 1 (acum k = 3). . ..270 CAPITOLUL 15. 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 . a ın Este totu¸i util˘ initializarea gol=min-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 ¸ .. . n n+1 Aici k = 4 = n ¸i 9 = max.15....... n n+1 Dup˘ plasarea unei valori pe pozitia k.. deci k = k + 1 0 1 2 3 4 5 0 1 0 1 ... .. n n+1 Aici (dac˘ nu am ie¸it ˆ afara vectorului.. .. a s ın ın a ˆ cazul nostru k = 4 ¸i pozitia este goal˘. deci k = k − 1 a 0 1 2 3 4 5 0 0 9 0 1 . n n+1 Aici k = 2 ¸i 0 < max.... n n+1 Cum trecerea de la o valoare la alta se face prin cre¸terea cu o unitate a valorii s vechi iar acum facem trecerea gol → min. a ¸ 0 1 2 3 4 5 0 0 1 0 1 . Pe pozitia k nu se mai poate pune o nou˘ valoare s ¸ a ¸i atunci: s • se pune gol pe pozitia k ¸ • se face pasul spre stˆnga. 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). ... ... S˘ consider˘m c˘ s-a ajuns la configuratia a a a ¸ 0 1 2 3 4 5 0 0 9 9 0 1 . 0 1 2 3 4 5 0 0 1 0 0 1 . a a ın 0 1 2 3 4 5 0 0 1 0 1 .. . deci ˆ acest caz cu 1. n n+1 Aici k = 3 ¸i 9 = max.1.. n n+1 Aici k = 3 ¸i gol=-1< max. ˆ urma pasului f˘cut spre dreapta!)... se plaseaz˘ prima valoare valid˘ (deci ın s ¸ a a a valoarea min). se face pasul spre dreapta (k = k + 1).... deci k = k − 1 a 0 1 2 3 4 5 0 0 0 1 . Pe pozitia k nu se mai poate pune o nou˘ valoare ¸i s ¸ a s atunci: • se pune gol pe pozitia k ¸ • se face pasul spre stˆnga. . ˆ ¸elegem de ce este util s˘ initializ˘m ınt a ¸ a variabila gol cu valoarea min − 1. 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..

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

while(culoare[k] < nrCulori)// selectez o culoare { ++culoare[k].i++) if((culoare[k]==culoare[i])&&(harta[i][k]==1)) return false. else culoare[++k]=0.println(). if (okk) break. } // scrieRez } // class Pentru 3 culori rezultatele care apar pe ecran sunt: 1 2 3 4 5 6 1 1 2 2 3 3 2 3 1 3 1 2 3 2 3 1 2 1 2 3 1 3 1 2 3 2 3 1 2 1 299 . } if (!okk) k--.print(++nrVar+" : ").print(culoare[i]+" ").3.i<=k-1.out. } // posibil static void afis() { System. else if (k == (n-1)) afis(). System.i<n.out. return true.out.i++) System.15. // tara k nu este colorata (inca) while (k>-1) // -1 = iesit in stanga !!! { okk=false. } } // harta static boolean posibil(int k) { for(int i=0. okk=posibil(k). for(int i=0. // prima pozitie culoare[k]=0. PROBLEME REZOLVATE k=0.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

342 CAPITOLUL 15. METODA BACKTRACKING .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

372 }// for h

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

373

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

16.2.10

Triangularizarea poligoanelor convexe

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

1

8 8

8 7

7

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

374 ¸i s

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

16.2.11

Algoritmul Roy-Floyd-Warshall

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

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

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

// drumuri intre i si j care trec

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

375

3 1 2 1 3 2

2 3 1 * * *

3 4 2 * * *

1 2 2 * * *

* * * 2 1 2

* * * 1 2 3

* * * 2 3 4

16.2.12

Oracolul decide - ONI2001 cls 10

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

376

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

377

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

378

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

379

380

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

381

16.2.13

Pav˘ri - ONI2001 clasa a X-a a

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

428 ˘ CAPITOLUL 16. PROGRAMARE DINAMICA .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

444 CAPITOLUL 17. POTRIVIREA SIRURILOR ¸ .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GEOMETRIE COMPUTATIONALA ¸ .492 ˘ CAPITOLUL 18.

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

494 CAPITOLUL 19. TEORIA JOCURILOR .

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

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

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

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

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

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

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

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

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

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

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

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

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