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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

126 CAPITOLUL 8. ALGORITMI COMBINATORIALI .

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

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

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

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

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

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

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

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

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

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

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

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

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

Deci.140 CAPITOLUL 10. ea are o proprietate foarte bun˘: num˘rul de operatii depinde puternic In a a ¸ de ordinea initial˘ din tablou. Atunci a ¸ ci = 1 + card{aj |aj > ai . aceast˘ metod˘ de sortare este mai s s a a eficient˘ decˆt sortarea prin selectie. 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. 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(π). 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. 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. j < i} Pentru o permutare π corespunz˘toare unui ¸ir de sort˘ri. a Fie ci num˘rul de comparatii. a a De¸i ordinul de cre¸tere este tot N 2 . ¸ ˆ plus. spre deosebire de a ¸ s ¸ ¸ primele dou˘ metode de sortare. unde num˘rul de a s a a inversiuni este inv(π). Cea mai bun˘ metod˘ de sortare necesit˘ a a ¸ a a a n log2 n comparatii. ˆ cazul ˆ care tabloul este aproape ordonat. s . 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.

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

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

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

144 CAPITOLUL 10. ALGORITMI ELEMENTARI DE SORTARE .

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

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

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

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

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

for(int j=k. Elementele a ınl˘ ¸ a a listei sunt intr˘ri care permit accesul la tabloul de nume ¸i numere de telefon. for(int i=n. } return a. for(Lista b=a. } k=a. int k.urmator) { if (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˘. } Exemplu 2.continut])) return telefon[a. int val) { int i = h(x). } . ınl˘ ¸ a s a a a a a Lista al[] = new Lista[N1].equals(nume[a. ++j) a=elimina(j*k. Dac˘ h este bine aleas˘. 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˘. c˘utarea este rapid˘.a).a). a a ¸ a a num˘rul de coliziuni devine important..continut. dac˘ functia h este r˘u aleas˘. al[i] = adauga (x. prin ad˘ugarea numerelor de la 2 la n ˆ a a ıncepˆnd cu n ¸i terminˆnd cu a s a 2. Tot astfel. static void insereaza (String x. k*k<=n. LISTE prima faz˘. a = a.150 CAPITOLUL 11. Astfel numerele vor fi ˆ list˘ ˆ ordine cresc˘toare.continut. --i) { a=adauga(i.continut]. j<=n/k. } return -1. Vom utiliza metoda clasic˘ a ın a ın a a ciurului lui Eratostene: consider˘m succesiv elementele listei ˆ ordine cresc˘toare a ın a ¸i suprim˘m toti multiplii lor.urmator) { k=b. i>=2. Aceasta se realizeaz˘ prin procedura urm˘toare: s a ¸ a a static Lista Eratostene (int n) { Lista a=null. al[i]).n − 1] se construie¸te s o list˘ simplu ˆ antuit˘ format˘ din toate cheile care au h(x) = i. Pentru fiecare intrare i din intervalul [0. b=b. a != null. } static int cauta (String x) { for (int a = al[h(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. sau la cas˘ ˆ ¸ a a ıntr-un magazin. nodurile unui graf. etc. a as a a • adauga este o aplicatie definit˘ pe E × Coada(E) cu valori ˆ Coada(E). adauga. C)) = f alse • Pentru coada C0 – elimina(adauga(x. valoare. nevid˘. de exemplu procesele ın s a de a¸teptare a resurselor unui sistem. 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. ˆ englez˘ se spune a as In a strategie FIFO (First In First Out). elimina(C)) – valoare(adauga(x. elementul aflat ˆ cap. ¸ a ın esteV ida(C) aste egal˘ cu true dac˘ ¸i numai dac˘ coada C este vid˘. a Mai formalizat. COZI 151 11. de exemplu la gar˘ pentru a lua bilete. Operatiile asupra cozilor satisfac urm˘toarele relatii: ¸ a ¸ • Pentru C = C0 – elimina(adauga(x. elimina: ¸ • esteV ida este o aplicatie definit˘ pe Coada(E) cu valori ˆ {true. a ın • elimina este o aplicatie definit˘ pe Coada(E)−C0 cu valori ˆ Coada(E) care ¸ a ın asociaz˘ unei cozi nevide C o coad˘ obtinut˘ plecˆnd de la C ¸i eliminˆnd a a ¸ a a s a primul element. f alse}. . C0 )) = C0 – valoare(adauga(x. iar operatiile asupra cozilor sunt: esteV ida.2 Cozi Cozile sunt liste liniare particulare utilizate ˆ programare pentru gestionarea ın obiectelor care sunt ˆ a¸teptarea unei prelucr˘ri ulerioare.2. Elementele sunt s ad˘ugate sistematic la coad˘ ¸i sunt eliminate din capul listei. multimea cozilor cu a ¸ ¸ elemente din E este notat˘ Coada(E). consider˘m o multime de elemente E. C)) = valoare(C) • Pentru toate cozile C – esteV ida(adauga(x. ˆ opozitie cu strategia LIFO (Last In Firs ın ¸ Out) utilizat˘ la stive.11. coada vid˘ (care nu contine nici un element) a a ¸ este C0 . ¸ a ın adauga(x. C)) = adauga(x. as • valoare este o aplicatie definit˘ pe Coada(E)−C0 cu valori ˆ E care asociaz˘ ¸ a ın a unei cozi C.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. a Un grup de puncte poate contine ˆ ¸ ıntre 1 ¸i N puncte inclusiv.ˆ ıntre punctele i1 ¸i j1 exist˘ o cale de acces de prioritate p1 s a i2 j2 p2 .242 CAPITOLUL 14.num˘rul de c˘i de acces ˆ a a ıntrerupte de calamitate k1 h1 .gravitatea maxim˘ a C . Date de intrare Fi¸ierul de intrare URGENTA. Autorit˘¸ile estimeaz˘ gravitatea unei calamit˘¸i ca fiind suma priorit˘¸ilor at a at at c˘ilor de acces distruse de aceasta ¸i doresc s˘ determine un scenariu de gravitate a s a maxim˘. iM jM pM . METODA OPTIMULUI LOCAL .OUT va avea urm˘torul format: s s a gravmax . a a ¸ a Exemplu . programul va determina una singur˘. ˆ 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 .ˆ ıntre punctele kC ¸i hC a fost ˆ s ıntrerupt˘ calea de acces a Restrictii ¸i preciz˘ri ¸ s a 0 < N < 256 N − 2 < M < 32385 0<K <N +1 Priorit˘¸ile c˘ilor de acces sunt ˆ at a ıntregi strict pozitivi mai mici decˆt 256.ˆ ıntre punctele iM ¸i jM exist˘ o cale de acces de prioritate pM s a Date de ie¸ire s Fi¸ierul de ie¸ire URGENTA. ˆ at ın ¸ ¸a Intre oricare dou˘ puncte de interes strategic a exist˘ cel mult o cale de acces ce poate fi parcurs˘ ˆ ambele sensuri ¸i cel putin a a ın s ¸ un drum (format din una sau mai multe c˘i de acces) ce le conecteaz˘.ˆ ıntre punctele k2 ¸i h2 a fost ˆ s ıntrerupt˘ calea de acces a ..IN are urm˘torul format: s a N M K i1 j1 p1 . a a ˆ cazul unei calamit˘¸i unele c˘i de acces pot fi temporar ˆ In at a ıntrerupte ¸i astfel s ˆ ıntre anumite puncte de interes nu mai exist˘ leg˘tur˘.ˆ ıntre punctele k1 ¸i h1 a fost ˆ s ıntrerupt˘ calea de acces a k2 h2 .ˆ ıntre punctele i2 ¸i j2 exist˘ o cale de acces de prioritate p2 s a .. s Dac˘ exist˘ mai multe solutii.GREEDY priorit˘¸i ˆ functie de important˘.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

METODA OPTIMULUI LOCAL .GREEDY .268 CAPITOLUL 14.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}// drum(..out. }//dfs // listele de adiacenta . afisv(f). color[u]=GRAY. f[u]=++t. System. for(i=1. } color[u]=BLACK. System.print("drum : ").v++) if(a[u][v]==1) if(color[v]==WHITE) { p[v]=u. for(v=1... drum(nodd). System. System. METODA BACKTRACKING t=0. System.println().) static void afisv(int[] x) { int i. System.312 CAPITOLUL 15.v<=n. dfs(nods).out. !!! // v in Ad(u) !!! static void drum(int u) // nod_sursa ---> nod_destinatie { if(p[u]!=-1) drum(p[u]). }//main static void dfs(int u) { int v.i++) System.print(u+" ").out. afisv(d).print("Finalizat :\t")..out.print("Descoperit :\t").out. d[u]=++t.i<=n.out. } }//class /* 6 7 3 4 1 4 4 6 6 1 drum : 3 1 4 Descoperit : Finalizat : 2 11 5 6 1 12 3 10 4 7 8 9 . dfs(v).out.println().print(x[i]+"\t").

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

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

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

v<=n. . ctc[u]=nctc. METODA BACKTRACKING for(v=1. //"at" //if((a[lista[j]][u]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]). v .316 CAPITOLUL 15. color[u]=GRAY. DFS pentru calcul f[u].13 Sortare topologic˘ a Folosind parcurgerea ˆ adˆncime ın a // // // // // // // // Sortare Topologica = ordonare lineara a varfurilor (in digraf) (u.*. color[u]=BLACK. lista[pozLista--]=u.io.v++) if((a[u][v]==1)&&(color[v]==WHITE)) dfsa(v).. for(j=1.j++) if((at[u][lista[j]]==1)&&(color[lista[j]]==WHITE)) dfsat(lista[j]). u .3. } }//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." in lista ("u se termina inaintea lui v") Algoritm: 1.j<=n. u=nod 2. //"a" color[u]=BLACK.. f[u]=++t..v)=arc ==> ". } static void dfsat(int u) // se poate folosi "a" inversand arcele ! { int j.... 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. f=new int[n+1]. d=new int[n+1].nval. System. BLACK=2.m. PROBLEME REZOLVATE 317 class SortTopoDFS { static final int WHITE=0.nextToken(). // culoare static int[] lista. color=new int[n+1].nods. a=new int[n+1][n+1].i<=n.i++) color[i]=WHITE. for(nods=1.i<=n.out.k. .print(lista[i]+" "). }//main static void dfs(int u) { int v. st. t=0.k<=m. // matricea de adiacenta public static void main(String[] args) throws IOException { int i. st. } for(i=1.nods<=n.println().pozl.k++) { st.j. lista=new int[n+1]. // finalizat static int[] color. d[u]=++t. // lista static int[][] a.nval. i=(int)st. pozl=n.t.i++) System. a[i][j]=1.nods++) if(color[nods]==WHITE) dfs(nods). muchii. n=(int)st. GRAY=1. time. j=(int)st. st. color[u]=GRAY.nval.in"))).nval. // descoperit static int[] f.15. static int n. m=(int)st. // varfuri.nextToken(). for(i=1.nextToken().nextToken(). // nods=nod_start_DFS StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("sortTopo. pozitie in lista static int[] d. for(k=1.out.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

342 CAPITOLUL 15. METODA BACKTRACKING .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

428 ˘ CAPITOLUL 16. PROGRAMARE DINAMICA .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

POTRIVIREA SIRURILOR ¸ .444 CAPITOLUL 17.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

492 ˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ .

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

494 CAPITOLUL 19. TEORIA JOCURILOR .

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful