ALGORITMI SI STRUCTURI DE DATE ¸ Note de curs

(uz intern - draft v2.3)

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

ALGORITMICA (model virtual)

PROGRAMARE

Gandire algoritmica

Experienta (rezolvarea de probleme)

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

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

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

2 Aprobat˘ a

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

3.3

3.4

3.5

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. . . . .

. . . . .

. . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

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

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

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1.1. PROGRAME CIUDATE

3

1.1.3

Un program ciudat ˆ Java ın

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

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

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

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

4

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.1.4

Structura unui program Java

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

1.2. CONVERSII ALE DATELOR NUMERICE

5

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

1.2

Conversii ale datelor numerice

1.2.1

Conversia din baza 10 ˆ baza 2 ın

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

6

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.2.2

Conversia din baza 2 ˆ baza 10 ın

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

1.2.3

Conversii ˆ ıntre bazele 2 ¸i 2r s

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

Capitolul 2

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

2.1

Date ¸i structuri de date s

2.1.1

Date

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

8

CAPITOLUL 2. STRUCTURI DE DATE

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

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

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

2.1. DATE SI STRUCTURI DE DATE ¸

9

2.1.2

Structuri de date

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

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

10

CAPITOLUL 2. STRUCTURI DE DATE

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

2.2

Structuri ¸i tipuri de date abstracte s

2.2.1

Structuri de date abstracte

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

2.2.2

Tipuri de date abstracte

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

2.3. STRUCTURI DE DATE ELEMENTARE

11

2.3

Structuri de date elementare

2.3.1

Liste

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

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

12

CAPITOLUL 2. STRUCTURI DE DATE

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

2.3.2

Stive ¸i cozi s

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

126 CAPITOLUL 8. ALGORITMI COMBINATORIALI .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ALGORITMI ELEMENTARI DE SORTARE .144 CAPITOLUL 10.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Pentru o a ¸ a ın a astfel de list˘.next). while (b. a¸ Nu trat˘m acest exercitiu decˆt ˆ cazul listelor circulare cu gard˘. b. b). Cˆmpul next din ultima celul˘ contine adresa primei celule. } static Lista nReverse1 (Lista b. a). a. Lista a) { if (a == null) return b. gratie unei functii auxiliare a ¸ ¸ care acumuleaz˘ rezultatul ˆ a ıntr-unul din argumentele sale: static Lista nReverse (Lista a) { return nReverse1(null. } .next)) b = b. a a a ¸ a a1 garda a2 a3 a4 a5 a6 Figura 11.162 CAPITOLUL 11. } Un alt exercitiu formator const˘ ˆ a administra liste ˆ care elementele sunt ¸ a ın ın aranjate ˆ ordine cresc˘toare. else return nReverse1(adauga(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. Lista a) { Lista b = a. a. LISTE Se poate scrie o versiune iterativ˘ a versiunii recursive. return a.info = head(a) + 1.next = adauga(v.info.next != a && v > head(b.next. b. valoarea cˆmpului inf o din prima celul˘ nu are nici o semnificatie a a a ¸ (este celula de gard˘).next).8: List˘ circular˘ cu gard˘ a a a static Lista insereaza (int 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. ˆ englez˘ ın a 163 . n.... S) if (n ≤ n0 ) then rezolv˘ subproblema P prin tehnici elementare a else ˆ ımparte P ˆ P1 . n1 . ..Capitolul 12 Algoritmi divide et impera 12. Pk de dimensiuni n1 . nk .. nk ın divideEtImpera(P1 .. .. s a a 1 divide-and-conquer.. .. 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˘. Metoda poate fi descris˘ astfel: a procedure divideEtImpera(P.. S1 ) . Sk ) combin˘ S1 . ın s • Compunerea subsolutiilor pentru a obtine solutia problemei (subproblemei) ¸ ¸ ¸ initiale. • Rezolvarea ˆ acela¸i mod (recursiv) a tuturor subproblemelor. divideEtImpera(Pa . ¸ Descompunerea problemei (subproblemelor) se face pˆn˘ n momentul ˆ care a aˆ ın se obtin subprobleme de dimensiuni atˆt de mici ˆ at au solutie cunoscut˘ sau ¸ a ıncˆ ¸ a pot fi rezolvate prin tehnici elementare.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[an .7 Problema sub¸irurilor s S˘ se descompun˘ un ¸ir de n numere ˆ a a s ıntregi ˆ sub¸iruri strict cresc˘toare ın s a astfel ˆ at numerele lor de ordine din ¸irul initial s˘ fie ordonate cresc˘tor ˆ ıncˆ s ¸ a a ın sub¸irurile formate ¸i num˘rul sub¸irurilor s˘ fie minim. ¸ ¸ a 1 2 3 4 5 6 7 modificat → 1 2 3 6 4 5 final → 1 2 3 4 5 6 rezolvat → × × × × × 1 2 3 4 5 6 7 modificat → 1 2 3 4 5 6 final → 1 2 3 4 5 6 rezolvat → × × × × × × 14.3. se selecteaz˘ primul interval. unul a a . b1 ]. se va alege acela care are cel mai mare ultim element.3. a 1 2 modificat → 1 2 final → 1 2 rezolvat → × × 3 4 5 3 6 3 4 × nerezolvat˘ a 3 3 3 × 4 6 5 4 219 6 7 5 4 5 6 × ¸i vom muta num˘rul din acea cutie ˆ s a ın 6 5 5 × 7 4 6 Repet˘m rationamentul prezentat la ˆ a ¸ ınceput ¸i vom continua pˆn˘ cˆnd toate s a a a numerele ajung pe pozitiile din configuratia final˘.. [a2 . a Observatie: Ultimele elemente din fiecare sub¸ir (care sunt ¸i elemente maxime ¸ s s din aceste sub¸iruri) formeaz˘ un ¸ir descresc˘tor. b2 ]. ıncˆ a a Metoda de rezolvare este urm˘toarea: se sorteaz˘ intervalele cresc˘tor dup˘ a a a a cap˘tul din dreapta. bn ].. Acest fapt permite utilizarea s a s a c˘ut˘rii binare pentru determinarea sub¸irului potrivit pentru elementul xi a a s atunci cˆnd prelucr˘m acest element. se parcurg intervalele. dac˘ nu exist˘ un astfel de sub¸ir se creeaz˘ un sub¸ir nou a as a a s a s care va contine ca prim element pe xi . 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. EXEMPLE 1 2 modificat → 1 2 final → 1 2 rezolvat → × × Acum vom c˘uta o cutie a cutia goal˘.. dac˘ exist˘ mai multe sub¸iruri la care se ¸ a a s poate ad˘uga xi .8 Problema intervalelor disjuncte Se consider˘ n intervale ˆ a ınchise pe axa real˘ [a1 . Se a cere selectarea unor intervale disjuncte astfel ˆ at num˘rul acestora s˘ fie maxim. . a a 14.14.3.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// distante de la nod catre arbore static int[] a1. EXEMPLE URGENTA.OUT 27 8 13 17 24 34 37 45 56 67 243 Timp maxim de executare: 1 secund˘ / test a Codul surs˘ a import java.m. // ncc = nr componente conexe static int[][] cost. st. // predecesor in arbore static int[] d.nval. st. si una slaba merge! { static final int oo=0x7fffffff. // a2[k]=varful 2 al muchiei k din arbore static int[] ac.out"))).. ncc=(int)st.IN 7 11 4 121 132 173 243 342 351 361 375 455 564 673 URGENTA.nval.min. j. PrintWriter out= new PrintWriter( new BufferedWriter(new FileWriter("urgenta. // arbore minim de acoperire: algoritmul lui Prim O(n^2) class Urgenta // sortare O(n^2) .nval. costArbore=0.nrm.nextToken().in"))).io. k.aux. n=(int)st.costmax.14. static int n. static int[] p..*.3.nextToken(). st. StreamTokenizer st= new StreamTokenizer( new BufferedReader(new FileReader("urgenta.nextToken().ncc. m=(int)st.gravmax.jmin=0. // a1[k]=varful 1 al muchiei k din arbore static int[] a2. . // a1[k]=costul muchiei k din arbore public static void main(String[]args) throws IOException { int nodStart=3. static boolean[] esteInArbore. // nod start int i.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

METODA OPTIMULUI LOCAL .GREEDY .268 CAPITOLUL 14.

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

.. .. de ap˘..... Folosim un vector cu n elemente a = (a1 .. 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 .. Este a a util˘.. ˆ acest moment. n n+1 Folosim o variabil˘ k pentru a urm˘ri pozitia din vector pe care se schimb˘ a a ¸ a valorile. a s˘ 0 1 2 3 4 5 0 0 0 0 0 1 . pentru n = 4). ¸ a 0 1 2 3 4 5 0 0 0 0 1 . pe aceast˘ pozitie k. . pe care putem s˘-l afi¸am.. odat˘ cu aparitia valorii 9 = max. n n+1 Trebuie s˘ construim urm˘toarele elemente ale produsului cartezian..... se poate spune c˘ aici este cheia metodei backtraching! Dac˘ reu¸im a a s s˘ ˆ ¸elegem acest pas de trecere de la o configuratie la alta ¸i s˘ formaliz˘m ce a ınt ¸ s a a am observat. . La ˆ ınceput k = n ¸i.. 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. 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˘).... max]. .. s 0 1 2 3 4 5 0 1 .... 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 . ...270 CAPITOLUL 15.... de gaze. 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. ˆ 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-au epuizat toate ¸ a ¸ valorile posibile. METODA BACKTRACKING (de exemplu. etc. n n+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 . ... deci k = k − 1 (acum k = 3)... n n+1 0 1 2 3 4 5 0 0 0 2 0 1 . n n+1 Ei bine. a ın Este totu¸i util˘ initializarea gol=min-1.. valorile se schimb˘ crescˆnd de s a ¸ a a la min c˘tre max. . a2 .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3 static int[][] harta= {// CoCaIaBrTu {2..0.1.1. Relatia de vecin˘tate ıncˆ a ta a ¸ a dintre ¸˘ri este retinut˘ ˆ ta ¸ a ıntr-o matrice n × n ale c˘rei elemente sunt: a vi.j = 1 dac˘ i este vecin˘ cu j a a 0 dac˘ i nu este vecin˘ cu j a a Reprezentarea solutiilor: O solutie a problemei este o modalitate de col¸ ¸ orare a ¸˘ rilor ¸i poate fi reprezentat˘ printru-un vector x = (x1 . Restrictii ¸i conditii de continuare: Restrictia ca dou˘ ¸˘ri vecine s˘ fie col¸ s ¸ ¸ a ta a orate diferit se specific˘ prin: xi = xj pentru orice i ¸i j avˆnd proprietatea vi.2.2. static int n=harta..// Ialomita (2) {1.out.. .// Constanta (0) {1..3. xn ) cu ta s a xi ∈ {1. // indicele pentru tara boolean okk. 2. Multimile de valor ale a a ta ¸ elementelor sint A1 = A2 = . = An = {1. ..5 Problema color˘rii h˘rtilor a a ¸ Se consider˘ o hart˘ cu n ¸˘ri care trebuie colorat˘ folosind m < n culori.0. static int nrVar=0..1..1.2. . } // main static void harta() { int k.1. a class ColorareHartiI1 { static int nrCulori=3.0}.1..j = 1. .// Calarasi (1) {1. x2 .2} // Tulcea (4) }.2.// culorile sunt 1. static int[] culoare=new int[n]. a a ta a astfel ˆ at oricare dou˘ ¸˘ri vecine s˘ fie colorate diferit.0...k = 1. public static void main(String[] args) { harta().. if(nrVar==0) System. x2 ..1}.// Braila (3) {1..0. xk ) ¸ a a ¸ ¸ a este: xk = xi pentru orice i < k cu proprietatea c˘ vi.length.println("Nu se poate colora !").1. a s a Conditia de continuare pe care trebuie s˘ o satisfac˘ solutia partial˘ (x1 . m} reprezentˆnd culoarea asociat˘ ¸˘rii i. .1}.. m}. 2.0}. METODA BACKTRACKING 15.1.298 CAPITOLUL 15.

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

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

break. 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”.m. .abs(i-x[k-1])!=2)&&(Math.j. x[k]=i. ˆ s a Intre oricare doi vecini izbucnesc conflicte. conditiile de continuare sunt: ¸ a) xj = xk .15. de la stanga la dreapta cu 1.3. pentru j = 1. k − 1 (x trebuie s˘ fie permutare) a b) |xk − xk−1 | = 2 sau 3 (ˆ ıntre persoana k ¸i vecinul s˘u anterior trebuie s˘ se s a a afle una sau dou˘ persoane) a ˆ locul solutiei x vom lista permutarea y = x−1 unde yi reprezint˘ persoana In a care se aseaz˘ pe locul i. 2..j<k.. } static void f(int k) { boolean ok. f(1). if(k<n) f(k+1). for(j=1.i<=m. x=new int[n+1]. int i. } . a s ¸ s Vom rezolva problema prin metada backtracking.. for(i=1.nsol=0. PROBLEME REZOLVATE 301 15.j++) if(i==x[j]) {ok=false. static int n.3.6 Problema vecinilor Un grup de n persoane sunt a¸ezate pe un rˆnd de scaune. a s Pentru k > 1 dat. else afis(). public static void main(String[] args) { n=7.} if(!ok) continue.i++) { ok=true. a class VeciniR1 { static int[] x. Presupunem c˘ persoanele a sunt numerotate la ˆ ınceput... m=7. . n.. if((Math.abs(i-x[k-1])!=3)) continue.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

(k ≥ 1) unde n1 . nk sunt numere naturale care verific˘ urm˘toarea relatie: a a ¸ n1 ≥ n2 ≥ . ≥ nk ≥ 1 Cerint˘ ¸a Fiind dat un num˘r natural n. n2 .3..in partitie.330 CAPITOLUL 15..in contine pe prima linie num˘rul n s ¸ a Datele de ie¸ire s Fi¸erul partitie.. s˘ se determine cˆte partitii ale lui se pot a a a ¸ scrie.ONI2003 clasa a X-a ¸ Se define¸te o partitie a unui num˘r natural n ca fiind o scriere a lui n sub s ¸ a forma: n = n1 + n2 + . ¸ Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ N ≤ 160 Exemplu partitie.out 7 5 Explicatii: ¸ Cele cinci partitii sunt: ¸ • 1+1+1+1+1+1+1 • 1+1+1+1+3 • 1+1+5 • 1+3+3 •7 Timp maxim de executare: 3 secunde/test Indicatii de rezolvare * ¸ Stelian Ciurea Problema se poate rezolva ˆ mai multe moduri: ın Solutia comisiei se bazeaz˘ pe urm˘toarele formule ¸i teoreme de combina¸ a a s toric˘: a .... conform cerintelor de mai sus. METODA BACKTRACKING 15. ≥ ni ≥ .. + nk .. a a Datele de intrare Fi¸ierul partitie. ..out va contine pe prima linie num˘rul de partitii ale lui n s ¸ a ¸ conform cerintelor problemei. ¸tiind c˘ oricare num˘r ni dintr-o partitie ¸ s a a ¸ trebuie s˘ fie un num˘r impar.18 Partitie .

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

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

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

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

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

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

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

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

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

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

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

METODA BACKTRACKING .342 CAPITOLUL 15.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

} static void afisv(char[]v) { int i.out.length-1. for(i=1.length-1. System.println(). out. } } Pe ecran apar urm˘toarele rezultate: a 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 0 1 1 2 2 2 2 2 2 2 2 2 2 2 0 1 2 2 2 2 2 2 2 2 2 2 2 4 0 1 2 2 3 3 3 3 3 3 3 3 3 6 0 1 2 2 3 3 4 4 4 4 4 4 4 5 0 1 2 2 3 4 4 4 4 4 4 4 4 a 0 1 2 2 3 4 4 5 5 5 5 5 5 c 0 1 2 2 3 4 4 5 5 6 6 6 6 b 0 1 2 2 3 4 4 5 6 6 6 6 6 d 0 1 2 2 3 4 4 5 6 6 7 7 7 f 0 1 2 2 3 4 4 5 6 6 7 7 8 e 0 1 2 2 3 4 4 5 6 6 7 8 8 359 1 2 3 4 5 6 a b c d e f 1 3 2 4 6 5 a c b d f e 1 2 3 4 * | | | * | * * - . PROBLEME REZOLVATE for(i=1.i<n.print(d[i][j]+" ").i++) System.out. System.print(v[i]).print(x.charAt(i-1)+" "). for(j=0.println().print(v[i]).16.println("\n").out.2.out.i++) { System. } System.j<m.i<=v. for(i=1.j++) System.length. m=d[i].i<=v.out.println().out.i++) out.

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

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

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

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

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

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

nextToken(). int[][] c.2. g=new int[n+1]. class Rucsac01 { public static void main(String[] args) throws IOException { int n.366 1 2 3 4 5 6 7 8 9 a 1 l 2 g m 3 o s r 4 i 5 i 6 i 7 t 8 m s i 9 a l t r u i s t i ˘ CAPITOLUL 16.i++) { st.io.m.j. . st. for(i=1.nval. int i. g[i]=(int)st.nval. n=(int)st. int[] g.i<=n. PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("rucsac.out"))).nextToken().6 Problema rucsacului (0 − 1) import java. v[i]=(int)st. i++) c[i][0]=0. v=new int[n+1].*.nextToken().nextToken(). i<=n. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("rucsac. c=new int[n+1][m+1]. } for(i=1.v.in"))).i++) { st. } for(i=1.nval.nval.i<=n. PROGRAMARE DINAMICA COST transformare = 5 16. st. m=(int)st.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

428 ˘ CAPITOLUL 16. PROGRAMARE DINAMICA .

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

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

UN ALGORITM EFICIENT . ¸ s Pentru t: 1 a ¸i s 1 a 2 b 3 a 1 a 3 a 4 b 2 b 4 b 5 a 3 a 5 a a 1 5 a 6 b 4 a 6 b b 2 6 b 7 a 5 b 7 a a 3 7 a 8 a 6 a 8 a a 4 8 a 9 b 7 b 9 b b 5 9 b 10 a 11 b 12 a 13 a 14 b 15 a 16 b 17 a p: 2 b exist˘ dou˘ potriviri: a a 10 a a 6 10 a a 1 11 b b 7 11 b b 2 12 a 13 a 14 b 15 a 16 b 17 a t: p: t: p: 1 a 2 b 3 a 4 b 12 a a 3 13 a a 4 14 b b 5 15 a a 6 16 b b 7 17 a Sirul ineficient de ˆ ¸ ıncerc˘ri este: a 1 a a 2 b b a 3 a a b a 4 b a a b a 5 a b a a b a 6 b a b a a b a 7 a b a b a a b a 8 a b a b a a b a 9 b 10 a 11 b 12 a 13 a 14 b 15 a 16 b 17 a t: p: p: p: p: p: p: p: p: p: p: p: b a b a a b a b a b a a b a b a b a a b a 1 * b a b a a b 2 b a b a a 3 b a b a 4 b a b 5 b a 6 * b 7 Prima nepotrivire din fiecare ˆ ıncercare este evidentiat˘ prin caracter boldat ¸ a iar solutiile sunt marcate cu *.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.2 Un algoritm eficient .2.17. f˘r˘ s˘ risc˘m aa a a s˘ pierdem vreo solutie! a ¸ .

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

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

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

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

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

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

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

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

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

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

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

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

POTRIVIREA SIRURILOR ¸ .444 CAPITOLUL 17.

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

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

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

. a ın P7 P6 P5 P1 P2 P3 P4 P1 P2 P3 P4 P7 P6 P5 a) b) 18. !!! { .*.1 5 4 3 2 1 1 2 3 4 5 6 7 5 4 3 2 1 1 2 3 4 5 6 7 a) b) Toate punctele de pe ˆ a¸ur˘toarea convex˘ (cazul a) ˆ figur˘): ınf˘s a a ın a import java.6 ˆ a¸ur˘toarea convex˘ Inf˘s a a ˆ Impachetarea Jarvis 18. 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. Dac˘ punctul este ˆ interiorul unui poligon convex ¸ a ın obtinut prin partitionarea poligonului concav atunci el se afl˘ ˆ interiorul acestuia. ¸ ¸ a ın Dac˘ nu se afl˘ ˆ nici un poligon convex obtinut prin partitionarea poligonului a a ın ¸ ¸ concav atunci el nu se afl˘ ˆ interiorul acestuia..io. // infasuratoare convexa class Jarvis1 // pe frontiera coliniare ==> le iau pe toate .448 ˘ CAPITOLUL 18.

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

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

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

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

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

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

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

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

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

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

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

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

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

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.1: Dreptunghi minim de acoperire Putem s˘ presupunem c˘ punctele formeaz˘ un poligon convex.nval. Dintre aceste dreptunghiuri ¸ a se alege cel cu aria minim˘. } scanareGraham(). x[k]=(int)st.1) pentru a prelucra mai putine puncte dar nu este obligatorie a ¸ aceast˘ strategie.462 ˘ CAPITOLUL 18. }//main }//class 18. a M P7 A P1 P2 N xmin D P6 P12 P10 P3 P11 P4 P5 C Q xmax ymin R ymax P8 P9 B Figura 18. Pentru fiecare latur˘ a poligonului convex se determin˘ drepa a tunghiul minim de acoperire care contine acea latur˘. o[k]=k. GEOMETRIE COMPUTATIONALA ¸ st. a .nval. y[k]=(int)st. st.nextToken().7 Dreptunghi minim de acoperire a punctelor Se poate determina dreptunghiul minim de acoperire pentru ˆ a¸ur˘toarea ınf˘s a convex˘ (figura 18.nextToken().

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GEOMETRIE COMPUTATIONALA ¸ .492 ˘ CAPITOLUL 18.

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

494 CAPITOLUL 19. TEORIA JOCURILOR .

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful