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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3.4. LIMBAJ ALGORITMIC

23

3.4

Limbaj algoritmic
ˆ continuare prezent˘m un exemplu de limbaj algoritmic. In a

3.4.1

Declararea datelor

Datele simple se declar˘ sub forma: a <tip> <nume>; unde <tip> poate lua una dintre valorile: byte, short, int, long, float, double, boolean, char. Tablourile unidimensionale se declar˘ sub forma: a <tip> <nume> [n1 ..n2 ]; Elementele vectorului pot fi accesate cu ajutorul unui indice, care poate lua valori ˆ ıntre n1 ¸i n2 , sub forma: s <nume>[i] unde i poate lua orice valoare ˆ ıntre n1 ¸i n2 . s ˆ cazul tablourilor bidimensionale, o declaratie de forma: In ¸ <tip> <nume>[m1 ..m2 ][n1 ..n2 ]; specific˘ o matrice cu m2 − m1 + 1 linii ¸i n2 − n1 + 1 coloane. Fiecare element se a s specific˘ prin doi indici: a <nume>[i][j] unde i reprezint˘ indicele liniei ¸i poate avea orice valoare ˆ a s ıntre m1 ¸i m2 iar j s reprezint˘ indicele coloanei ¸i poate avea orice valoare ˆ a s ıntre n1 ¸i n2 . s

3.4.2

Operatii de intrare/ie¸ire ¸ s

Preluarea valorilor pentru datele de intrare este descris˘ sub forma: a read v1 , v2 , ...; unde v1 , v2 , ... sunt nume de variabile. Afi¸area rezultatelor este descris˘ sub forma: s a write e1 , e2 , ...;

24

CAPITOLUL 3. ALGORITMI

unde e1 , e2 , ... sunt expresii (ˆ particular pot fi constante sau variabile). ın Operatia de atribuire. Operatia de atribuire a unei valori c˘tre o variabil˘ ¸ ¸ a a se descrie prin: v = <expresie>; unde v este un nume de variabil˘, <expresie> desemneaz˘ o expresie aritmetic˘ a a a sau logic˘, iar ”=” este operatorul de atribuire. Pentru acesta din urm˘ pot fi a a folosite ¸i alte simboluri, ca de exemplu ”:=” sau ”←”. Expresiile pot fi descrise s conform regulilor utilizate ˆ matematic˘. ın a

3.4.3

Prelucr˘ri liniare a

O secvent˘ de prelucr˘ri se descrie ˆ modul urm˘tor: ¸a a ın a <prel 1>; <prel 2>; ... <prel n>; sau <prel 1>; <prel 2>; ... <prel n>; O astfel de scriere indic˘ faptul c˘ ˆ momentul executiei prelucr˘rile se a a ın ¸ a efectueaz˘ ˆ ordinea ˆ care sunt specificate. a ın ın

3.4.4

Prelucr˘ri alternative a

O prelucrare alternativ˘ complet˘ (cu dou˘ ramuri) este descris˘ prin: a a a a if <conditie> ¸ sau sub forma if <conditie> then <prel 1> else <prel 2>; ¸ unde <conditie> este o expresie relational˘. Aceast˘ prelucrare trebuie ˆ ¸eleas˘ ¸ ¸ a a ınt a ˆ modul urm˘tor: dac˘ conditia este adev˘rat˘ atunci se efectueaz˘ prelucrarea ın a a ¸ a a a <prel 1>, altfel se efectueaz˘ <prel 2>. a O prelucrare alternativ˘ cu o singur˘ ramur˘ se descrie prin: a a a if <conditie> <prel>; ¸ sau if <conditie> then <prel>; ¸ iar executia ei are urm˘torul efect: dac˘ conditia este satisfacut˘ atunci se efectueaz˘ ¸ a a ¸ a a prelucrarea specificat˘, altfel nu se efectueaz˘ nici o prelucrare ci se trece la a a urm˘toarea prelucrare a algoritmului. a <prel 1> else <prel 2>;

3.4. LIMBAJ ALGORITMIC

25

3.4.5

Prelucr˘ri repetitive a

Prelucr˘rile repetitive pot fi de trei tipuri: a • cu test initial, ¸ • cu test final ¸i s • cu contor. Prelucrarea repetitiv˘ cu test initial se descrie prin: Prelucrarea repetitiv˘ cu a ¸ a test initial se descrie prin: ¸ while <conditie> <prel>; ¸ sau while <conditie> do <prel>; ¸ ˆ momentul executiei, atˆt timp cˆt conditia este adevarat˘, se va executa In ¸ a a ¸ a instructiunea. Dac˘ conditia nu este la ˆ ¸ a ¸ ınceput satisf˘cut˘, atunci instructiunea a a ¸ nu se efectueaz˘ niciodat˘. a a Prelucrarea repetitiv˘ cu test final se descrie prin: a do <prel> while <conditie>; ¸ Prelucrarea se repet˘ pˆn˘ cˆnd conditia specificat˘ devine fals˘. ˆ acest caz a a a a ¸ a a In prelucrarea se efectueaz˘ cel putin o dat˘, chiar dac˘ conditia nu este satisfacut˘ a ¸ a a ¸ a la ˆ ınceput. Prelucrarea repetitiv˘ cu contor se caracterizeaz˘ prin repetarea prelucr˘rii a a a de un num˘r prestabilit de ori ¸i este descris˘ prin: a s a for i = i1 , i2 , ..., in <prel>; sau for i = i1 , i2 , ..., in do <prel>; unde i este variabila contor care ia, pe rˆnd, valorile i1 , i2 , ..., in ˆ aceast˘ a ın a ordine, prelucrarea fiind efectuat˘ pentru fiecare valoare a contorului. a Alte forme utilizate sunt: for i = vi to vf do <prel>;

ˆ care contorul ia valori consecutive cresc˘toare ˆ ın a ıntre vi ¸i vf , ¸i s s for i = vi downto vf do <prel>;

ˆ care contorul ia valori consecutive descresc˘toare ˆ ın a ıntre vi ¸i vf . s

26

CAPITOLUL 3. ALGORITMI

3.4.6

Subalgoritm

ˆ cadrul unui algoritm poate s˘ apar˘ necesitatea de a specifica de mai In a a multe ori ¸i ˆ diferite locuri un grup de prelucr˘ri. Pentru a nu le descrie ˆ s ın a ın mod repetat ele pot constitui o unitate distinct˘, identificabil˘ printr-un nume, a a care este numit˘ subalgoritm. Ori de cˆte ori este necesar˘ efectuarea grupului a a a de prelucr˘ri din cadrul subalgoritmului se specific˘ numele acestuia ¸i, eventual, a a s datele curente asupra c˘rora se vor efectua prelucrarile. Aceast˘ actiune se nume¸te a a ¸ s apel al subalgoritmului, iar datele specificate al˘turi de numele acestuia ¸i asupra a s c˘rora se efectueaz˘ prelucrarile se numesc parametri. a a ˆ urma traducerii ˆ In ıntr-un limbaj de programare un subalgoritm devine un subprogram. Un subalgoritm poate fi descris ˆ felul urm˘tor: ın a <nume subalg> (<tip> <nume p1>, <tip> <nume p2>, ... ) { ... /* prelucr˘ri specifice subalgoritmului */ a ... return <nume rezultat>; } unde <nume subalg> reprezint˘ numele subalgoritmului iar nume p1, nume p2, a ... reprezint˘ numele parametrilor. Ultimul enunt, prin care se returneaz˘ rezultatul a ¸ a calculat ˆ cadrul subalgoritmului, este optional. ın Modul de apel depinde de modul ˆ care subalgoritmul returneaz˘ rezultatele ın a sale. Dac˘ subalgoritmul returneaz˘ efectiv un rezultat, printr-un enunt de forma a a ¸ return <nume rezultat>; atunci subalgoritmul se va apela ˆ felul urm˘tor: ın a v=<nume subalg>(nume p1, nume p2, ...); Ace¸ti subalgoritmi corespund subprogramelor de tip functie. s ¸ Dac˘ ˆ subalgoritm nu apare un astfel de enunt, atunci el se va apela prin: a ın ¸ <nume subalg>(nume p1, nume p2, ...); variant˘ care corespunde subprogramelor de tip procedur˘. a a Observatie. Prelucr˘rile care nu sunt detaliate ˆ cadrul algoritmului sunt ¸ a ın descrise ˆ limbaj natural sau limbaj matematic. Comentariile suplimentare vor fi ın cuprinse ˆ ıntre /* ¸i */. Dac˘ pe o linie a descrierii algoritmului apare simbolul // s a atunci tot ce urmeaz˘ dup˘ acest simbol, pe aceea¸i linie cu el, este interpretat ca a a s fiind un comentariu (deci, nu reprezint˘ o prelucrare a algoritmului). a

3.4. LIMBAJ ALGORITMIC

27

3.4.7

Probleme rezolvate

1. Algoritmului lui Euclid. Descrierea ˆ pseudocod a algoritmului lui Euclid este urm˘toarea: ın a int a, b, d, i, r; read a, b; if (a<b) { d=a; i=b; } else { d=b; i=a; }; r = d % i; while (r ! = 0) { d=i; i=r; r=d % i; }; write i; 2. Schema lui Horner. Descrierea ˆ pseudocod a schemei lui Horner este urm˘toarea: ın a int n, a, b, i; read n, a, b; int a[0..n], c[0..n-1]; for i=n,0,-1 read a[i]; c[n-1]=b*a[n]; for i=1,n-1 c[n-i-1]=b*c[n-i]+a[n-i]; val:=b*c[1]+a[1]; write val; 3. Conversia unui num˘r natural din baza 10 ˆ baza 2. a ın Fie n un num˘r ˆ a ıntreg pozitiv. Pentru a determina cifrele reprezentarii ˆ ın baza doi a acestui num˘r se poate folosi urm˘toarea metod˘: a a a Se ˆ ımparte n la 2, iar restul va reprezenta cifra de rang 0. Cˆtul obtinut la a ¸ ˆ ımpartirea anterioar˘ se ˆ a ımparte din nou la 2, iar restul obtinut va reprezenta cifra ¸ de ordin 1 ¸.a.m.d. Secventa de ˆ artiri continu˘ pˆ a la obtinerea unui cˆt nul. s ¸ ımp˘ ¸ a ın˘ ¸ a Descrierea ˆ pseudocod a acestui algoritm este: ın int n, d, c, r; read n; d = n; c = d / 2; /* cˆtul ˆ artirii ˆ a ımp˘ ¸ ıntregi a lui d la 2 */ r = d % 2; /* restul ˆ artirii ˆ ımp˘ ¸ ıntregi a lui d la 2 */ write r; while (c != 0) { d = c; c = d / 2; /* cˆtul ˆ artirii ˆ a ımp˘ ¸ ıntregi a lui d la 2 */ r = d % 2; /* restul ˆ artirii ˆ ımp˘ ¸ ıntregi a lui d la 2 */ write r; } 4. Conversia unui num˘r ˆ a ıntreg din baza 2 ˆ baza 10. ın

28

CAPITOLUL 3. ALGORITMI

Dac˘ bk bk−1 ...b1 b0 reprezint˘ cifrele num˘rului ˆ baza 2, atunci valoarea ˆ a a a ın ın baza 10 se obtine efectuˆ calculul: ¸ ınd (bk bk−1 ...b1 b0 )10 = bk 2k + bk−1 2k−1 + ... + b1 2 + b0 De¸i calculul de mai sus este similar cu evaluarea pentru X = 2 a polinomului s P [X] = bk X k + bk−1 X k−1 + ... + b1 X + b0 prelucrare pentru care ar putea fi folosit algoritmul corespunz˘tor schemei lui a Horner, ˆ continuare prezent˘m o alt˘ variant˘ de rezolvare a acestei probleme, ın a a a care folose¸te un subalgoritm pentru calculul puterilor unui num˘r ˆ s a ıntreg: int k, i, s; putere(int a, int n) read k; { int b[0..k]; int i, p; read b; p = 1; s = 0; for i=2,n p = p*a; for i=0,k s = s+b[i] * putere(2,i); return p; write s; } 5. S˘ se scrie un algoritm pentru determinarea tuturor divizorilor naturali ai a unui num˘r ˆ a ıntreg. Rezolvare. Fie n num˘rul ai c˘rui divizori trebuie determinati. Evident 1 ¸i a a ¸ s |n| sunt divizori ai lui n. Pentru a determina restul divizorilor este suficient ca ace¸tia s˘ fie c˘utati printre elementele multimii {2, 3, ..., [|n|]} cu [x] desemnˆnd s a a ¸ ¸ a partea ˆ ıntreag˘ a lui x. a Algoritmul poate descris ˆ modul urm˘tor: ın a int n, d; read n; write 1; /* afi¸area primului divizor */ s for d = 2, [|n|/2] if (d divide pe n) then write d; write |n| /* afi¸area ultimului divizor */ s 6. S˘ se scrie un algoritm pentru determinarea celui mai mare element dintra un ¸ir de numere reale. s Rezolvare. Fie x1 , x2 , ..., xn ¸irul analizat. Determinarea celui mai mare eles ment const˘ ˆ initializarea unei variabile de lucru max (care va contine valoarea a ın ¸ ¸ maximului) cu x1 ¸i compararea acesteia cu fiecare dintre celelalte elemente ale s ¸irului. Dac˘ valoarea curent˘ a ¸irului, xk , este mai mare decˆt valoarea variaas a a s a bilei max atunci acesteia din urm˘ i se va da valoarea xk . Astfel, dup˘ a k − 1 a a comparatie variabila max va contine valoarea maxim˘ din sub¸irul x1 , x2 , ..., xk . ¸ ¸ a s Algoritmul poate fi descris ˆ modul urm˘tor: ın a

3.4. LIMBAJ ALGORITMIC int k, n; read n; double x[1..n], max; /* vectorul ¸i variabila de lucru */ s read x; /* preluarea elementelor ¸irului */ s max = x[1]; for k = 2, n if (max < x[k]) then max = x[k]; write max; 7. S˘ se aproximeze, cu precizia ε, limita ¸irului a s
n

29

sn =
k=0

1 . k!

Rezolvare. Calculul aproximativ (cu precizia ε) al limitei ¸irului sn const˘ ˆ s a ın 1 calculul sumei finite sk , unde ultimul termen al sumei, tk = k! , are proprietatea tk a ¸ a tk < ε. ˆ Intrucˆt tk+1 = k+1 , aceast˘ relatie va fi folosit˘ pentru calculul valorii a termenului curent (permitˆnd mic¸orarea num˘rului de calcule). ¸a s a double eps, t, s; int k; k=1; /* initializare indice */ ¸ t=1; /* initializare termen */ ¸ s=1; /* initializare suma */ ¸ do { s=s+t; /* ad˘ugarea termenului curent */ a k=k+1; t=t/k; /* calculul urm˘torului termen */ a } while (t ≥ eps); s=s+t; (* ad˘ugarea ultimului termen *) a write s; 8. Fie A o matrice cu m linii ¸i n coloane, iar B o matrice cu n linii ¸i p coloane, s s ambele avˆnd elemente reale. S˘ se determine matricea produs C = A × B. a a Rezolvare. Matricea C va avea m linii ¸i p coloane, iar fiecare element se s determin˘ efectuˆnd suma: a a
n

ci,j =
k=1

ai,k · bk,j ,

1 ≤ i ≤ m, 1 ≤ j ≤ p.

ˆ felul acesta calculul elementelor matricei C se efectueaz˘ prin trei cicluri In a imbricate (unul pentru parcurgerea liniilor matricei C, unul pentru parcurgerea coloanelor matricei C, iar unul pentru efectuarea sumei specificate mai sus).

30

CAPITOLUL 3. ALGORITMI int m, n, p; /* dimensiunile matricelor */ read m, n, p; double a[1..m][1..n], b[1..n][1..p], c[1..m][1..p]; int i, j, k; /* indici */ read a; /* citirea matricei a */ read b; /* citirea matricei b */ for i=1,m for j=1,p { c[i,j]=0; for k=1,n c[i][j]=c[i][j]+a[i][k]*b[k][j]; } write c;

/* matrice */

3.4.8

Probleme propuse

1. Fie D o dreapt˘ de ecuatie ax+by+c = 0 ¸i (C) un cerc de centru O(x0 , y0 ) a ¸ s ¸i raz˘ r. S˘ se stabileasc˘ pozitia dreptei fat˘ de cerc. s a a a ¸ ¸a Indicatie. Se calculeaz˘ distanta de la centrul cercului la dreapta D utilizˆnd ¸ a ¸ a formula: |ax0 + by0 + c| √ . d= a2 + b2 Dac˘ d ≥ r+ε atunci dreapta este exterioar˘ cercului, dac˘ d ≤ r−ε atunci dreapta a a a este secant˘, iar dac˘ r − ε < d < r + ε atunci este tangent˘ (la implementarea a a a egalitatea ˆ ıntre dou˘ numere reale ...). a 2. S˘ se genereze primele n elemente ale ¸irurilor ak ¸i bk date prin relatiile a s s ¸ de recurent˘: ¸a ak+1 = 5ak + 3 , ak + 3 bk = ak + 3 , ak + 1 k ≥ 0, a0 = 1.

3. S˘ se determine r˘d˘cina p˘trat˘ a unui num˘r real pozitiv a cu precizia a a a a a u ε = 0.001, folosind relatia de recurent˘: ¸ ¸a xn+1 = 1 2 xn + a xn , x1 = a.

Precizia se consider˘ atins˘ cˆnd |xn+1 − xn | < ε. a a a 4. Fie A o matrice p˘tratic˘ de dimensiune n. S˘ se transforme matricea A, a a a prin interrschimb˘ri de linii ¸i de coloane, astfel ˆ at elementele de pe diagonala a s ıncˆ principal˘ s˘ fie ordonate cresc˘tor. a a a

3.4. LIMBAJ ALGORITMIC

31

5. S˘ se determine cel mai mare divizor comun al unui ¸ir de numere ˆ a s ıntregi. 6. S˘ se calculeze coeficientii polinomului a ¸ P [X] = (aX + b)n , a, b ∈ Z, n ∈ N.

7. Fie A o matrice p˘tratic˘. S˘ se calculeze suma elementelor din fiecare zon˘ a a a a (diagonala principal˘, diagonala secundar˘, etc.) marcat˘ ˆ figura urm˘toare: a a a ın a

a.

b.

c.

Figura 3.1: Zone ˆ matrice p˘tratic˘ ın a a

8. Fie x1 , x2 , ..., xn ∈ Z r˘d˘cinile unui polinom cu coeficienti ˆ a a ¸ ıntregi: P [X] = an X n + an−1 X n−1 + ... + a1 X + a0 . S˘ se determine coeficientii polinomului. a ¸ 9. S˘ se determine toate r˘d˘cinile rationale ale polinomului P [X] care are a a a ¸ coeficienti ˆ ¸ ıntregi. 140. Fie [P1 , P2 , ..., Pn ] un poligon convex dat prin coordonatele carteziene ale vˆrfurilor sale (ˆ ordine trigonometric˘). S˘ se calculeze aria poligonului. a ın a a 11. Fie f : [a, b] → R o functie continu˘ cu proprietatea c˘ exist˘ un unic ξ ∈ ¸ a a a (a, b) care are proprietatea c˘ f (ξ) = 0. S˘ se aproximeze ξ cu precizia ε = 0.001 a a utilizˆnd metoda bisectiei. a ¸ 12. Fie P ¸i Q polinoame cu coeficienti ˆ s ¸ ıntregi. S˘ se determine toate r˘d˘cinile a a a rationale comune celor dou˘ polinoame. ¸ a 13. S˘ se determine toate numerele prime cu maxim 6 cifre care r˘mˆn prime a a a ¸i dup˘ ”r˘sturnarea” lor (r˘sturnatul num˘rului abcdef este f edcba). s a a a a

32

CAPITOLUL 3. ALGORITMI

3.5

Instructiuni corespondente limbajului algorit¸ mic

3.5.1

Declararea datelor

Datele simple se declar˘ sub forma: a <tip> <nume>; sau <tip> <nume>= literal; unde <tip> poate lua una dintre urm˘toarele valori: byte, short, int, long, float, a double, boolean, char. ˆ exemplul urm˘tor sunt prezentate cˆteva modalit˘¸i In a a at de declarare pentru diferite tipuri de variabile. class Literali { public static void main(String args[]) { long l1 = 5L; long l2 = 12l; int i1hexa = 0x1; int i2hexa = 0X1aF; int i3octal = 01; long i4octal = 012L; long i5LongHexa = 0xAL; float f1 = 5.40F; float f2 = 5.40f; float f3 = 5.40e2f; float f4 = 5.40e+12f; float f5 = 5.40; // da eroare, trebuie cast double d1 = 5.40; // implicit este double ! double d2 = 5.40d; double d3 = 5.40D; double d4 = 5.40e2; double d5 = 5.40e+12d; char c1 = ’r’; char c2 = ’\u4567’; } }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

126 CAPITOLUL 8. ALGORITMI COMBINATORIALI .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ALGORITMI ELEMENTARI DE SORTARE .144 CAPITOLUL 10.

145 . ¸ a independent de natura elementelor sale.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. a class Lista { int continut. 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. numit ¸i pointer. etc. obiecte informatice complexe. prin ad˘ug˘ri sau ¸tergeri de elemente pe parcursul ¸ a a s prelucr˘rii. ¸iruri de caractere. Listele sunt obiecte dinamice. Elementele acestui ansamblu pot fi numere ˆ ıntregi sau reale. Lista urmator. Mai precis. 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. 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

METODA OPTIMULUI LOCAL .GREEDY .268 CAPITOLUL 14.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

public static void main(String[] args) throws IOException { int i.close().out.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. n=(int)st. out. }//main static boolean econex(int nodscos) { . static int [] cc.3. static int[][] a. } }//class /* 6 4 6 3 1 2 3 4 5 6 */ 1 2 5 6 3 4 15. a=new int[n+1][n+1].out"))).nextToken().nextToken(). st.i<=n.k.io. } for(i=1.nval. st.k++) { st.nextToken().print(i+" "). for(k=1.k<=m. a[i][j]=a[j][i]=1. st.j.m. StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("noduriSeparare.nval. j=(int)st. i=(int)st.i++) if(!econex(i)) System.nval. METODA BACKTRACKING if(color[v]==WHITE) if(a[u][v]==1) gi[v]--.320 CAPITOLUL 15. class NoduriSeparare { static int n.*.nextToken(). m=(int)st.in"))).nval.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

342 CAPITOLUL 15. METODA BACKTRACKING .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

16...println(). System. else c[v][s]=c[v-1][s]+c[v-1][s-v].2.println(c[S][S]). PROBLEME REZOLVATE 415 for(v=1..j. } out.j<=S.out. while((n>0)&&(S>0)) { v=0. //afism(). while(c[v][S]<n) v++. t2=System. for(i=0.nanoTime().j++) System.out. System. out. } }// afism() }// class /* 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 2 1 1 1 0 0 0 0 0 0 1 1 1 2 2 2 2 2 1 1 1 0 0 1 1 1 2 2 3 3 3 3 3 3 2 2 1 1 1 2 2 3 4 4 4 5 5 5 5 1 1 1 2 2 3 4 5 5 6 7 7 8 1 1 1 2 2 3 4 5 6 7 8 9 10 1 1 1 2 2 3 4 5 6 8 9 10 12 1 1 1 2 2 3 4 5 6 8 10 11 13 1 1 1 2 2 3 4 5 6 8 10 12 14 1 1 1 2 2 3 4 5 6 8 10 12 15 Press any key to continue. out.close().i<=S.s++) if(s<v) c[v][s]=c[v-1][s]. n=n-c[v-1][S].out.println("Timp = "+((double)(t2-t1))/1000000000).i++) { for(j=0.s<=S. }// main(.v++) for(s=0.print(v+" "). . S=S-v..v<=S.print(c[i][j]+" ").) static void afism() { int i.

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

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

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

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

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

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

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

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

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

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

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

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

PROGRAMARE DINAMICA .428 ˘ CAPITOLUL 16.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

POTRIVIREA SIRURILOR ¸ .444 CAPITOLUL 17.

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

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

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

!!! { . // infasuratoare convexa class Jarvis1 // pe frontiera coliniare ==> le iau pe toate .6 ˆ a¸ur˘toarea convex˘ Inf˘s a a ˆ Impachetarea Jarvis 18. ¸ ¸ a ın Dac˘ nu se afl˘ ˆ nici un poligon convex obtinut prin partitionarea poligonului a a ın ¸ ¸ concav atunci el nu se afl˘ ˆ interiorul acestuia.io. GEOMETRIE COMPUTATIONALA ¸ Poligonul concav se descompune ˆ poligoane convexe cu ajutorul diagoın nalelor interne ¸i se folose¸te un algoritm pentru poligoane convexe pentru fiecare s s poligon convex astfel obtinut. Dac˘ punctul este ˆ interiorul unui poligon convex ¸ a ın obtinut prin partitionarea poligonului concav atunci el se afl˘ ˆ interiorul acestuia.6.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...448 ˘ CAPITOLUL 18.*.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GEOMETRIE COMPUTATIONALA ¸ .492 ˘ CAPITOLUL 18.

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

494 CAPITOLUL 19. TEORIA JOCURILOR .

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

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

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

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

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

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

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

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

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

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

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

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

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