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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

s=-b/a.out. } } static int maxFibo(long nr) { int k. x=x-y.38 CAPITOLUL 3. } } De exemplu.out. while(x>0) { iy=maxFibo(x).c=2. a=1. .k++) if (f[k]>nr) break. c. s. long[] ss=new long[n+1].b=1.k<=n. return k-1. p. k. nrt++. Fie Sn = xn +xn unde x1 ¸i x2 sunt r˘d˘cinile ecuatiei cu coeficienti ˆ 2 1 ax + bx + c = 0 (vom considera a = 1!). System. for(k=1.println("x = "+x). a a s ın Rezolvare: 2 class e02 { public static void main(String[] args) { int a. b. y=f[iy]. ALGORITMI System. iar dac˘ nu s a ın a a a a este num˘r prim s˘ se afi¸eze descompunerea ˆ factori.println(nrt+" : "+x+" f["+iy+"] = "+y). 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. nnp=0. 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%d==0) return false. else System.out.print(d+" ").k++) ss[k]=s*ss[k-1]-p*ss[k-2]. while((nr%d!=0)&&(d*d<=nr)) d=d+2.out. ss[1]=s.k++) if(esteprim(Math. if(nr%2==0) return false. } System.out.println().abs(ss[k])). else { System. else return true. if((nr==0)||(nr==1)){System. ss[2]=s*s-2*p. descfact(Math.} d=3.println("nnp = "+nnp).} while(nr%d==0){System.out. } if(nr!=1) System.k<=n.out. return.} d=d+2.5. INSTRUCTIUNI CORESPONDENTE LIMBAJULUI ALGORITMIC ¸ p=c/a.println(nr). while((d*d<=nr)&&(nr!=1)) { while(nr%d==0){System.k<=n. if((nr==2)||(nr==3)) return true. } static boolean esteprim(long nr) { if((nr==0)||(nr==1)) return false.print(k+" : "+ss[k]+" = "). long d=3. for(k=1.print(d+""). for(k=3.out.abs(ss[k]))) System. }// main static void descfact(long nr) { long d=2. nr=nr/d. nr=nr/d.3.out.println().println(k+" : "+ss[k]+" PRIM "+(++nnp)).out. } }// class Pe ecran apar urm˘toarele rezultate: a 1 : -1 = 2 : -3 PRIM 1 39 .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Datorit˘ principiului invariantei vom exprima a a ¸ eficienta unui algoritm ˆ limitele unei constante multiplicative. Datorit˘ principiului invariantei nu ¸ a ¸ ne intereseaz˘ timpul de executie a unei operatii elementare. care la rˆndul lor sunt ¸ a compuse din mai multe operatii elementare. Acesta ne arat˘ c˘ nu este necesar s˘ folosim ¸ a a a o astfel de unitate. de limbajul de programare si ˆ ındemˆnarea programatorului (cu conditia ca a ¸ acesta s˘ nu modifice algoritmul). ¸ ın Un algoritm este compus din mai multe instructiuni. Dorim In a a s a s˘ exprim˘m eficienta algoritmilor sub forma unui criteriu care s˘ ne permit˘ s˘ a a ¸ a a a alegem din mai multi algoritmi pe cel optim. a s ¸ Definitia 1 O operatie elementar˘ este o operatie al c˘rui timp de executie poate ¸ ¸ a ¸ a ¸ fi m˘rginit superior de o constant˘ care depinde numai de particularitatea implea a ment˘rii (calculator.Capitolul 4 Analiza complexit˘¸ii at algoritmilor 4. ˆ ambele cazuri ˆ a. ¸a Se pune problema de alegere a unei unit˘¸i de m˘sur˘ pentru a exprima at a a eficienta teoretic˘ a unui algoritm. avem o In ıns˘ dependent˘ de dimensiunea cazului studiat. ci numai num˘rul lor.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. Exist˘ mai multe moduri ˆ care ¸ a ın putem exprima eficienta: prin timpul necesar pentru executia algoritmului sau ¸ ¸ prin alte resurse necesare (de exemplu memoria). a ı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˘. a 55 . limbaj de programare etc). a ¸ ¸ a dar ne intereseaz˘ care ¸i ce sunt operatiile elementare.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

C.Capitolul 5 Recursivitate Definitiile prin recurent˘ sunt destul de curente ˆ matematic˘: progresia ¸ ¸a ın a aritmetic˘. a a s s 5. toate ¸irurile definite a ¸ s prin recurent˘ se scriu ˆ aceast˘ manier˘ ˆ Java.1 Functii numerice ¸ Pentru calculul termenilor ¸irului lui Fibonacci. a a s 71 .1. etc. ca de altfel ˆ multe alte limbaje de programare (Fortran. 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. De asemenea. ¸ a ın ¸ dac˘ argumentul n este mai mic decˆt 1 returneaz˘ valoarea 1 iar ˆ caz contrar a a a ın returneaz˘ f ib(n − 1) + f ib(n − 2).1 Functii recursive ¸ 5. a ˆ Java este posibil. s˘ definim astfel de functii recursive. ¸irul lui Fibonacci. cum se poate observa din ¸a ın a a ın urm˘toarele dou˘ exemple numerice: factorialul ¸i triunghiul lui Pascal. limite de ¸iruri. etc). progresia geometric˘. In ın Pascal. Dealtfel. fib(n-1) + fib(n-2).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

c2 = ¸ ¸ a Solutia general˘ este: ¸ a xn = 3 2 x0 = 0. 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 . s ¸ Solutia general˘ este de forma: ¸ a xn = (c1 + c2 n + c3 n2 )2n . x1 = 2. solutia relatiei de recurent˘ care define¸te numerele lui Fibonacci este: ¸ ¸ ¸a s √ n √ n 1 1+ 5 1− 5 1 Fn = √ −√ 2 2 5 5 2. Din conditiile initiale rezult˘ constantele: c1 = 0. Ecuatia caracteristic˘ corespunz˘toare este: ¸ a a r3 − 6r2 + 12r − 8 = 0 ¸i are solutiile: r1 = r2 = r3 = 2. ¸i c3 = − 1 . 2 2 4. 5 1 2 x0 = 0. S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+2 = 2xn+1 − 2xn . . S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+3 = xn+2 + 8xn+1 − 12xn . ANALIZA ALGORITMILOR RECURSIVI 1 1 obtinem c1 = √5 ¸i c1 = − √5 . x1 = 2. Din conditiile initiale rezult˘ constantele: c1 = 1 . x2 = 3. x1 = 1.88 CAPITOLUL 6. ¸i c3 = − 1 . s 2 3 1 n − n2 2n = (3n − n2 )2n−1 . x2 = 4. x0 = 0. ¸ s Deci. S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+3 = 6xn+2 − 12xn+1 + 8xn . s 5 3. 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 − 4r2 + 6r − 4 = 0 ¸i are solutiile: r1 = 2. + 3 sin 2 4 4 6. 4 Solutia general˘ este de forma: ¸ a xn = √ 2 n c1 cos nπ nπ . s ¸ s Solutia general˘ este de forma: ¸ a xn = c1 2n + c2 √ 2 n x0 = 0. ¸ ¸ a Solutia general˘ este: ¸ a xn = √ 2 n sin nπ . T (1) = 1. PROBLEME REZOLVATE Ecuatia caracteristic˘ corespunz˘toare este: ¸ a a r2 − 2r + 2 = 0 89 ¸i are solutiile: r1 = 1 + i ¸i r2 = 1 − i care se pot scrie sub form˘ trigonometric˘ s ¸ s a a astfel: √ √ π π π π r1 = 2 cos + i sin . n ≥ 2. + c2 sin 4 4 Din conditiile initiale rezult˘ constantele: c1 = 0 si c2 = 1. S˘ se rezolve relatia de recurent˘: a ¸ ¸a T (n) − 3T (n − 1) + 4T (n − 2) = 0. x1 = 1. cos √ nπ 2 + c3 4 n sin nπ . 4 5. 4 4 4 4 Solutiile fundamentale sunt: ¸ x(1) = n √ 2 n cos √ nπ (2) 2 . r2 = 1 + i ¸i r3 = 1 − i. 4 3 1 Din conditiile initiale rezult˘ constantele: c1 = − 2 . x2 = 1.3. . ¸ ¸ a 2 Solutia general˘ este: ¸ a √ n 2 nπ nπ n−1 xn = −2 + cos . S˘ se rezolve relatia de recurent˘: a ¸ ¸a xn+3 = 4xn+2 − 6xn+1 + 4xn . r2 = 2 cos − i sin . c2 = 1 si c3 = 2 . xn = 4 n sin nπ .6. T (0) = 0.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ALGORITMI COMBINATORIALI .126 CAPITOLUL 8.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

n=a.a[k]). i. for(int k=1. ALGORITMI ELEMENTARI DE SORTARE static void sorteaza() { int N=a. boolean schimb = true. afiseaza().length.k-1. } } public static void main(String[] args) { afiseaza(). 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. a a static void interschimbare(int a[]) { int x.i). } } 10.i.k<=N-1.length. i<n-1. i++) if (a[i]>a[i+1]) { x = a[i]. sorteaza(). } } } . deplasare(k. schimb=true. for(i=0. ˆ cadrul acestei metode se compar˘ ¸i se interschimb˘ perechi In as a adiacente de chei pan˘ cˆnd toate elementele sunt sortate.k++) { i=pozitiaCautBin(0.142 CAPITOLUL 10. a[i] = a[i+1]. a[i+1] = x.

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

144 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE .

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. obiecte informatice complexe. Elementele acestui ansamblu pot fi numere ˆ ıntregi sau reale. Mai precis. ¸iruri de caractere.Capitolul 11 Liste Scopul listelor este de a genera un ansamblu finit de elemente al c˘rui num˘r a a nu este fixat apriori. a class Lista { int continut. numit ¸i pointer. prin ad˘ug˘ri sau ¸tergeri de elemente pe parcursul ¸ a a s prelucr˘rii.1 Liste liniare Fiecare element al listei este continut ˆ ¸ ıntr-o celul˘ care contine ˆ plus adresa a ¸ ın elementului urm˘tor. etc. Listele sunt obiecte dinamice. ˆ sensul c˘ num˘rul elementelor variaz˘ ˆ ın a a a ın cursul executiei programului. ¸ a independent de natura elementelor sale. Lista urmator. 145 . 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. Referinta c˘tre prima celul˘ a ¸ ¸a a a ¸ a a este continut˘ ˆ ¸ a ıntr-o variabil˘.

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

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

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

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

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

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

} static Coada vida(){ return new Coada(). incrementeaz˘ inceput ¸i cheam˘ posesorul acestui num˘r. c. a • atunci cˆnd functionarul este liber el poate servi un alt client. c. dac˘ coada a ¸ a nu este vid˘. sfarsit = 0. boolean plina. plina= false. c. a a s a a ˆ cele ce urmeaz˘ sunt prezentate toate operatiile ˆ Java utilizˆnd tehnicile In a ¸ ın a urm˘toare: se reatribuie num˘rul 0 unui nou client atunci cˆnd se dep˘¸e¸te un a a a as s anumit prag pentru valoarea sf arsit. int continut[]. Coada () { inceput = 0. int sfarsit. 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. Not˘m aceste dou˘ numere inceput ¸i sf arsit ¸i gestion˘m a a s s a sistemul ˆ modul urm˘tor: ın a • coada de a¸teptare este vid˘ dac˘ ¸i numai dac˘ inceput = sf arsit. a a class Coada { final static int MaxC = 100. 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. info = new int[MaxC]. se incrementeaz˘ sf arsit ¸i se d˘ acest a s a s a num˘r clientului respectiv.152 CAPITOLUL 11. sau coad˘ circular˘. } static void facVida (Coada c) { c. s a as a • atunci cˆnd sose¸te un nou client. } . vida. vida = true.plina = false. int inceput. } static boolean esteVida(Coada c) { return c.inceput = 0. Pentru gestionarea acestui sistem. Se spune c˘ este un tablou (sau tampon) a circular.vida = true.vida.sfarsit = 0.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ˆ cele dou˘ configuratii. se repet˘ acest ¸a ¸ ¸a ¸ a a pas pˆn˘ cˆnd s-au verificat toate statiile. Pentru aceste statii ¸ ¸ ¸ a ¸ ¸ repet˘m strategia sugerat˘ de propozitia anterioar˘. a ¸ a ¸a a Rezolvarea problemei este urm˘toarea: a • se alege statia 1. . Evident |ai2 − ai1 | ≥ d. a a ¸ a 14. Statia i1 se poate ˆ ¸ 1 pentru c˘ |ai2 − a1 | = |ai2 − ai1 + ai1 − a1 | = |ai2 − ai1 | + |ai1 − a1 | > d. ˆ configuratia final˘. vom repeta acest rationament pˆn˘ cˆnd a a ¸ ¸ a a a pozitiile cutiilor goale. ¸ ın ¸ a Prezent˘m algoritmul de rezolvare pe baza urm˘torului exemplu: a a 1 2 3 4 5 6 7 initial → 3 1 ¸ 2 6 5 4 final → 1 2 3 4 5 6 rezolvat → × Vom proceda astfel: cutia goal˘ din configuratia initial˘ se afl˘ pe pozitia 3 a ¸ ¸ a a ¸ dar pe aceast˘ pozitie. ¸ a ¸ a ¸ ¸ ¸ ¸ Demonstratie: Fie B = {ai1 . METODA OPTIMULUI LOCAL . O mutare se poate face dintr-o anumit˘ s ın a a pozitie numai ˆ pozitia liber˘.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. se afl˘ num˘rul 3.. ai2 . ¸ • se parcurge ¸irul statiilor ¸i se alege prima statie ˆ alnit˘ care este s ¸ s ¸ ıntˆ a la distant˘ cel putin d fat˘ de statia aleas˘ anterior. a 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. c˘ut˘m num˘rul 3 a ¸ ın ¸ a a a a a a din configuratia initial˘ (ˆ g˘sim pe pozitia 1) ¸i ˆ mut˘m pe pozitia cutiei goale. ¸ ¸ a ıl a ¸ s ıl a ¸ acum.218 CAPITOLUL 14. coincid.3. ¸ ı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 → × × .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. aim } o solutie a problemei care nu contine ¸ ¸ ınlocui cu statia ¸ statia 1 (deci ai1 > a1 ). a a a ¸ Propozitia 3 Exist˘ o solutie optim˘ care contine statia 1...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

268 CAPITOLUL 14.GREEDY . METODA OPTIMULUI LOCAL .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ok=posibil(k). k=1. x[k]=0. } static void regine(int n) { int k.i<=n. else if (k == n) afis(). public static void main(String[] args) { n=5. while (x[k] <= n-1) // caut o valoare posibila { ++x[k]. a Restrictii ¸i conditii de continuare: xi = xj pentru oricare i = j (reginele nu ¸ s ¸ se atac˘ pe coloan˘) ¸i |xi − xj | = |i − j| (reginele nu se atac˘ pe diagonal˘).3. a a s ıncˆ a Reprezentarea solutiei: un vector x unde xi reprezint˘ coloana pe care se afl˘ ¸ a a regina dac˘ linia este i. regine(n). while (k>0) { ok=false.294 CAPITOLUL 15.nv=0.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. if(ok) break. boolean ok. METODA BACKTRACKING 15. else x[++k]=0. static int n. for(int i=0. } if(!ok) k--. x=new int[n+1]. } }//regine() . a as a class RegineI1 { static int[] x. a a s a a Sunt prezentate o variant˘ iterativ˘ ¸i una recursiv˘.i++) x[i]=i.

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

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

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

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

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

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

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

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

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

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

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

a s ¸ s a Din valoarea care se g˘se¸te pe un nivel.i>=1. return. S[k] − 1. static int[] x=new int[n+1]. t1=System.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n").t2. int maxp. dim=poz-1.i).val).maxp). afis1(). 2. t2=System. procedura va apela o alta pentru afi¸area vectorului (initial afi¸eaz˘ n). f(val-i.n. public static void main(String[] args) { long t1. s ¸ ¸ a a a Initial. for(int i=maxok..poz+1).currentTimeMillis(). .306 CAPITOLUL 15. } int maxok=min(maxp. a Vom folosi o procedur˘ f care are doi parametri: componenta la care s-a a ajuns (k) ¸i o valoare v (care contine diferenta care a mai r˘mas pˆn˘ la n).out.8 Generarea partitiilor unui num˘r natural ¸ a S˘ se afi¸eze toate modurile de descompunere a unui num˘r natural n ca a s a sum˘ de numere naturale.i<=dim. Imediat ce este ¸ a s apelat˘. afis2(val. procedura este apelat˘ pentru nivelul 1 ¸i valoarea n.currentTimeMillis().min(val-i. for(int i=1.print("\n"+nsol+" : "). } static void f(int val.print(x[i]+" "). } } static void afis1() { System.out. S[k]. n=6. System. a a La revenire se reface valoarea existent˘. METODA BACKTRACKING 15. } if(val==0) { nsol++. dim=poz-1. int poz) { if(maxp==1) { nsol++.out.i--) { x[poz]=i.3.. return..i++) System. f(n. a class PartitieNrGenerare // n = suma de numere { static int dim=0.1). valori cu care se apeleaz˘ procedura pentru nivelul urm˘tor. nsol=0. } . se scad pe rˆnd valorile a s a 1.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

METODA BACKTRACKING .342 CAPITOLUL 15.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. PROBLEME REZOLVATE }//afissol(. } }// class Rezultate afi¸ate ˆ fi¸ierul de ie¸ire: s ın s s altruisti 0123456789 a1012345678 l2101234567 g3211234567 o4322234567 r5433234567 i6544333456 t7654444445 m8765555555 i9876665665 altruisti --------a|x-------l||x------g|||x-----o||||x----r||||x----i|||||xx--x t|||x|||xxm|||||||||x i||||||x-|x altruisti iiiiiiiii as iiiiiiii lss iiiiiii gsssmiiiiii ossssmiiiii rssss iiiii isssssm ii tsss sssm i msssssssssm issssss is . int b) { return a<b ? a:b.2.16.min(b. } static int min(int a. int c) { return min(a. int b.) 365 static int min(int a.c))..

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

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

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

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

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

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

372 }// for h

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

373

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

16.2.10

Triangularizarea poligoanelor convexe

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

1

8 8

8 7

7

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

374 ¸i s

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

16.2.11

Algoritmul Roy-Floyd-Warshall

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

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

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

// drumuri intre i si j care trec

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

375

3 1 2 1 3 2

2 3 1 * * *

3 4 2 * * *

1 2 2 * * *

* * * 2 1 2

* * * 1 2 3

* * * 2 3 4

16.2.12

Oracolul decide - ONI2001 cls 10

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

376

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

377

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

378

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

379

380

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

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

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

381

16.2.13

Pav˘ri - ONI2001 clasa a X-a a

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

PROGRAMARE DINAMICA .428 ˘ CAPITOLUL 16.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

POTRIVIREA SIRURILOR ¸ .444 CAPITOLUL 17.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GEOMETRIE COMPUTATIONALA ¸ .492 ˘ CAPITOLUL 18.

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

494 CAPITOLUL 19. TEORIA JOCURILOR .

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful