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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

k<=n. nnp=0.println("x = "+x).b=1. k.c=2. long[] ss=new long[n+1]. n=10. for(k=1. iar dac˘ nu s a ın a a a a este num˘r prim s˘ se afi¸eze descompunerea ˆ factori. x=x-y.out. 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. while(x>0) { iy=maxFibo(x). s. ALGORITMI System. return k-1. pentru x = 5678 pe ecran apare: 1 2 3 4 5 6 7 : : : : : : : 5678 f[19] = 418 1497 f[16] = 987 510 f[14] = 377 133 f[11] = 89 44 f[9] = 34 10 f[6] = 8 2 f[3] = 2 s a a ¸ ¸ ıntregi 2.out. } } De exemplu. b. p.k++) if (f[k]>nr) break.println(nrt+" : "+x+" f["+iy+"] = "+y). y=f[iy]. a a s ın Rezolvare: 2 class e02 { public static void main(String[] args) { int a. .38 CAPITOLUL 3. nrt++. a=1. System. s=-b/a. 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!). c. } } static int maxFibo(long nr) { int k.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

int i.. S˘ not˘m prin Rn num˘rul apelurilor functiei a a a ¸ f ibo pentru calculul lui f ibo(n). FUNCTII RECURSIVE ¸ argumentului. 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)). for (i = 2. } return u. v = 1. } . i <= n. v. ¸i Rn = 1 + Rn−1 + Rn−2 s ′ ′ ′ pentru n > 1. f ibo(n) f ibo(n − 1) = 1 1 1 0 u v × = f ibo(n − 1) f ibo(n − 2) 1 1 1 0 × = . u = 1. Punˆnd Rn = Rn + 1. Evident R0 = R1 = 1. a ¸ a ′ ′ ′ ′ a s ¸ a ¸i R1 = R0 = 2. ++i) { u0 = u. Rezult˘ Rn = 2 · f ibo(n) ¸i de aici obtinem c˘ Rn = 2 · f ibo(n) − 1.. u = u0 + v0. = u0 v0 1 1 1 0 n × 1 0 static int fibo(int n) { int u. v0.5.1. v = v0. v0 = v. obtinem c˘ Rn = Rn−1 + Rn−2 pentru n > 1. int u0. 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

coloana 5 ¸ s reprezint˘ submultimea {a2 . while(v[i]>1) { v[i]=v[i]-2. de exemplu. for(i=1.i++) nc*=2. ¸i trecem la ordinul urm˘tor s a 0 ¸i adun˘m 1 s a 1 ¸i adun˘m 1 s a 2 care nu este permis. coloana F reprezint˘ ˆ ¸ a a ıntreaga multime. 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.i++) v[i]=0. OPERATII CU MULTIMI ¸ ¸ initial ¸ 0 0 0 0 0 0 0 0 1 1 · 0 0 0 1 1 1 2 0 · 1 0 ¸i adun˘m 1 s a 1 ¸i adun˘m 1 s a 103 obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem 0 ¸ obtinem · ¸ obtinem 1 ¸ 2 care nu este permis. } . ¸i 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. int[] v=new int[n+1]. } for(i=1. a4 } iar coloana 7 reprezint˘ submultimea {a2 . for(i=1. i=n.i<=n. a a static int[][] submultimi(int n) { int i. int[][] c.i++) c[j][i-1]=v[i]. while(j<nc) { v[n]=v[n]+1. a3 . j=0. Coloana 0 reprezint˘ a ¸ a a multimea vid˘. c=new int[n][nc]. j++.i<=n. j. i--.i<=n. a4 }.7. } return c. ¸i.4. nc=1. v[i-1]=v[i-1]+1. ¸i trecem la ordinul urm˘tor s a 0 care nu este permis.

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

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

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

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

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

. A2 .. Not˘m prin |A| cardinalul multimii A.+(−1)n |A1 ∩A2 ∩...1 Principiul includerii ¸i al excluderii ¸i aplicatii s s ¸ 8.. 2. .Capitolul 8 Algoritmi combinatoriali 8..∩An | Se pot demonstra prin inductie matematic˘ urm˘toarele formule: ¸ a a n n n | i=1 n Ai | = i=1 n |Ai |− 1≤i<j≤n |Ai ∩Aj |+ 1≤i<j<k≤n |Ai ∩Aj ∩Aj |−.+(−1)n+1 | i=1 n Ai | | i=1 Ai | = i=1 |Ai |− 1≤i<j≤n |Ai ∪Aj |+ 1≤i<j<k≤n |Ai ∪Aj ∪Aj |−.. An submultimi ale sale... .. Fie A o multime finit˘ ¸i A1 .. n) este ın ¸ egal cu: n |A|− i=1 |Ai |+ 1≤i<j≤n |Ai ∩Aj |− 1≤i<j<k≤n |Ai ∩Aj ∩Ak |+..+(−1)n+1 | i=1 Ai | 109 .1 Principiul includerii ¸i al excluderii s Fie A ¸i B dou˘ multimi finite.1.. 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|..

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

126 CAPITOLUL 8. ALGORITMI COMBINATORIALI .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

144 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

¸ 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. Pk de dimensiuni n1 . S1 ) . divideEtImpera(Pa . Sk ) combin˘ S1 ..1 Tehnica divide et impera Divide et impera1 este o tehnic˘ de elaborare a algoritmilor care const˘ ˆ a a ın: • Descompunerea repetat˘ a problemei (subproblemei) ce trebuie rezolvat˘ ˆ a a ın subprobleme mai mici. nk ... ın s • Compunerea subsolutiilor pentru a obtine solutia problemei (subproblemei) ¸ ¸ ¸ initiale... ... s a a 1 divide-and-conquer. S) if (n ≤ n0 ) then rezolv˘ subproblema P prin tehnici elementare a else ˆ ımparte P ˆ P1 .. . n1 . Metoda poate fi descris˘ astfel: a procedure divideEtImpera(P.. • Rezolvarea ˆ acela¸i mod (recursiv) a tuturor subproblemelor. ˆ englez˘ ın a 163 .Capitolul 12 Algoritmi divide et impera 12.. nk ın divideEtImpera(P1 . Sk pentru a obtine S a ¸ Exemplele tipice de aplicare a acestei metode sunt algoritmii de parcurgere a arborilor binari ¸i algoritmul de c˘utare binar˘. n.. .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

METODA OPTIMULUI LOCAL .GREEDY .268 CAPITOLUL 14.

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

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

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

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

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

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

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

. .. for(i=1. solutia poate fi pus˘ sub forma unui vector x = (x1 . for(i=1..i<=n.15.j<=m[k]. .println().length-1. n.out.2 Metoda bactracking Se folose¸te pentru rezolvarea problemelor care ˆ s ındeplinesc urm˘toarele conditii: a ¸ 1. multimile Ai sunt finite... f(1). m=new int[n+1]. ¸ a i = 1. System.out. i++) System.i++) m[i]=a[i].j++) { x[k]=a[k][j].. for(j=1. n=a. } static void f(int k) { int j. METODA BACTRACKING public static void main(String[] args) { int i. x=new char[n+1]. xn ) cu xi ∈ Ai .length-1.. i<=n.2. nu se cunoa¸te o alt˘ metod˘ mai rapid˘ de rezolvare. } }// class 277 15. } } static void afisv() { int i. ¸ Tehnica backtracking pleac˘ de la urm˘toarea premis˘: a a a dac˘ la un moment dat nu mai am nici o ¸ans˘ s˘ ajung la solutia a s a a ¸ c˘utat˘.print(x[i]). if(k<n) f(k+1). else afisv(). s a a a 2. renunt s˘ continui pentru o valoare pentru care ¸tiu c˘ nu a a ¸ a s a ajung la nici un rezultat. 3. 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˘. 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 × . 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). • dac˘ au fost ˆ a ıncercate toate valorile corespunz˘toare componentei k ¸i ˆ a a s ınc˘ nu a fost g˘ sit˘ o solutie. METODA BACKTRACKING Specificul metodei const˘ ˆ maniera de parcurgere a spatiului solutiilor.a. 1}4 . × An . a ¸ ˆ figur˘ este ilustrat modul de parcurgere a spatiului solutiilor ˆ cazul In a ¸ ¸ ın gener˘rii produsului cartezian{0.m... a ˆ cazul ˆ care sunt specificate restrictii (ca de exemplu.. ¸. se revine a a ¸ s s ¸ la componenta anterioar˘ (k−1) ¸i se ˆ a s ıncearc˘ urm˘toarea valoare corespunz˘ toare a a a acesteia. ıncˆ a a a ¸ • la completarea componentei k se verific˘ dac˘ solutia partial˘ (x1 . x2 . s˘ nu fie dou˘ In ın ¸ a a componente al˘turate egale) anumite ramuri ale structurii arborescente asociate a sunt abandonate ˆ ınainte de a atinge lungimea n.. sau dac˘e dore¸te determinarea unei noi solutii.d. a ın ¸ ¸ • solutiile sunt construite succesiv.278 CAPITOLUL 15. xk ) a a ¸ ¸ a verific˘ conditiile induse de restrictiile problemei (acestea sunt numite conditii de a ¸ ¸ ¸ continuare). ..

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

342 CAPITOLUL 15. METODA BACKTRACKING .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

428 ˘ CAPITOLUL 16. PROGRAMARE DINAMICA .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

POTRIVIREA SIRURILOR ¸ .444 CAPITOLUL 17.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

492 ˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ .

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful