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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

v. v0 = v. u = u0 + v0. i <= n. v = v0. Punˆnd Rn = Rn + 1.. } . Deci f ibo(4) → → f ibo(3) + f ibo(2) (f ibo(2) + f ibo(1)) + f ibo(2) ((f ibo(1) + f ibo(1)) + f ibo(1)) + f ibo(2) ((1 + f ibo(1)) + f ibo(1)) + f ibo(2) ((1 + 1) + f ibo(1)) + f ibo(2) (2 + f ibo(1)) + f ibo(2) (2 + 1) + f ibo(2) 3 + f ibo(2) 3 + (f ibo(1) + f ibo(1)) 3 + (1 + f ibo(1)) 3 + (1 + 1) 3+2 5 73 → → → → → → → → → → → Exist˘ deci un num˘r semnificativ de apeluri succesive ale functiei f ib (9 a a ¸ apeluri pentru calculul lui f ibo(4)). f ibo(n) f ibo(n − 1) = 1 1 1 0 u v × = f ibo(n − 1) f ibo(n − 2) 1 1 1 0 × = . v = 1. u = 1. v0.1.. } return u. 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.5. = u0 v0 1 1 1 0 n × 1 0 static int fibo(int n) { int u. int u0. Evident R0 = R1 = 1. for (i = 2. int i. Rezult˘ Rn = 2 · f ibo(n) ¸i de aici obtinem c˘ Rn = 2 · f ibo(n) − 1. ++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. a ¸ a ′ ′ ′ ′ a s ¸ a ¸i R1 = R0 = 2. obtinem c˘ Rn = Rn−1 + Rn−2 pentru n > 1. FUNCTII RECURSIVE ¸ argumentului.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1) − 1− √ 5 n .3.5) (8.. 233. Numerele lui Fibonacci satisfac multe identit˘¸i interesante.. In a a a . 55. F1 = 1.3.3. + Fn F1 F2 + F2 F3 + .3.10) (8.6) 2 Fn+1 Fn−1 − Fn Fn+m Fnk = Fm Fn+1 + Fm−1 Fn = multiplu de Fk ¸i s F2 + F4 + . a a Folosind aceast˘ descompunere.. atunci aceast˘ descompunere este unic˘ abstractie a a ¸ f˘cˆnd de ordinea termenilor...3. 144. 987.8) (8.11) F1 F2 + F2 F3 + .3) (8. 34.3.4) (8. NUMERE REMARCABILE 115 8.3. 610. 377.3.. 8.1 Numerele lui Fibonacci Numerele lui Fibonacci se pot defini recursiv prin: F0 = 0.. Primele numere Fibonacci sunt: 0.. Dac˘ nu se folosesc ˆ descompunere numerele F0 ¸i F1 ¸i nici dou˘ a ın s s a numere Fibonacci consecutive. 5. 1.. 2.3. ca de exemplu: at 1 1 1 0 n = = Fn+1 Fn (−1)n Fn Fn−1 (8. 89.3. + F2n F1 + F3 + . + F2n−1 F2n = F2n+1 − 1 = F2n = Fn Fn+1 2 = F2n 2 = F2n+1 − 1 (8. + F2n F2n+1 Teorema 2 Orice num˘r natural n se poate descompune ˆ a ıntr-o sum˘ de numere a Fibonacci. 1597. 3. Fn = Fn−1 + Fn−2 pentru n ≥ 2.7) (8. + F2n−1 2 2 2 F1 + F2 + ... . 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.8. numerele naturale pot fi reprezentate asem˘n˘tor a a a reprezent˘rii ˆ baza 2.3. 13.3..9) (8.2) (8.3. 21. Se poate ar˘ta c˘ a a Fn = 2n 1 √ 5 1+ √ 5 n (8. 1.

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

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

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

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

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

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

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

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

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

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

126 CAPITOLUL 8. ALGORITMI COMBINATORIALI .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

144 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GREEDY . METODA OPTIMULUI LOCAL .268 CAPITOLUL 14.

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

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

... a a ın 0 1 2 3 4 5 0 0 1 0 1 .. deci k = k − 1 a 0 1 2 3 4 5 0 0 0 1 .. . a ¸ 0 1 2 3 4 5 0 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.... Pe pozitia k nu se mai poate pune o nou˘ valoare ¸i s ¸ a s atunci: • se pune gol pe pozitia k ¸ • se face pasul spre stˆnga....... n n+1 Aici k = 2 ¸i 0 < max. deci ˆ acest caz cu 1... 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. .. n n+1 Dup˘ plasarea unei valori pe pozitia k. .. n n+1 Aici k = 4 = n ¸i 9 = max. deci k = k + 1 0 1 2 3 4 5 0 1 0 1 ... se face pasul spre dreapta (k = k + 1). .. . ˆ urma pasului f˘cut spre dreapta!). deci k = k − 1 a 0 1 2 3 4 5 0 0 9 0 1 . 0 1 2 3 4 5 0 0 1 0 0 1 .. n n+1 Aici (dac˘ nu am ie¸it ˆ afara vectorului.1.. se plaseaz˘ prima valoare valid˘ (deci ın s ¸ a a a valoarea min)... .15.. ˆ ¸elegem de ce este util s˘ initializ˘m ınt a ¸ a variabila gol cu valoarea min − 1. n n+1 Aici k = 3 ¸i 9 = max. S˘ consider˘m c˘ s-a ajuns la configuratia a a a ¸ 0 1 2 3 4 5 0 0 9 9 0 1 . a s ın ın a ˆ cazul nostru k = 4 ¸i pozitia este goal˘. n n+1 Cum trecerea de la o valoare la alta se face prin cre¸terea cu o unitate a valorii s vechi iar acum facem trecerea gol → min. GENERAREA PRODUSULUI CARTEZIAN 271 Pe pozitia k = 3 se af˘ valoarea 0 care se va schimba cu urm˘toarea valoare ¸ a a (dac˘ exist˘ o astfel de valoare ≤ max). 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 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.. METODA BACKTRACKING • se face pasul spre dreapta. . .. . n n+1 0 1 2 3 4 5 9 9 9 se gole¸te pozitia ¸i ajungem la k = 2 s ¸ s 0 1 ... a ın a a a ¸ ˆ ıncerc˘m s˘ punem urm˘toarea valoare (gol are ca ”urm˘toarea valoare” pe min) a a a a pe aceast˘ pozitie ¸i a ¸ s • dac˘ se poate: se pune urm˘toarea valoare ¸i se face pasul spre dreapta. n n+1 Nu-i nimic! Aici s-a terminat generarea produsului cartezian! .. n n+1 0 1 2 3 4 5 iar aici k = 0 ¸i am ajuns ˆ afara vectorului! s ın 0 1 .... Ajungem la k = 4 s a ¸ 0 1 .. Pe pozitia k se ¸ s a s ¸ poate pune o nou˘ valoare: a • se pune 0 + 1 = 1 = urm˘toarea valoare pe pozitia k a ¸ • se face pasul spre dreapta. ..... n n+1 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.. a a s • dac˘ nu se poate: se pune gol=min − 1 ¸i se face pasul spre stˆnga. ... 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 1 0 1 . ...... n n+1 0 1 2 3 4 5 9 9 se gole¸te pozitia ¸i ajungem la k = 1 s ¸ s 0 1 .272 CAPITOLUL 15. . Aici k = 4 ¸i 0 < max.. deci k = k + 1 0 1 2 3 4 5 0 1 0 0 0 1 . n n+1 0 1 2 3 4 5 9 se gole¸te pozitia ¸i ajungem la k = 0 s ¸ s 0 1 . Pe pozitia k se poate pune o nou˘ valoare: s ¸ a • se pune gol+1= −1 + 1 = 0 = urm˘toarea valoare pe pozitia k a ¸ • se face pasul spre dreapta. deci k = k + 1 0 1 2 3 4 5 0 1 0 0 1 .... n n+1 A ap˘rut ˆ mod evident urm˘toarea regul˘: ajungˆnd pe pozitia k ≤ n. . n n+1 Aici k = 4 ¸i gol=-1< max....

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

METODA BACKTRACKING 15. a a s a a Sunt prezentate o variant˘ iterativ˘ ¸i una recursiv˘.i++) x[i]=i.294 CAPITOLUL 15. static int n. while (x[k] <= n-1) // caut o valoare posibila { ++x[k]. } static void regine(int n) { int k. } if(!ok) k--. else if (k == n) afis().3.3 Problema reginelor pe tabla de ¸ah s O problem˘ clasic˘ de generare a configuratiilor ce respect˘ anumite restrictii a a ¸ a ¸ este cea a amplas˘rii damelor pe o tabl˘ de ¸ah astfel ˆ at s˘ nu se atace reciproc. 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˘). regine(n). while (k>0) { ok=false. if(ok) break. a as a class RegineI1 { static int[] x.i<=n. } }//regine() . boolean ok.nv=0. x[k]=0. for(int i=0. ok=posibil(k). a a s ıncˆ a Reprezentarea solutiei: un vector x unde xi reprezint˘ coloana pe care se afl˘ ¸ a a regina dac˘ linia este i. k=1. x=new int[n+1]. else x[++k]=0. public static void main(String[] args) { n=5.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

color[u]=BLACK.. cand u=terminat ==> plasaz in lista pe prima pozitie libera de la sfarsit catre inceput Solutia nu este unica (cea mai mica lexicografic = ???) O(n*n)= cu matrice de adiacenta O(n+m)= cu liste de adiacenta import java.v<=n. . } }//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.j<=n. METODA BACKTRACKING for(v=1. ctc[u]=nctc. //"at" //if((a[lista[j]][u]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j])...v)=arc ==> ".j++) if((at[u][lista[j]]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]). u=nod 2.io. v ..316 CAPITOLUL 15." in lista ("u se termina inaintea lui v") Algoritm: 1.3. color[u]=GRAY.v++) if((a[u][v]==1)&&(color[v]==WHITE)) dfsa(v).13 Sortare topologic˘ a Folosind parcurgerea ˆ adˆncime ın a // // // // // // // // Sortare Topologica = ordonare lineara a varfurilor (in digraf) (u.*. f[u]=++t. for(j=1. } static void dfsat(int u) // se poate folosi "a" inversand arcele ! { int j. DFS pentru calcul f[u]. lista[pozLista--]=u... //"a" color[u]=BLACK. u .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

342 CAPITOLUL 15. METODA BACKTRACKING .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

gata:boolean.s.next). begin for i:=1 to n do best[i]:=0. var c. if q=u then gata:=true. {expandarea corespunzatoare cifrei curente} procedure calc(nr:longint.t+1.t:=p^. repeat if (cif>0) and (nc-nr<=(n-p^.last:=cif end. q:=p.dispose(q) until gata.x).s>best[q^.s:=p^.s:=p^.t]=p^. readln(f.cif:byte). u^. u^. q:pnod.418 ˘ CAPITOLUL 16. var i:longint. u^.x:=x div 10 until x=0 end.p:=p^. end.t] then best[q^. u^.in’).last<100)and(nc-nr>=n-p^. if (p^.next. u^.s+cif.q:pnod.t-1)*3+2) and (best[p^. repeat if q^. u^.last*10+cif.gata:=false. if p=c then gata:=true. close(f) end.next).t]:=q^.next. gata:boolean.s) then begin new(u^. q:=p.t.’suma.last*9+cif. PROGRAMARE DINAMICA assign(f. .t) then begin new(u^.u:=u^.last:=p^. repeat inc(nc). {recalcularea valorilor maxime memorate in vectorul best} procedure optim.next.reset(f). begin c:=u.t:=p^. end.gata:=false.u:=u^. for i:=1 to n do begin read(f.n).s+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. calc(i. s a Obtinem memorie O(N ) ¸i timp de executie O(L ∗ N ) = O(N 2 ). ignorand spatiile} reset(f).ord(c)-48).s.s:=ord(c)-48. 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.t:=1. p^.N ).s.c) until c<>’ ’.c) until c<>’ ’. for i:=2 to nc do begin repeat read(f. readln(f). new(p). assign(f. best[1]:=p^. u:=p. // punˆnd ultima sum˘ format˘ doar din cifra a[i] a a a smax[p-2][n-1]+a[p-1]*10+a[p]. PROBLEME REZOLVATE q:=q^. a¸a c˘ excludem termenii din expresia de minim.close(f) END. {reluarea citirii cifrelor. BEGIN citire.2.. ¸ s ¸ . p^. Obtinem ¸ a¸ s a a ın ¸ relatia: ¸ smax[p][n] = min( smax[p-1][n-1]+a[p]. close(f).p^. end. p = 2 sau p = 3 ¸i cazurile ın a a s ˆ care a[p].. repeat read(f.best[n]).rewrite(f).out’). moment ˆ care nu putem forma o sum˘ ın ın a de lungimea respectiv˘.next until gata. nu p˘str˘m decˆt pe a a ¸ a a a acestea ¸i linia curent˘ pentru a nu avea probleme cu memoria. a[p − 1] sau a[p − 2] sunt zero. optim end. Concaten˘m nua a a merele ¸i obtinem un ¸ir (s˘ ˆ not˘m a) de cifre (de lungime L maxim N ∗ 3).’suma.last:=p^. 419 Solutia II ¸ Problema se rezolv˘ prin metoda programare dinamic˘. iar ın s a a ımp˘ ¸ ın ın observˆnd c˘ recurenta depinde doar de ultimele 3 linii.L) calcul˘m prima maxim˘ pe care o putem ¸ a a obtine desp˘rtind sub¸irul de pˆn˘ la p inclusiv ˆ n sume (n = 1. s ¸ s a ıl a Pentru fiecare pozitie p (p = 1.16.writeln(f.

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

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

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

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

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

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

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

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

PROGRAMARE DINAMICA .428 ˘ CAPITOLUL 16.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

POTRIVIREA SIRURILOR ¸ .444 CAPITOLUL 17.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GEOMETRIE COMPUTATIONALA ¸ .492 ˘ CAPITOLUL 18.

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

494 CAPITOLUL 19. TEORIA JOCURILOR .

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful