Documente Academic
Documente Profesional
Documente Cultură
9 decembrie 2020
Dedicaµie
1
to myself ...
in a time when ...
I will not be able ...
2
to be.
3
When I Die Nobody Will Remember Me
1
I Dedicate This Book to Myself By Carol Lynne
2
To be, or not to be ..., Hamlet, Act III, Scene I, William Shakespeare, 1564-1616
3
https://www.youtube.com/watch?v=eMtcDkSh7fU
ii
Prefaµ
Stilul acestor c rµi este ... ca ³i cum a³ vorbi cu nepoµii mei (³i chiar cu mine însumi!) ... încercând
împreun s g sim o rezolvare cât mai bun pentru o problem dat la olimpiad .
Ideea, de a scrie aceste culegeri de probleme date la olimpiadele de informatic , a ap rut acum
câµiva ani când am întrebat un student (care nu reu³ea s rezolve ni³te probleme foarte simple):
Ce te faci dac un elev, care ³tie c e³ti student ³i c studiezi ³i informatic , te roag , din când în
când, s -l ajuµi s rezolve câte o problem de informatic dat la gimnaziu la olimpiad , sau pur
³i simplu ca tem de cas , ³i tu, aproape de ecare dat , nu îl poµi ajuta? Eu cred c nu este prea
bine ³i poate c ... te faci ... de râs! Pe vremea mea (!), când eram elev de gimnaziu, un student
era ca un fel de ... zeu! Cu trecerea anilor am înµeles c nu este chiar a³a! i înc ceva: nu am
reu³it s înµeleg de ce, atunci când cineva nu poate s rezolve corect o problem de informatic
de clasa a 6-a, dat la olimpiada de informatic sau ca tem de cas , folose³te aceast scuz : eu
nu am f cut informatic în liceu! ³i acest cineva este zeul sau zeiµa despre care vorbeam!.
4
Sunt convins c este important s studiem cu atenµie cât mai multe probleme rezolvate! Cred
cred c sunt utile ³i primele versiuni în care sunt prezentate chiar ³i numai enunµurile ³i indicaµiile
"ociale" de rezolvare. Acestea se g sesc în multe locuri; aici încerc s le pun pe toate la un loc !
Limbajul de programare se alege în funcµie de problema pe care o avem de rezolvat. Cu ni³te
ani în urm alegerea era mai simpl : dac era o problem de calcul se alegea Fortran iar dac era
o problem de prelucrarea masiv a datelor atunci se alegea Cobol. Acum alegerea este ceva mai
5 6
dicil ! :-) Vezi, de exemplu, IOI2020 ³i IOI2019 , IOI2015 .
Cred c , de cele mai multe ori, este foarte greu s gândim "simplu" ³i s nu "ne complic m"
atunci când caut m o rezolvare pentru o problem dat la olimpiad . Acesta este motivul pentru
care vom analiza cu foarte mare atenµie atât exemplele date în enunµurile problemelor cât ³i
"restricµiile" care apar acolo (ele sigur "ascund" ceva interesant din punct de vedere al algoritmului
7
de rezolvare!) .
Am început câteva c rµi (pentru clasele de liceu) cu mai mulµi ani în urm , pentru perioada
2000-2007 ([29] - [33]), cu anii în ordine cresc toare!). A urmat o pauz de câµiva ani (destul de
mulµi!). Am observat c acele cursuri s-au împr ³tiat un pic pe net ([48] - [56])! Încerc acum
s ajung acolo unde am r mas ... plecând mereu din prezent ... pân când nu va mai posibil ...
a³a c , de aceast dat , anii sunt în ordine ... descresc toare! :-)
Codurile surs sunt cele ociale (publicate pe site-urile olimpiadelor) sau publicate pe alte
site-uri (dac mi s-a p rut c sunt utile ³i se poate înv µa câte ceva din ele).
Pentru liceu perioada acoperit este de azi (pân când va exista acest azi pentru mine!)
pân în anul 2000 (aveam deja perioada 2000-2007!).
Pentru gimnaziu perioada acoperit este de azi pân în anul 2010 (nu am prea mult timp
disponibil ³i, oricum, calculatoarele folosite la olimpiade înainte de 2010 erau ceva mai 'slabe' ³i
8
... restricµiile de memorie, din enunµurile problemelor, par 'ciudate' acum!). i indc a venit
vorba despre calculatoare mai slabe sau mai puternice: laptopul meu¯t u este puµin mai slab
decât cel mai puternic calculator din lume în 1985 dar ³i ... un pic mai puternic decât cel mai
9
puternic calculator din lume în 1983. (armaµia este valabil acum, în 2020).
4
Se poate observa din Coduri surs c orice problem are numeroase soluµii, atât ca algoritmi de rezolvare
cât ³i ca stil de programare! Studiind aceste coduri ... avem ce înv µa ... de³i uneori pare c 'se trage cu tunul' ...
5
IOI2019 ³i IOI2020 au a permis utilizarea limbajelor de programare C++ ³i Java
6
IOI2015 a permis utilizarea limbajelor de programare C++, Java, Pascal, Python ³i Rubi (...)
7
8
Vezi cele 5 secunde pentru Timp maxim de executare/test din problema avârcolaci - ONI2014 clasa a 11-a
Când eram eu elev/student un calculator obi³nuit executa în jur de 1.000.000 de operaµii pe secund , acum
execut 1.000.000.000 de operaµii pe secund , iar mai târziu ... cine stie ce va mai ?!
9
https://en.wikipedia.org/wiki/List_of_fastest_computers
iii
În perioada 2017-2020 cele mai puternice calculatoare din lume au fost: în noiembrie 2017 în
China, în noiembrie 2019 în SUA ³i ... în iunie 2020 în Japonia (Fugaku: 415 petaops, adic
15 10
415 10 operaµii pe secund , adic ... 415 milioane de ... miliarde de ... operaµii pe secund ).
11
O mic observaµie: în 2017 a fost prima ediµie a olimpiadei EJOI în Bulgaria ³i ... tot în
12
Bulgaria a fost ³i prima ediµie a olimpiadei IOI în 1989.
Dar ... prima ediµie a olimpiadei IMO (International Mathematical Olympiad) a fost în
13
România în 1959. Tot în România s-au µinut ediµiile din anii 1960, 1969, 1978, 1999 ³i 2018.
Revenind la ... culegerile noastre ... mai departe, probabil, va urma completarea unor
informaµii în Rezolv ri detaliate ... pentru unele probleme numai (tot din cauza lipsei timpului
necesar pentru toate!). Prioritate vor avea problemele de gimnaziu (nu pentru c sunt mai 'u³oare'
ci pentru c ... elevii de liceu se descurc ³i singuri!). Totu³i, vor prezentate ³i Rezolv ri
detaliate ale problemelor de liceu (pe care le-am considerat în mod subiectiv!) utile.
Îmi aduc aminte c exista o interesant vorb de duh printre programatorii din generaµia mea:
nu se trage cu tunul într-o musc . Sensul este: nu se scrie un cod complicat dac se poate
scrie un cod simplu ³i clar! Asta încerc eu în Rezolv ri detaliate .
Vom încerca, împreun , ³i câteva probleme de ... IOI ... dar asta este o treab ... nu prea
u³oar ! Cred totu³i c este mai bine s prezint numai enunµuri ale problemelor date la IOI în
ultimii câµiva ani! (asta a³a, ca s vedem cum sunt problemele la acest nivel!). Cei care ajung
acolo sau vor s ajung acolo (la IOI) nu au nevoie de ajutorul meu! Se descurc singuri! La
Indicaµii de rezolvare voi prezenta numai ... numele algoritmilor clasici folosiµi în rezolvare.
10
https://www.top500.org/lists/top500/
11
https://ejoi.org/about/
12
https://stats.ioinformatics.org/olympiads/
13
https://en.wikipedia.org/wiki/International_Mathematical_Olympiad
14
https://www.etsy.com/listing/604809336/john-wayne-quotes-i-am-only-responsible
"Acknowledgements"
15
"I want to thank God most of all because without God I wouldn't be able to do any of this."
Adrian R.
15
I.d.k.: "I don't know who the author is."
v
Despre autor16
nume: R bâea Aurel-Adrian, 18.03.1953 - ...
adresa: Str. Valea Ghinzii nr. 21 B, Bistriµa, România
telefon: +40 728 18 03 53 +40 363 10 25 10
email: adrian1803@gmail.com
Lector universitar - Universitatea Tehnic din Cluj Napoca - Centrul
Universitar Nord din Baia Mare, Facultatea de tiinµe, Str. Victoriei,
nr. 76, Baia Mare, România, (pensionat: 01.10.2018)
http://www.stiinte.utcluj.ro/
Discipline predate (1992-2018):
Algoritmi ³i structuri de date, Algoritmi în teoria opµiunilor nanciare, Bazele matematice
ale calculatoarelor, Bazele tehnologiei informaµiei, Birotic , Capitole speciale de inteligenµ
articial , Capitole speciale de teoria algoritmilor, Calcul paralel, Informatic economic ,
Instruire asistat de calculator, Limbaje de programare; Programare orientat pe obiecte,
Programare procedural , Structuri de date,
16
https://stiinte.utcluj.ro/files/cv/CV%20Rabaea_Adrian.pdf
17
http://opac.biblioteca.ase.ro/opac/bibliographic_view/149021
18
http://www.ionivan.ro/2015-PERSONALITATI/Dodescu.htm
19
https://sites.google.com/site/ciprianatudor/Home/professor-constantin-tudor
20
https://ro.wikipedia.org/wiki/Ion_V%C4%83duva
vi
Cuprins
Prefaµ iii
Cuprins vii
Lista gurilor xiii
Lista tabelelor xiv
Lista programelor xv
1 Structuri de date 1
1.1 Date ³i structuri de date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.2 Structuri de date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Structuri ³i tipuri de date abstracte . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 Structuri de date abstracte . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.2 Tipuri de date abstracte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Structuri de date elementare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.1 Liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.2 Stive ³i cozi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3.3 Grafuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3.4 Arbori binari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3.5 Heap-uri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3.6 Structuri de mulµimi disjuncte . . . . . . . . . . . . . . . . . . . . . . . . . 7
2 Algoritmi 8
2.1 Etape în rezolvarea problemelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2 Algoritmi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2.1 Ce este un algoritm? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2.2 Propriet µile algoritmilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2.3 Tipuri de prelucr ri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3 Descrierea algoritmilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.1 Limbaj natural . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3.2 Scheme logice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3.3 Pseudocod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4 Limbaj algoritmic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.1 Declararea datelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.2 Operaµii de intrare/ie³ire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.3 Prelucr ri liniare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.4 Prelucr ri alternative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4.5 Prelucr ri repetitive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4.6 Subalgoritm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4.7 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4.8 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.5 Instrucµiuni corespondente limbajului algoritmic . . . . . . . . . . . . . . . . . . . 18
2.5.1 Declararea datelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.5.2 Operaµii de intrare/ie³ire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.5.3 Prelucr ri liniare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.5.4 Prelucr ri alternative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.5.5 Prelucr ri repetitive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
vii
2.5.6 Subprograme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.5.7 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.5.8 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4 Recursivitate 46
4.1 Funcµii recursive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.1.1 Funcµii numerice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.1.2 Funcµia lui Ackerman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.1.3 Recursii imbricate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.2 Proceduri recursive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6 Algoritmi elementari 61
6.1 Operaµii cu numere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
6.1.1 Minim ³i maxim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
6.1.2 Divizori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
6.1.3 Numere prime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
6.2 Algoritmul lui Euclid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
6.2.1 Algoritmul clasic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
6.2.2 Algoritmul lui Euclid extins . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
6.3 Operaµii cu polinoame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
6.3.1 Adunarea a dou polinoame . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
6.3.2 Înmulµirea a dou polinoame . . . . . . . . . . . . . . . . . . . . . . . . . . 64
6.3.3 Calculul valorii unui polinom . . . . . . . . . . . . . . . . . . . . . . . . . . 64
6.3.4 Calculul derivatelor unui polinom . . . . . . . . . . . . . . . . . . . . . . . . 65
6.4 Operaµii cu mulµimi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
6.4.1 Apartenenµa la mulµime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
6.4.2 Diferenµa a dou mulµimi . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
6.4.3 Reuniunea ³i intersecµia a dou mulµimi . . . . . . . . . . . . . . . . . . . . 66
6.4.4 Produsul cartezian a dou mulµimi . . . . . . . . . . . . . . . . . . . . . . . 67
6.4.5 Generarea submulµimilor unei mulµimi . . . . . . . . . . . . . . . . . . . . . 67
6.5 Operaµii cu numere întregi mari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
6.5.1 Adunarea ³i sc derea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
6.5.2 Inmulµirea ³i împ rtirea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.5.3 Puterea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
6.6 Operaµii cu matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.6.1 Înmulµirea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.6.2 Inversa unei matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
7 Algoritmi combinatoriali 73
7.1 Principiul includerii ³i al excluderii ³i aplicaµii . . . . . . . . . . . . . . . . . . . . . 73
7.1.1 Principiul includerii ³i al excluderii . . . . . . . . . . . . . . . . . . . . . . . 73
7.1.2 Num rul funcµiilor surjective . . . . . . . . . . . . . . . . . . . . . . . . . . 73
7.1.3 Num rul permut rilor f r puncte xe . . . . . . . . . . . . . . . . . . . . . 75
7.2 Principiul cutiei lui Dirichlet ³i aplicaµii . . . . . . . . . . . . . . . . . . . . . . . . 75
7.2.1 Problema subsecvenµei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.2.2 Problema sub³irurilor strict monotone . . . . . . . . . . . . . . . . . . . . . 76
7.3 Numere remarcabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
7.3.1 Numerele lui Fibonacci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
7.3.2 Numerele lui Catalan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
7.4 Descompunerea în factori primi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
7.4.1 Funcµia lui Euler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
7.4.2 Num rul divizorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.4.3 Suma divizorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.5 Partiµia numerelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
7.5.1 Partiµia lui n în exact k termeni . . . . . . . . . . . . . . . . . . . . . . . . 82
7.5.2 Partiµia lui n în cel mult k termeni . . . . . . . . . . . . . . . . . . . . . . . 83
7.5.3 Partiµii multiplicative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7.6 Partiµia mulµimilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7.7 Probleme rezolvate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
8 Algoritmi de c utare 85
8.1 Problema c ut rii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
8.2 C utarea secvenµial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
8.3 C utare binar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
8.4 Inserare în tabel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
8.5 Dispersia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
10 Liste 97
10.1 Liste liniare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
10.2 Cozi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
10.3 Stive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
10.4 Evaluarea expresiilor aritmetice prexate . . . . . . . . . . . . . . . . . . . . . . . 105
10.5 Operaµii asupra listelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Glosar 326
Bibliograe 327
Lista autorilor 330
Lista gurilor
1.1 List liniar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Arbore binar plin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Arbore binar complet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4 Max-heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
xiii
Lista tabelelor
2.1 Tipurile primitive de date în Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
xiv
Lista programelor
2.5.1 DescFibo.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
15.2.1 distanµa (de editare) Levenshtein . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
15.2.2 distEdit.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
15.2.3 distEdit.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
xv
Capitolul 1
Structuri de date
1.1.1 Date
Datele sunt entit µi purt toare de informaµie. În informatic , o dat este un model de repre-
zentare a informaµiei, accesibil unui anumit procesor (om, unitate central , program), model cu
care se poate opera pentru a obµine noi informaµii despre fenomenele, procesele ³i obiectele lumii
reale. În functie de modul lor de organizare, datele pot : elementare (simple) sau structurate.
Datele elementare au caracter atomic, în sensul c nu pot descompuse în alte date mai simple.
Astfel de date sunt cele care iau ca valori numere sau ³iruri de caractere. O dat elementar apare
ca o entitate indivizibil atât din punct de vedere al informaµiei pe care o reprezint cât ³i din
punct de vedere al procesorului care o prelucreaz .
O dat elementar poate privit la nivel logic (la nivelul procesorului uman) sau la nivel zic
(la nivelul calculatorului).
Din punct de vedere logic, o dat poate denit ca un triplet de forma
Din punct de vedere zic, o dat poate denit ca o zon de memorie de o anumit lungime,
situat la o anumit adres absolut , în care sunt memorate în timp ³i într-o form specic
valorile datei.
Identicatorul este un simbol asociat datei pentru a o distinge de alte date ³i pentru a o putea
referi în cadrul programului.
Atributele sunt propriet µii ale datei ³i precizeaz modul în care aceasta va tratat în cadrul
procesului de prelucrare. Dintre atribute, cel mai important este atributul de tip care dene³tete
apartenenµa datei la o anumit clas de date.
O clas de date este denit de natura ³i domeniul valorilor datelor care fac parte din clasa
respectiv , de operaµiile specice care se pot efectua asupra datelor ³i de modelul de reprezentare
intern a datelor. Astfel, exist date de tip întreg, de tip real, de tip logic, de tip ³ir de caractere,
etc.
O mulµime de date care au acelea³i caracteristici se nume³te tip de date. Evident, un tip de date
este o clas de date cu acela³i mod de interpretare logic ³i reprezentare zic ³i se caracterizeaz
prin valorile pe care le pot lua datele ³i prin operaµiile care pot efectuate cu datele de tipul
respectiv.
De exemplu, tipul întreg se caracterizeaz prin faptul c datele care îi aparµin pot lua doar
valori întregi, ³i asupra lor pot efectuate operaµii aritmetice clasice (adunare, sc dere, înmulµire,
împ rµire în mulµimea numerelor întregi, comparaµii).
Se poate considera c datele organizate sub forma tablourilor unidimensionale formeaz tipul
vector iar datele organizate sub forma tablourilor bidimensionale formeaz tipul matrice.
În funcµie de natura elementelor care o compun, o structur de date poate :
1
CAPITOLUL 1. STRUCTURI DE DATE 2
Datele apar frecvent sub forma unor colecµii de date de diferite tipuri, menite s faciliteze
prelucrarea în cadrul rezolv rii unei anumite probleme concrete.
Datele structurate, numite uneori ³i structuri de date, sunt constituite din mai multe date
elementare (uneori de acela³i tip, alteori de tipuri diferite), grupate cu un anumit scop ³i dup
anumite reguli.
Exemple.
1. Un ³ir nit de numere reale a1 , a2 , ..., an poate reprezentat ca o dat structurat (tablou
unidimensional sau vector).
2. O matrice
Z
^ a1,1 a1,2 a1,n [
_
^
^ _
_
^
^ a2,1 a2,2 a2,n _
_
^
^ _
_
^
^ _
_
^
^ a _
_
\ m,1 am,1 am,n ]
poate reprezentat ca o dat structurat (tablou bidimensional) specicînd ecare element prin
doi indici (de linie ³i de coloan ).
O structur de date este deci o colecµie de date, eventual de tipuri diferite, pe care s-a denit
o anumit organizare ³i c reia îi este specic un anumit mod de identicare a elementelor compo-
nente. Componetele unei structuri de date pot identicate prin nume sau prin ordinea pe care
o ocup în cadrul structurii.
Dac accesul la o anumit component a structurii de date se poate face f r s µinem seama
de celelalte componente, vom spune c structura de date este cu acces direct. În schimb, dac
accesul la o component a structurii de date se poate face numai µinând cont de alte câmpuri ale
structurii (în conformitate cu ordinea structurii, printr-un proces de traversare) atunci vom spune
c structura este cu acces secvenµial.
Structurile de date pot create pentru a depozitate în memoria intern (aceste structuri de
date se numesc structuri interne) sau în memoria extern (se numesc structuri externe, sau ³iere).
Structurile interne au un caracter de date temporare (ele dispar odat cu încetarea activit µii de
prelucrare) iar cele externe au un caracter de date permanente (mai bine spus, de lung durat ).
Dac pe lâng componentele structurii se înregistreaz pe suport ³i alte date suplimentare
care s materializeze relaµia de ordonare, atunci structura de date respectiv este explicit , în caz
contrar este implicit . De exemplu, structura de date de tip tablou este o structur implicit de
date iar structura de date de tip list liniar este o structur explicit de date.
Asupra structurilor de date se pot efectua operaµii care se refer structura respectiv sau la
valorile datelor componente. Cele mai importante operaµii sunt:
CAPITOLUL 1. STRUCTURI DE DATE 3
abstractizarea procedural , care separ propriet µile logice ale unei acµiuni de detaliile imple-
ment rii acesteia
abstractizarea datelor, care separ propriet µile logice ale datelor de detaliile reprezent rii
lor
O structur de date abstracte are un singur exemplar (o singur instanµ ). Pentru a crea mai
multe exemplare ale structurii de date abstracte se dene³te un tip de date abstract. În Java, de
exemplu, clasa asigur un mod direct de denire a oric rui tip de date abstract.
1.3.1 Liste
O este o colecµie de elemente de informaµie (noduri) aranjate într-o anumit ordine. Lun-
list
gimea unei liste este num rul de noduri din list . Structura corespunzatoare de date trebuie s
ne permit s determin m ecient care este primul/ultimul nod în structur ³i care este predece-
sorul/succesorul unui nod dat (dac exist ). Iat cum arat cea mai simpl list , lista liniar :
capul coada
listei listei
O este o list în care, dup ultimul nod, urmeaz primul nod, deci ecare nod
list circular
are succesor³i predecesor.
Câteva dintre operaµiile care se efectueaz asupra listelor sunt: inserarea (ad ugarea) unui nod,
extragerea (³tergerea) unui nod, concatenarea unor liste, num rarea elementelor unei liste etc.
Implementarea unei liste se realizeaz în dou moduri: secvenµial ³i în nµuit.
Implementarea secvential se caracterizeaz prin plasarea nodurilor în locaµii succesive de me-
morie, în conformitate cu ordinea lor în list . Avantajele acestui mod de implementare sunt accesul
rapid la predecesorul/succesorul unui nod ³i g sirea rapid a primului/ultimului nod. Dezavanta-
jele sunt modalit µile relativ complicate de inserarea/³tergere a unui nod ³i faptul c , în general,
nu se folose³te întreaga memorie alocat listei.
Implementarea înl nµuit se caracterizeaz prin faptul c ecare nod conµine dou p rµi: in-
formaµia propriu-zis ³i adresa nodului succesor. Alocarea memoriei pentru ecare nod se poate
face în mod dinamic, în timpul rul rii programului. Accesul la un nod necesit parcurgerea tuturor
predecesorilor s i, ceea ce conduce la un consum mai mare de timp pentru aceast operaµie. În
schimb, operaµiile de inserare/³tergere sunt foarte rapide. Se consum exact atât spaµiu de memo-
rie cât este necesar dar, evident, apare un consum suplimentar de memorie pentru înregistrarea
leg turii c tre nodul succesor. Se pot folosi dou adrese în loc de una, astfel încât un nod s
conµin pe lang adresa nodului succesor ³i adresa nodului predecesor. Obµinem astfel o list
dublu inl nµuit , care poate traversat în ambele direcµii.
Listele înl nµuite pot reprezentate prin tablouri. În acest caz, adresele nodurilor sunt de fapt
indici ai tabloului.
O alternativ este s folosim dou tablouri val ³i next astfel: s memor m informaµia ecarui
nod i în locaµia vali, iar adresa nodului s u succesor în locaµia nexti. Indicele locaµiei primului
nod este memorat în variabila p. Vom conveni ca, pentru cazul listei vide, s avem p 0 ³i
nextu 0 unde u reprezint ultimul nod din list . Atunci, valp va conµine informaµia primului
nod al listei, nextp adresa celui de-al doilea nod, valnextp informaµia din al doilea nod,
nextnextp adresa celui de-al treilea nod, etc. Acest mod de reprezentare este simplu dar
apare problema gestion rii locaµiilor libere. O soluµie este s reprezent m locaµiile libere tot sub
forma unei liste înlanµuite. Atunci, ³tergerea unui nod din lista iniµial implic inserarea sa în
lista cu locaµii libere, iar inserarea unui nod în lista iniµial implic ³tergerea sa din lista cu locaµii
libere. Pentru implementarea listei de locaµii libere, putem folosi acelea³i tablouri dar avem nevoie
de o alt variabil , f reehead, care s conµin indicele primei locaµii libere din val ³i next. Folosim
acelea³i convenµii: dac f reehead 0 înseamn c nu mai avem locaµii libere, iar nextul 0
unde ul reprezint ultima locaµie liber .
Vom descrie in continuare dou tipuri de liste particulare foarte des folosite.
1.3.3 Grafuri
a1 , a2 , a2 , a3 , ..., an1 , an
sau de forma
ra1 , a2 x, ra2 , a3 x, ..., ran1 , an x
dup cum graful este orientat sau neorientat. Lungimea drumului este egal cu num rul muchiilor
care îl constituie. Un drum simplu este un drum în care nici un vârf nu se repet . Un ciclu este
un drum care este simplu, cu excepµia primului ³i ultimului vârf, care coincid. Un graf aciclic este
un graf f r cicluri.
Un graf neorientat este conex, dac între oricare dou vârfuri exist un drum. Pentru grafuri
orientate, aceast notiune este înt rit : un graf orientat este tare conex, dac între oricare dou
vârfuri i ³i j exist un drum de la i la j ³i un drum de la j la i.
Vârfurilor unui graf li se pot ata³a informaµii (numite valori), iar muchiilor li se pot ata³a
informaµii numite uneori lungimi sau costuri.
Exist cel puµin trei moduri de reprezentare ale unui graf:
a Printr-o matrice de adiacenµ A, în care Ai, j true dac vârfurile i ³i j sunt adiacente, iar
Ai, j f alse în caz contrar. O alt variant este s -i d m lui Ai, j valoarea lungimii muchiei
dintre vârfurile i ³i j , considerand Ai, j atunci când cele dou vârfuri nu sunt adiacente.
Cu aceast reprezentare, putem verica u³or dac dou vârfuri sunt adiacente. Pe de alt parte,
dac dorim s a m toate vârfurile adiacente unui vârf dat, trebuie s analiz m o întreag linie
din matrice. Aceasta necesit n operaµii (unde n este num rul de vârfuri în graf), independent de
num rul de muchii care conecteaz vârful respectiv.
a Prin liste de adiacenµ , adic prin ata³area la ecare vârf i a listei de vârfuri adiacente
(pentru grafuri orientate, este necesar ca muchia s plece din i). Într-un graf cu m muchii, suma
lungimilor listelor de adiacent este 2m, dac graful este neorientat, respectiv m, dac graful este
orientat. Dac num rul muchiilor în graf este mic, aceast reprezentare este preferabil din punct
de vedere al memoriei necesare. Totu³i, pentru a determina dac dou vârfuri i ³i j sunt adiacente,
trebuie s analiz m lista de adiacent a lui i (³i, posibil, lista de adiacent a lui j ), ceea ce este
mai puµin ecient decât consultarea unei valori logice în matricea de adiacenµ .
a Printr-o list de muchii. Aceast reprezentare este ecient atunci când avem de examinat
toate muchiile grafului.
Un arbore este un graf neorientat, aciclic ³i conex. Sau, echivalent, un arbore este un graf
neorientat în care exist exact un drum între oricare dou vârfuri.
Un arbore reprezentat pe niveluri se nume³te arbore cu r d cin . Vârful plasat pe nivelul 0
se nume³te r d cina arborelui. Pe ecare nivel i % 0 sunt plasate vârfurile pentru care lungimea
drumurilor care le leag de r d cin este i.
Vârfurile de pe un nivel i % 0 legate de acela³i vârf j de pe nivelul i 1 se numesc descendenµii
direcµi (ii) vârfului j iar vârful j se nume³te ascendent direct (tat ) al acestor vârfuri.
Dac exist un drum de la un vârf i de pe nivelul ni la un vârf j de pe nivelul nj % ni, atunci
vârful i se nume³te ascendent al lui j , iar vârful j se nume³te descendent al lui i.
Un vârf terminal (sau frunz ) este un vârf f r descendenµi. Vârfurile care nu sunt terminale
se numesc neterminale.
Un arbore în care orice vârf are cel mult doi descendenµi se nume³te arbore binar.
Într-un arbore cu r d cin (reprezentat pe niveluri), adâncimea unui vârf este lungimea dru-
mului dintre r d cin ³i acest vârf iar în lµimea unui vârf este lungimea celui mai lung drum dintre
acest vârf ³i un vârf terminal.
În lµimea arborelui este în lµimea r d cinii.
CAPITOLUL 1. STRUCTURI DE DATE 6
k
Într-un arbore binar, num rul maxim de vârfuri aate pe nivelul k este 2 . Un arbore binar
k1 k1
de în lµime k are cel mult 2 1 vârfuri, iar dac are exact 2 1 vârfuri, se nume³te arbore
plin.
Varfurile unui arbore plin se numeroteaza în ordinea nivelurilor. Pentru acela³i nivel, numero-
tarea se face în arbore de la stânga la dreapta.
1 nivelul 0
2 3 nivelul 1
4 5 6 7 nivelul 2
8 9 10 11 12 13 14 15 nivelul 3
Un arbore binar cu n vârfuri ³i de în lµime k este complet, dac se obµine din arborele binar
plin de în lµime k , prin eliminarea, dac este cazul, a vârfurilor numerotate cu n 1, n 2, ...,
k1
2 1.
Acest tip de arbore se poate reprezenta secvenµial folosind un tablou T , punând vârfurile de
k k k1
adâncime k , de la stânga la dreapta, în poziµiile T 2 , T 2 1, ..., T 2 1 (cu posibila
excepµie a ultimului nivel care poate incomplet).
1 nivelul 0
2 3 nivelul 1
4 5 6 7 nivelul 2
8 9 10 11 12 nivelul 3
Tat l unui vârf reprezentat în T i, i % 0, se a în T i©2. Fiii unui vârf reprezentat în T i
se a , dac exist , în T 2i ³i T 2i 1.
1.3.5 Heap-uri
11 7 10 7 7 9 2 4 6 5 7 3
Caracteristica de baz a acestei structuri de date este c modicarea valorii unui vârf se face
foarte ecient, p strându-se proprietatea de heap.
De exemplu, într-un max-heap, dac valoarea unui vârf cre³te, astfel încât dep ³e³te valoarea
tat lui, este sucient s schimb m între ele aceste dou valori ³i s continu m procedeul în mod
ascendent, pân când proprietatea de heap este restabilit . Dac , dimpotriv , valoarea vârfului
CAPITOLUL 1. STRUCTURI DE DATE 7
11 nivelul 0
7 10 nivelul 1
7 7 9 2 nivelul 2
4 6 5 7 3 nivelul 3
scade, astfel încât devine mai mic decât valoarea cel puµin a unui u, este sucient s schimb m
intre ele valoarea modicat cu cea mai mare valoare a iilor, apoi s continu m procesul în mod
descendent, pân când proprietatea de heap este restabilit .
Heap-ul este structura de date ideal pentru extragerea maximului/minimului dintr-o mulµime,
pentru inserarea unui vârf, pentru modicarea valorii unui vârf. Sunt exact operaµiile de care avem
nevoie pentru a implementa o list dinamic de priorit µi: valoarea unui vârf va da prioritatea
evenimentului corespunzator.
Evenimentul cu prioritatea cea mai mare/mic se va aa mereu la radacina heap-ului, iar
prioritatea unui eveniment poate modicat în mod dinamic.
Algoritmi
(a) Datele iniµiale sunt reprezentate de c tre coecienµii ecuaµiei iar obiectivul este
determinarea r d cinilor reale ale ecuaµiei.
(b) Vom folosi metoda de rezolvare a ecuaµiei de gradul al doilea având forma general
2
ax bx c 0. Aceast metod poate descris astfel:
2
Pasul 1. Se calculeaz discriminantul: ∆ b 4ac.
Pasul 2. Dac ∆ % 0
Ó
atunci ecuaµia are dou r d cini reale distincte: x1,2 b
2a
∆
altfel, dac ∆ 0
atunci ecuaµia are o r d cina real dubl : x1,2 b
2a
altfel ecuaµia nu are r d cini reale.
(c) Aplicarea metodei pentru datele problemei (a 1, b 3, c 2) conduce la rezul-
tatul: x1 1, x2 2.
2.2 Algoritmi
8
CAPITOLUL 2. ALGORITMI 9
Secvenµa de pa³i prin care este descris metoda de rezolvare a ecuaµiei de gradul al doilea
(prezentat în secµiunea anterioar ) este un exemplu de algoritm. Calculul efectuat la Pasul 1
este un exemplu de operaµie aritmetic , iar analiza semnului discriminantului (Pasul 2) este un
exemplu de operaµie logic .
Descrierea unui algoritm presupune precizarea datelor iniµiale ³i descrierea prelucr rilor efec-
tuate asupra acestora. Astfel, se poate spune c :
algoritm = date + prelucr ri
Al-Khwarizmi a fost cel care a folosit pentru prima dat reguli precise ³i clare pentru a descrie
procese de calcul (operaµii aritmetice fundamentale) în lucrarea sa "Scurt carte despre calcul
algebric". Mai târziu, aceast descriere apare sub denumirea de algoritm în "Elementele lui
Euclid". Algoritmul lui Euclid pentru calculul celui mai mare divizor comun a dou numere
naturale este, se pare, primul algoritm cunoscut în matematic .
În matematic noµiunea de algoritm a primit mai multe deniµii: algoritmul normal al lui A.
A. Markov, algoritmul operaµional al lui A. A. Leapunov, ma³ina Turing, funcµii recursive, sisteme
POST. S-a demonstrat c aceste deniµii sunt echivalente din punct de vedere matematic.
În informatic exist de asemenea mai multe deniµii pentru noµiunea de algoritm. De exemplu,
în [41] noµiunea de algoritm se dene³te astfel:
Un algoritm este sistemul virtual
A M, V, P, R, Di, De, M i, M e
constituit din urm toarele elemente:
M - memorie intern format din locaµii de memorie ³i utilizat pentru stocarea tem-
porar a valorilor variabilelor;
V - mulµime de variabile denite în conformitate cu raµionamentul R, care utilizeaz
memoria M pentru stocarea valorilor din V ;
P - proces de calcul reprezentat de o colecµie de instrucµiuni/comenzi exprimate într-un
limbaj de reprezentare (de exemplu, limbajul pseudocod); folosind memoria virtu-
al M ³i mulµimea de variabile V , instrucµiunile implementeaz /codic tehnicile
³i metodele care constituie raµionamentul R; execuµia instrucµiunilor procesului de
calcul determin o dinamic a valorilor variabilelor; dup execuµia tuturor instruc-
µiunilor din P , soluµia problemei se a în anumite locaµii de memorie corespunz -
toare datelelor de ie³ire De;
R- raµionament de rezolvare exprimat prin diverse tehnici ³i metode specice dome-
niului din care face parte clasa de probleme supuse rezolv rii (matematic , zic ,
chimie etc.), care îmbinate cu tehnici de programare corespunz toare realizeaz
acµiuni/procese logice, utilizând memoria virtual M ³i mulµimea de variabile V ;
Di - date de intrare care reprezint valori ale unor parametri care caracterizeaz ipo-
tezele de lucru/st rile iniµiale ale problemei ³i care sunt stocate în memoria M prin
intermediul instrucµiunilor de citire/intrare care utilizeaz mediul de intrare M i;
De - date de ie³ire care reprezint valori ale unor parametri care caracterizeaz soluµia
problemei/st rile nale; valorile datelor de ie³ire sunt obµinute din valorile unor
variabile generate de execuµia instrucµiunilor din procesul de calcul P , sunt stocate
în memoria M , ³i înregistrate pe un suport virtual prin intermediul instrucµiunilor
de scriere/ie³ire care utilizeaz mediul de ie³ire M e; ;
M i - mediu de intrare care este un dispozitiv virtual de intrare/citire pentru preluarea
valorilor datelor de intrare ³i stocarea acestora în memoria virtual M ;
M e - mediu de ie³ire care este un dispozitiv virtual de ie³ire/scriere pentru prelua-
rea datelor din memoria virtual M ³i înregistrarea acestora pe un suport virtual
(ecran, hârtie, disc magnetic, etc.).
Un limbaj este un mijloc de transmitere a informaµiei.
Exist mai multe tipuri de limbaje: limbaje naturale (englez , român , etc), limbaje ³tiinµice
(de exemplu limbajul matematic), limbaje algoritmice, limbaje de programare (de exemplu Pascal,
C, Java), etc.
Un limbaj de programare este un limbaj articial, riguros întocmit, care permite
descrierea algoritmilor astfel încât s poat transmi³i calculatorului cu scopul ca
acesta s efectueze operaµiile specicate.
CAPITOLUL 2. ALGORITMI 10
a Prelucr rile simple sunt atribuiri de valori variabilelor, eventual prin evaluarea unor
expresii;
a Prelucr rile structurate pot de unul dintre tipurile:
Scrierea unui program pornind de la un algoritm descris într-un limbaj mai mult sau mai puµin
riguros, ca în exemplele de mai sus, este dicil întrucât nu sunt pu³i în evidenµ foarte clar pa³ii
algoritmului.
Modalit µi intermediare de descriere a algoritmilor, între limbajul natural sau cel matematic
³i un limbaj de programare, sunt schemele logice ³i limbajele algoritmice.
Schemele logicesunt descrieri grace ale algoritmilor în care ec rui pas i se ata³eaz
un simbol grac, numit bloc, iar modul de înl nµuire a blocurilor este specicat prin
segmente orientate.
Schemele logice au avantajul c sunt sugestive dar ³i dezavantajul c pot deveni dicil de
urm rit în cazul unor prelucr ri prea complexe. Acest dezavantaj, dar ³i evoluµia modului de
concepere a programelor, fac ca schemele logice s e din ce în ce mai puµin folosite (în favoarea
limbajelor algoritmice).
2.3.3 Pseudocod
Un limbaj algoritmic este o notaµie care permite exprimarea logicii algoritmilor într-un mod
formalizat f r a necesare reguli de sintax riguroase, ca în cazul limbajelor de programare.
Un limbaj algoritmic mai este denumit ³i pseudocod. Un algoritm descris în pseudocod conµine
atât enunµuri care descriu operaµii ce pot traduse direct într-un limbaj de programare (unui
enunµ în limbaj algoritmic îi corespunde o instrucµiune în program) cât ³i enunµuri ce descriu
prelucr ri ce urmeaz a detaliate abia în momentul scrierii programului.
Nu exist un anumit standard în elaborarea limbajelor algoritmice, ecare programator putând
s conceap propriul pseudocod, cu condiµia ca acesta s permit o descriere clar ³i neambigu a
algoritmilor. Se poate folosi sintaxa limbajului de programare preferat, în care apar enunµuri de
prelucr ri. De exemplu:
for ecare vârf v din V
{
culoare[v] = alb;
distanta[v] = innit;
predecesor[v]=-1;
CAPITOLUL 2. ALGORITMI 12
2.4.6 Subalgoritm
În cadrul unui algoritm poate s apar necesitatea de a specica de mai multe ori ³i în diferite
locuri un grup de prelucr ri. Pentru a nu le descrie în mod repetat ele pot constitui o unitate
distinct , identicabil printr-un nume, care este numit subalgoritm. Ori de câte ori este nece-
sar efectuarea grupului de prelucr ri din cadrul subalgoritmului se specic numele acestuia ³i,
eventual, datele curente asupra c rora se vor efectua prelucrarile. Aceast acµiune se nume³te apel
al subalgoritmului, iar datele specicate al turi de numele acestuia ³i asupra c rora se efectueaz
prelucrarile se numesc parametri.
În urma traducerii într-un limbaj de programare un subalgoritm devine un subprogram.
Un subalgoritm poate descris în felul urm tor:
$nume_subalg% ($tip% $nume_p1%, $tip% $nume_p2%, ... )
{
...
/* prelucr ri specice subalgoritmului */
...
return $nume_rezultat%;
}
unde $nume_subalg% reprezint numele subalgoritmului iar nume_p1, nume_p2, ... reprezint
numele parametrilor. Ultimul enunµ, prin care se returneaz rezultatul calculat în cadrul subal-
goritmului, este optional.
Modul de apel depinde de modul în care subalgoritmul returneaz rezultatele sale. Dac su-
balgoritmul returneaz efectiv un rezultat, printr-un enunµ de forma
return $nume_rezultat%;
atunci subalgoritmul se va apela în felul urm tor:
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;
prelucrare pentru care ar putea folosit algoritmul corespunz tor schemei lui Horner, în continuare
prezent m o alt variant de rezolvare a acestei probleme, care folose³te un subalgoritm pentru
calculul puterilor unui num r î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; }
6. S se scrie un algoritm pentru determinarea celui mai mare element dintr-un ³ir de numere
reale.
Rezolvare. Fie x1 , x2 , ..., xn ³irul analizat. Determinarea celui mai mare element const în
iniµializarea unei variabile de lucru max (care va conµine valoarea maximului) cu x1 ³i compararea
acesteia cu ecare dintre celelalte elemente ale ³irului. Dac valoarea curent a ³irului, xk , este
mai mare decât valoarea variaabilei max atunci acesteia din urm i se va da valoarea xk . Astfel,
dup a k 1 comparaµie variabila max va conµine valoarea maxim din sub³irul x1 , x2 , ..., xk .
Algoritmul poate descris în modul urm tor:
int k, n;
read n;
double x[1..n], max; /* vectorul ³i variabila de lucru */
read x; /* preluarea elementelor ³irului */
max = x[1];
for k = 2, n
if (max $ x[k]) then max = x[k];
write max;
= k!1 .
n
sn
k 0
Rezolvare. Calculul aproximativ (cu precizia ε) al limitei ³irului sn const în calculul sumei
nite sk , unde ultimul termen al sumei, tk 1
k!
, are proprietatea tk $ ε. Întrucât tk1 tk
k1
,
aceast relaµie va folosit pentru calculul valorii termenului curent (permiµând mic³orarea nu-
m rului de calcule).
double eps, t, s;
int k;
k=1; /* iniµializare indice */
t=1; /* iniµializare termen */
s=1; /* iniµializare suma */
do {
s=s+t; /* ad ugarea termenului curent */
k=k+1;
t=t/k; /* calculul urm torului termen */
} while (t ' eps);
s=s+t; (* ad ugarea ultimului termen *)
write s;
8. Fie A o matrice cu m linii ³i n coloane, iar B o matrice cu n linii ³i p coloane, ambele având
elemente reale. S se determine matricea produs C A B .
Rezolvare. Matricea C va avea m linii ³i p coloane, iar ecare element se determin efectuând
suma:
=a
n
ci,j i,k bk,j , 1 & i & m, 1 & j & p.
k 1
În felul acesta calculul elementelor matricei C se efectueaz prin trei cicluri imbricate (unul
pentru parcurgerea liniilor matricei C , unul pentru parcurgerea coloanelor matricei C , iar unul
pentru efectuarea sumei specicate mai sus).
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]; /* matrice */
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;
CAPITOLUL 2. ALGORITMI 17
¶ax0 by0 c¶
d Ó .
2 2
a b
Dac d ' r ε atunci dreapta este exterioar cercului, dac d & r ε atunci dreapta este secant ,
iar dac r ε $ d $ r ε atunci este tangent (la implementarea egalitatea între dou numere
reale ...).
3. S se determine r d cina p trat a unui numur real pozitiv a cu precizia ε 0.001, folosind
relaµia de recurenµ :
1 a
xn1 x , x1 a.
2 n xn
Precizia se consider atins când ¶xn1 xn ¶ $ ε.
5. S se determine cel mai mare divizor comun al unui ³ir de numere întregi.
7. Fie A o matrice p tratic . S se calculeze suma elementelor din ecare zon (diagonala
principal , diagonala secundar , etc.) marcat în gura urm toare:
a. b. c.
9. S se determine toate r d cinile raµionale ale polinomului P X care are coecienµi întregi.
140. Fie P1 , P2 , ..., Pn un poligon convex dat prin coordonatele carteziene ale vârfurilor sale
(în ordine trigonometric ). S se calculeze aria poligonului.
CAPITOLUL 2. ALGORITMI 18
11. Fie f a, b R o funcµie continu cu proprietatea c exist un unic ξ " a, b care are
proprietatea c f ξ 0. S se aproximeze ξ cu precizia ε 0.001 utilizând metoda bisecµiei.
13. S se determine toate numerele prime cu maxim 6 cifre care r mân prime ³i dup "r stur-
narea" lor (r sturnatul num rului abcdef este f edcba).
Conversiile între diferitele tipuri sunt permise (acolo unde au semnicaµie). Se vede din tabel
c unele tipuri de variabile au posibilitatea s reprezinte un spectru mai mare de numere decât
altele.
În afara tipurilor de baz , limbajul Java suport ³i tipuri de date create de utilizator, de
pild variabile de tip clas , interfaµ sau tablou. Ca ³i celelalte variabile, dac nu sunt explicit
iniµializate, valoarea atribuit implicit este null.
Modicatorul static este folosit pentru a specica faptul c variabila are o singur valoare,
comun tuturor instanµelor clasei în care ea este declarat . Modicarea valorii acestei variabile
din interiorul unui obiect face ca modicarea s e vizibil din celelalte obiecte. Variabilele statice
sunt iniµializate la înc rcarea codului specic unei clase ³i exist chiar ³i dac nu exist nici o
instanµ a clasei respective. Din aceast cauz , ele pot folosite de metodele statice.
Tablourile unidimensionale se declar sub forma:
$nume%[i]
unde i poate lua orice valoare între 0 ³i n 1.
În cazul tablourilor bidimensionale, o declaraµie de forma:
$nume%[i][j]
unde i reprezint indicele liniei ³i poate avea orice valoare între 0 ³i m 1 iar j reprezint indicele
coloanei ³i poate avea orice valoare între 0 ³i n 1.
Preluarea unei valori de tip int de la tastatur se poate face sub forma:
2.5.6 Subprograme
În cadrul unui program poate s apar necesitatea de a specica de mai multe ori ³i în diferite
locuri un grup de prelucr ri. Pentru a nu le descrie în mod repetat ele pot constitui o unitate
distinct , identicabil printr-un nume, care este numit subprogram sau, mai precis, funcµie (dac
returneaz un rezultat) sau procedur (dac nu returneaz nici un rezultat). În Java funcµiile ³i
procedurile se numesc metode. Ori de câte ori este necesar efectuarea grupului de prelucr ri
din cadrul programului se specic numele acestuia ³i, eventual, datele curente asupra c rora se
vor efectua prelucrarile. Aceast acµiune se nume³te apel al subprogramului, iar datele specicate
al turi de numele acestuia ³i asupra c rora se efectueaz prelucrarile se numesc parametri.
Un subprogram poate descris în felul urm tor:
$tipr% $nume_sp% ($tipp1% $numep1%, $tipp2% $numep2%, ... )
{
...
/* prelucr ri specice subprogramului */
...
return $nume_rezultat%;
}
unde $tipr% reprezint tipul rezultatului returnat (void dac subprogramul nu returneaz nici
un rezultat), $nume_sp% reprezint numele subprogramului, iar numep1, numep2, ... reprezint
numele parametrilor. Ultimul enunµ, prin care se returneaz rezultatul calculat în cadrul subpro-
gramului, trebuie pus numai dac $tipr% nu este void.
Modul de apel depinde de modul în care subprogramul returneaz rezultatele sale. Dac sub-
programul returneaz efectiv un rezultat, printr-un enunµ de forma
return $nume_rezultat%;
atunci subprogramul se va apela în felul urm tor:
v=$nume_sp%(nume_p1, nume_p2, ...);
Aceste subprograme corespund subprogramelor de tip funcµie.
Dac în subprogram nu apare un astfel de enunµ, atunci el se va apela prin:
$nume_sp%(nume_p1, nume_p2, ...);
variant care corespunde subprogramelor de tip procedur .
Observaµie. Prelucr rile care nu sunt detaliate în cadrul algoritmului sunt descrise în limbaj
natural sau limbaj matematic. Comentariile suplimentare vor cuprinse între /* ³i */. Dac
pe o linie a descrierii algoritmului apare simbolul // atunci tot ce urmeaz dup acest simbol,
pe aceea³i linie cu el, este interpretat ca ind un comentariu (deci, nu reprezint o prelucrare a
programului).
Metoda static int maxFibo ( long nr ) returneaz indicele celui mai mare element din
³irul lui Fibonacci care este mai mic sau egal cu parametrul nr.
n n 2
2. Fie Sn x1 x2 unde x1 ³i x2 sunt r d cinile ecuaµiei cu coecienµi întregi ax bx c 0
(vom considera a 1!). S se a³eze primii 10 termeni ai ³irului Sn ³i s se precizeze în dreptul
ec rui termen dac este num r prim, iar dac nu este num r prim s se a³eze descompunerea
în factori.
Rezolvare:
class e02
{
public static void main(String[] args)
{
int a, b, c, nnp=0, s, p, n=10, k;
long[] ss=new long[n+1];
a=1;b=1;c=2;
s=-b/a;
p=c/a;
CAPITOLUL 2. ALGORITMI 23
ss[1]=s;
ss[2]=s*s-2*p;
for(k=3;k<=n;k++) ss[k]=s*ss[k-1]-p*ss[k-2];
for(k=1;k<=n;k++)
if(esteprim(Math.abs(ss[k])))
System.out.println(k+" : "+ss[k]+" PRIM "+(++nnp));
else
{
System.out.print(k+" : "+ss[k]+" = ");
descfact(Math.abs(ss[k]));
}
System.out.println("nnp = "+nnp);
}// main
αx
3. Se consider funcµia f x P xe unde P x este un polinom de grad n cu coecienµi
întregi. S se a³eze toate derivatele pân la ordinul m ale funcµiei f , ³i, în dreptul coecienµilor
polinoamelor care apar în aceste derivate, s se precizeze dac respectivul coecient este num r
prim, iar dac nu este num r prim s se a³eze descompunerea în factori. De asemenea, s se
a³eze care este cel mai mare num r prim care apare, ³i care este ordinul derivatei în care apare
acest cel mai mare num r prim.
CAPITOLUL 2. ALGORITMI 24
αx
Rezolvare: Derivata funcµiei f are forma Q xe unde Q este un polinom de acela³i grad cu
polinomul P . Toat rezolvarea problemei se reduce la determinarea coecienµilor polinomului Q
în funcµie de coecienµii polinomului P .
class e03
{
static long npmax=1,pmax=0;
System.out.println();
return;
}
while(nr%d==0)
{
System.out.print(d+" ");
nr=nr/d;
}
d=3;
while((d*d<=nr)&&(nr!=1))
{
while(nr%d==0)
{
System.out.print(d+" ");
nr=nr/d;
}
d=d+2;
}
if(nr!=1) System.out.println(nr);
else System.out.println();
}
4. R d cini raµionale. S se determine toate r d cinile raµionale ale unei ecuaµii cu coecienµi
întregi.
Rezolvare: Se caut r d cini raµionale formate din fracµii în care num r torul este divizor
al termenului liber iar numitorul este divizor al termenului dominant. Programul care urmeaz
genereaz coecienµii ecuaµiei, plecând de la fracµii date (ca r d cini), ³i apoi determin r d cinile
raµionale
}// afisv()
}// class
= 21 C
n
n
f n k nk
k 0
n
neµinând cont de faptul c f n are o expresie mult mai simpl , ³i anume 2 . Suma trebuie
calculat simulând operaµiile de adunare, înmulµire ³i împ rµire la 2, cu numere mari.
Rezolvare: Funcµia se pune sub forma:
=2
n
1 nk n
f n Cnk
2n
k 0
class e05
{
public static void main (String[]args)
{
int n, k;
int[] s;
int[] p;
for(n=10;n<=12;n++)
{
s=nrv(0);
for(k=0;k<=n;k++)
{
p=inm(comb(n+k,n),putere(2,n-k));
s=suma(s,p);
}
afisv(s);
for(k=1;k<=n;k++) s=impartLa2(s);
System.out.print(n+" : ");
afisv(s);
fcifre(s);
}
System.out.println("GATA");
}//main()
}// class
6. S se a³eze S n, 1, S n, 2, ..., S n, m (inclusiv suma cifrelor ³i num rul cifrelor pentru
ecare num ) ³tiind c
S n 1, m S n, m 1 mS n, m
³i
S n, 1 S n, n 1, ¾n ' m.
Se vor implementa operaµiile cu numere mari.
Rezolvare: Matricea de calcul este subdiagonal . Se completeaz cu 1 prima coloan ³i diago-
nala principal , iar apoi se determin celelalte elemente ale matricei folosind relaµia dat (aranjat
puµin altfel!). Matricea de calcul va avea de fapt trei dimensiuni (numerele devin foarte mari, a³a
c elementul Si,j trebuie s conµin vectorul cifrelor valorii sale).
class e06
{
public static void main(String[] args)
{
int n=50, m=40, i, j;
int[][][] s=new int[n+1][m+1][1];
for(i=1;i<=n;i++)
{
if(i<=m) s[i][i]=nr2v(1);
s[i][1]=nr2v(1);
for(j=2;j<=min(i,m);j++)
s[i][j]=suma(s[i-1][j-1],inm(nr2v(j),s[i-1][j]));
if(i<=m) s[i][i]=nr2v(1);
}
for(i=1;i<=m;i++)
{
System.out.print("\n"+i+" : "+s[n][i].length+" ");
afissumac(s[n][i]);
afisv(s[n][i]);
}
}
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
= C B ,B
n
k
Bn1 n k 0 1.
k 0
class e07
{
public static void main(String[] args)
{
int n=71; // n=25 ultimul care incape pe long
int k,i;
int[][] b=new int[n+1][1];
int[] prod={1};
b[0]=nr2v(1);
for(i=1;i<=n;i++)
{
b[i]=nr2v(0);;
for(k=0;k<=i-1;k++)
{
prod=inm(comb(i-1,k),b[k]);
b[i]=suma(b[i],prod);
}
CAPITOLUL 2. ALGORITMI 32
System.out.print(i+" : ");
afisv(b[i]);
}
System.out.println(" "+Long.MAX_VALUE);
System.out.println("... Gata ...");
}
n n n
1. Fie Sn x1 x2 x3 unde x1 , x2 ³i x3 sunt r d cinile ecuaµiei cu coecienµi întregi
3 2
ax bx cx d 0 (vom considera a 1!). S se a³eze primii 10 termeni ai ³irului Sn ³i s
se precizeze în dreptul ec rui termen dac este num r prim, iar dac nu este num r prim s se
a³eze descompunerea în factori.
=C
n1
k n1k
f n n1 n k 1!
k 0
n
neµinând cont de faptul c f n are o expresie mult mai simpl , ³i anume n . Suma trebuie
calculat simulând operaµiile cu numere mari.
=C k
n1
n1 k k1 nk
f n n n n k
k 1
n
neµinând cont de faptul c f n are o expresie mult mai simpl , ³i anume n . Suma trebuie
calculat simulând operaµiile cu numere mari.
CAPITOLUL 2. ALGORITMI 33
4. S se calculeze
1 1 1
f n n 1 p
1 p
... 1 p
1 2 m
i i i
unde n p11 p22 ...pmm reprezint descompunerea în factori primi a lui n.
5. S se calculeze
=φ n
6. S se calculeze
f n
d¶n
unde φ este funcµia de la exerciµiul anterior, neµinând cont de faptul c f n are o expresie mult
mai simpl , ³i anume n.
7. S se calculeze n
1 1 1
f n n! 1 ...
.
1! 2! n!
8. S se calculeze
=
m
mk k 1 λ1 2 λ2 n λn
f m, n, λ1 , λ2 , ..., λn 1 Cm Ck Ck1 ... Ckn1 .
k 1
9. S se calculeze
1 λ1 2 λ2 n λn
g m, n, λ1 , λ2 , ..., λn Cm Cm1 ... Cmn1
10. S se calculeze
1 1 2 2 n n
f n 2n! Cn 2 2n 1! Cn 2 2n 2! ... 1 2 n! .
2n
11. S se calculeze
1 n
Cn C
n 1 2n
implementând operaµiile cu numere mari.
12. S se a³eze P 100, 50 (inclusiv suma cifrelor ³i num rul cifrelor) ³tiind c
P n k, k P n, 1 P n, 2 ... P n, k
³i
P n, 1 P n, n 1, ¾n ' k ' 1.
Se vor implementa operaµiile cu numere mari.
r
13. S se determine cel mai mic num r natural r, astfel încât p e, unde p este o permutare
dat ³i e este permutarea identic .
=C
n
Cn k1 Cnk , C0 1.
k 1
16. S se calculeze
=
m1
1 k k n
S n, m 1 Cm m k
m!
k 0
Deniµia 1. O operaµie elementar este o operaµie al c rui timp de execuµie poate m rginit
superior de o constant care depinde numai de particularitatea implement rii (calculator, limbaj
de programare etc).
Deoarece ne intereseaz timpul de executie în limita unei constante multiplicative, vom consi-
dera doar num rul operaµiilor elementare executate într-un algoritm, nu ³i timpul exact de execuµie
al operaµiilor respective.
Este foarte important ce anume denim ca operaµie elementar . Este adunarea o operaµie
elementara? Teoretic nu este, pentru c depinde de lungimea celor doi operanzi. Practic, pentru
operanzi de lungime rezonabil putem s consider m c adunarea este o operaµie elementar . Vom
considera în continuare c adun rile, sc derile, înmulµirile, împ rµirile, operaµiile modulo (restul
împ arµirii întregi), operaµiile booleene, comparaµiile ³i atribuirile sunt operaµii elementare.
Uneori ecienµa difer dac µinem cont numai de unele operaµii elementare ³i le ignor m pe
celelalte (de exemplu la sortare: comparaµia ³i interschimbarea). De aceea în analiza unor algoritmi
vom considera o anumit operaµie elementar , care este caracteristic algoritmului, ca operaµie
barometru, neglijându-le pe celelalte.
De multe ori, timpul de executie al unui algoritm poate varia pentru cazuri de m rime identic .
De exemplu la sortare, dac introducem un ³ir de n numere gata sortat, timpul necesar va cel mai
mic dintre timpii necesari pentru sortarea oricarui alt ³ir format din n numere. Spunem c avem
de-a face cu cazul cel mai favorabil. Dac ³irul este introdus în ordine invers , avem cazul cel mai
defavorabil ³i timpul va cel mai mare dintre timpii de sortare a ³irului de n numere.
Exist algoritmi în care timpul de execuµie nu depinde de cazul considerat.
Dac dimensiunea problemei este mare, îmbun t µirea ordinului algoritmului este esenµial , în
timp ce pentru timpi mici este sufcient performanµa hardware.
35
CAPITOLUL 3. ANALIZA COMPLEXITII ALGORITMILOR 36
Elaborarea unor algoritmi ecienµi presupune cuno³tinµe din diverse domenii (informatic ,
matematic ³i cuno³tiinµe din domeniul c ruia îi aparµine problema practic a c rui model este
studiat, atunci când este cazul).
Exemplul 1. Elaboraµi un algoritm care returneaz cel mai mare divizor comun (cmmdc) a doi
termeni de rang oarecare din ³irul lui Fibonacci.
irul lui Fibonacci, fn fn1 fn2 , este un exemplu de recursivitate în cascad ³i calcularea
efectiv a celor doi termeni fm fn , urmat de calculul celui mai mare divizor al lor, este total
neindicat . Un algoritm mai bun poate obµinut dac µinem seama de rezultatul descoperit de
Lucas în 1876:
cmmdc fm , fn fcmmdc m,n
Deci putem rezolva problema calculând un singur termen al ³irului lui Fibonacci.
Exist mai mulµi algoritmi de rezolvare a unei probleme date. Prin urmare, se impune o analiz
a acestora, în scopul determin rii ecienµei algoritmilor de rezolvare a problemei ³i pe cât posibil a
optimalit µii lor. Criteriile în funcµie de care vom stabili ecienµa unui algoritm sunt complexitatea
spaµiu (memorie utilizat ) ³i complexitatea timp (num rul de operaµiilor elementare).
Rezult c O f este mulµimea tuturor funcµiilor m rginite superior de un multiplu real pozitiv
al lui f , pentru valori sucient de mari ale argumentului.
Dac t n " O f vom spune c t este de ordinul lui f sau în ordinul lui f .
Fie un algoritm dat ³i o funcµie t N R , astfel încât o anumit implementare a algoritmului
s necesite cel mult t n unit µi de timp pentru a rezolva un caz de marime n.
Principiul invarianµei ne asigur c orice implementare a algoritmului necesit un timp în
ordinul lui t. Mai mult, acest algoritm necesit un timp în ordinul lui f pentru orice functie
f N R pentru care t " O f . În particular t " O t. Vom c uta s g sim cea mai simpl
funcµie astfel încât t " O f .
Pentru calculul ordinului unei funcµii sunt utile urm toarele propriet µi:
Proprietatea 1. O f ¿ f " O g
O g ³i "O
g f
Proprietatea 2. O f L O g ¿ f " O g ³i g O f
Proprietatea 3. O f g O max f, g
Pentru calculul mulµimilor O f ³i O g este util proprietatea urm toare:
Proprietatea 4. Fie f, g N R
. Atunci
n
lim
f n
g n
"R
O f O g
lim
n
f n
g n
0 O f L O g.
Reciproca nu este în general valabil .
2
Fie de exemplu, t n n 3n 2, atunci
¼O n
2
n 3n 2 2 2
lim 1 3n 2 O n
n n2
¼O n
2
n 3n 2
3n 2 L O n
2 3
lim 0
n n3
1
lim
n
ln n
Ó
n
lim
n
n
1
Ó
lim Ó
n n
2
0 ¼ O ln n L O Ón
2 n
Ó
dar O n L © O ln n
m
Dac p este un polinom de gradul m în variabila n, atunci O p O n .
Notaµia asimptotic dene³te o relaµie de ordine parµial între funcµii.
Pentru f, g N R not m f T g dac O f N O g .
Aceast ierarhie corespunde ierarhiei algoritmilor dup criteriul performanµei. Pentru o pro-
blem dat , dorim sa realiz m un algoritm cu un ordin situat cât mai în stânga în aceast ierarhie.
CAPITOLUL 3. ANALIZA COMPLEXITII ALGORITMILOR 38
rezult TA n " O n .
k
sunt necesare aproximativ 18 minute. Pentru n 50, acela³i program va rula 13 zile pe acest
13
calculator, pentru n 60, vor necesari peste 310 ani, iar pentru n 100 aproximativ 4.10 ani.
Utilitatea algoritmilor polinomiali de grad mare este de asemenea limitat . De exemplu, pentru
10 9
O n , pe un calculator care execut 10 operaµii pe secund sunt necesare 10 secunde pentru
13
n 10, aproximativ 3 ani pentru n 100 ³i circa 3.10 ani pentru n 1000.
Uneori este util s determin m ³i o limit inferioar pentru timpul de execuµie a unui algoritm.
Notaµia matematic este Ω.
Deniµie: Spunem c TA n " Ω f n dac ³i numai dac ¿c % 0 ³i n0 " N astfel încât
TA n ' c f n, ¾n ' n0 .
De exemplu:
a) dac TA n 3n 2, atunci TA n " Ω n, pentru c 3n 2 ' 3n, ¾n ' 1;
b) dac TA n 10n 4n 2, atunci TA n " Ω n, pentru c 10n 4n 2 ' n2, ¾n ' 1;
2 2
Exist funcµii f care constituie atât o limit superioar cât ³i o limit inferioar a timpului de
... a1 n a0 , ak % 0 atunci
k k1
execuµie a algoritmului. De exemplu, dac TA n ak n ak1 n
TA n " Ω n .
k
Deniµie : Spunem c TA n " Θ f n dac ³i numai dac ¿c1 , c2 % 0 ³i n0 " N astfel încât
c1 f n & TA n & c2 f n, ¾n ' n0 .
În acest caz f n constituie atât o limit inferioar cât ³i o limit superioar pentru timpul
de execuµie a algoritmului. Din acest motiv Θ se poate numi ordin exact. Se poate ar ta u³or c
CAPITOLUL 3. ANALIZA COMPLEXITII ALGORITMILOR 39
k k1
Θ f n O f n = Ω f n. De asemenea, dac TA n ak n ak1 n ... a1 n a0 ,
ak % 0 atunci TA n " Θ n .
k
atunci este
=pd
complexitatea medie
TA d.
d"D
3.3 Exemple
max = a[1];
for i = 2 to n do
if a[i] > max
then max = a[i];
Vom estima timpul de execuµie al algoritmului în funcµie de n, num rul de date de intrare. Fi-
ecare iteraµie a ciclului for o vom considera operaµie elementar . Deci complexitatea algoritmului
este O n, atât în medie cât ³i în cazul cel mai defavorabil.
= 1i =k
i i
1 1 i i 1 i1 i1
k 1 1 i
1
i i 2 2 2
k 1 k 1
CAPITOLUL 3. ANALIZA COMPLEXITII ALGORITMILOR 41
=i 21
n
1 n n 1 n n 1 n n 1
1 n 1
1
2 2 2 2 4
i 2
2
operaµii elementare. Deci complexitatea algoritmului în medie este tot O n .
dac n 0 sau n
<
0, 1
Tn w1
n 1, dac n % 1
n
n k 1 Tk1 Tnk
CAPITOLUL 3. ANALIZA COMPLEXITII ALGORITMILOR 42
Deci,
=T
n
2
Tn n1 n k1
k 1
=T
n
nTn n n 1 2 k1
k 1
=T
n1
n 1Tn1 n 1 n 2 2 k1
k 1
obµinem
nTn n 1Tn1 n n 1 n 1 n 2 2Tn1
de unde rezult
nTn 2 n 1 n 1Tn1
Împ rµind ambii membri cu n n 1 obµinem
= k kk
n
Tn Tn1 2 n 1 Tn2 2 n 1 2 n 2 T2 1
n ... 2
n1 n n 1 n1 n n 1 n 1n 3
k 3
1
Deci
= = k1 2 = k1 2 ln n
n n n
Tn T2 1 1 1 T2 2
2
2
n1 3 k1 k k1 3 n1
k 3 k 3 k 1
O prim soluµie ar s calcul m pentru ecare persoan p din grup num rul de persoane pe
care p le cunoa³te (out) ³i num rul de persoane care cunosc persoana p (in). Cu alte cuvinte,
pentru ecare vârf din digraf calcul m gradul interior ³i gradul exterior. Dac g sim o persoan
pentru care out 0 ³i in n 1, aceasta va celebritatea c utat .
celebritate=0;
for p=1,2,...,n
{
in=0; out=0;
for j=1,2,...,n
{
in=in+a[j][p];
CAPITOLUL 3. ANALIZA COMPLEXITII ALGORITMILOR 43
out=out+a[p][j];
}
if (in=n-1) and (out = 0) celebritate=p;
}
if celebritate=0 writeln(’Nu exista celebritati !’)
else writeln(p, ’ este o celebritate.’);
2
Se poate observa cu u³urinµ c algoritmul este de O n . Putem îmbun t µi algoritmul f când
observaµia c atunci când test m relaµiile dintre persoanele x ³i y apar urm toarele posibilit µii:
ax, y 0 ³i în acest caz y nu are nici o ³ans s e celebritate, sau
ax, y 1 ³i în acest caz x nu poate celebritate.
Deci la un test elimin m o persoan care nu are ³anse s e celebritate.
F când succesiv n 1 teste, în nal vom avea o singur persoan candidat la celebritate.
R mâne s calcul m num rul de persoane cunoscute ³i num rul de persoane care îl cunosc pe
acest candidat, singura celebritate posibil .
candidat=1;
for i=2,n
if a[candidat][i]=1 candidat=i;
out=0;
in=0;
for i=1,n
{
in=in+a[i][candidat];
out=out+a[candidat][i];
}
if (out=0) and (in=n-1) write(candidat, ’ este o celebritate .’)
else write(’Nu exista celebritati.’);
În acest caz algoritmul a devenit liniar.
3.4 Probleme
c) 2
n1
" O 2n
d) n 1! " O n!
e) ¾f N R , f " O n
¼f 2
" O n2
f ) ¾f N R , f " O n
¼2 f
" O 2n
Rezolvare:
a) Armaµia este adevarat pentru c : limn
3
n
2
n3
0 ¼n 2
"O 3
n .
b) Armaµia este fals pentru c : limn
n
n2
=i " Θ n ,
n
ii
k
k1 pentru oricare k "N
i 1
= 1i " Θ n log n
n
iii
i 1
iv log n! " Θ n log n
= 1i γ
ln n
i 1
n i i 1 ' n
Deoarece
n 1 1 n ' n
2 n
n! n 1 n 1 2 n 2 3... 2
rezult 2 log n! ' n log n, adic log n! ' 0.5n log n, deci log n! " Ω n log n.
Relaµia se poate demonstra ³i folosind aproximarea lui Stirling
Ó n n
n! 2πn e 1 Θ 1©n
1. Ar taµi c :
3 6 " 3
a) n 10 n Θ n
b) n 6 2 " Θ n
n n
2 n 2
c) 2n n log n " Θ n
2 2
O f g O max f, g (3.4.1)
i lim
n
f n
g n
"R
O f O g , ii lim
n
f n
g n
0 O f L O g
Observaµie: Implicaµiile inverse nu sunt în general adev rate, deoarece se poate întampla ca
limitele s nu existe.
4. Demonstraµi prin inducµie c pentru a determina maximul a n numere sunt necesare n 1
comparaµii.
5. Care este timpul de execuµie a algoritmului quicksort pentru un vector cu n componente
egale?
6. S consider m urm torul algoritm de sortare a unui vector a cu n componente:
do
{
ok=true;
for i=1,n-1
if a[i]>a[i+1] { aux=a[i]; a[i]=a[i+1]; a[i+1]= aux; }
ok=false;
} while !ok;
CAPITOLUL 3. ANALIZA COMPLEXITII ALGORITMILOR 45
Recursivitate
Deniµiile prin recurenµ sunt destul de curente în matematic : progresia aritmetic , progresia
geometric , ³irul lui Fibonacci, limite de ³iruri, etc.
Pentru calculul termenilor ³irului lui Fibonacci, a transcriere literal a formulei este urm toa-
rea:
fibo(4) fact(4)
46
CAPITOLUL 4. RECURSIVITATE 47
comb(4,2)
comb(3,2) comb(3,1)
Ne putem întreba cum efectueaz Java calculul funcµiilor recursive. Putem s r spundem prin
urm rirea calculelor în cazul calculului lui f ibo 4. Reamintim c argumentele sunt transmise
prin valoare în acest caz, iar un apel de funcµie const în evaluarea argumentului, apoi lansarea
în execuµie a funcµiei cu valoarea argumentului. Deci
f ibo 4 f ibo 3 f ibo 2
f ibo 2 f ibo 1 f ibo 2
f ibo 1 f ibo 1 f ibo 1 f ibo 2
1 f ibo 1 f ibo 1 f ibo 2
1 1 f ibo 1 f ibo 2
2 f ibo 1 f ibo 2
2 1 f ibo 2
3 f ibo 2
3 f ibo 1 f ibo 1
3 1 f ibo 1
3 1 1
32
5
Exist deci un num r semnicativ de apeluri succesive ale funcµiei f ib (9 apeluri pentru calculul
lui f ibo 4). S not m prin Rn num rul apelurilor funcµiei f ibo pentru calculul lui f ibo n.
Evident R0 R1 1, ³i Rn 1 Rn1 Rn2 pentru n % 1. Punând Rn Rn 1, obµinem c
¬
Rn 2 f ibo n 1. Num rul de apeluri recursive este foarte mare! Exist o metod iterativ
simpl care permite calculul lui f ibo n mult mai repede.
n
f ibo n 1 1 f ibo n 1 1 1 1
...
f ibo n 1 1 0 f ibo n 2 1 0 0
u 1 1 u0
v 1 0 v0
static int fibo(int n) {
int u, v;
int u0, v0;
int i;
u = 1; v = 1;
for (i = 2; i <= n; ++i) {
u0 = u; v0 = v;
u = u0 + v0;
v = v0;
}
return u;
}
CAPITOLUL 4. RECURSIVITATE 48
Se poate calcula ³i mai repede folosind ultima form ³i calculând puterea matricei ...
Pentru a rezuma, o regul bun este s nu încerc m s intr m în meandrele detaliilor apelurilor
recursive pentru a înµelege sensul unei funcµii recursive. În general este sufucient s înµelegem
sintetic funcµia. Funcµia lui Fibonacci este un caz particular în care calculul recursiv este foarte
lung. Cam la fel se întâmpl (dac nu chiar mai r u!) ³i cu triunghiul lui Pascal. Dar nu
aceasta este situaµia în general. Nu numai c scrierea recursiv se poate dovedi ecace, dar ea este
totdeauna natural ³i deci cea mai estetic . Ea nu face decât s respecte deniµia matematic
prin recurenµ . Este o metod de programare foarte puternic .
irul lui Fibonacci are o cre³tere exponenµial . Exist funcµii recursive care au o cre³tere mult
mai rapid . Prototipul este funcµia lui Ackerman. În loc s denim matematic aceast funcµie,
este de asemenea simplu s d m deniµia recursiv în Java.
Funcµia lui Ackerman conµine dou apeluri recursive imbricate ceea ce determin o cre³tere
rapid . Un alt exemplu este "funcµia 91" a lui MacCarty [10]:
Se poate ar ta c aceast funcµie va returna 91 dac n & 100 ³i n 10 dac n % 100. Aceast
funcµie anecdotic , care folose³te recursivitatea imbricat , este interesant pentru c
nu este evident
c o astfel de deniµie d d acela³i rezultat.
Un alt exemplu este funcµia lui Morris [10] care are urm toarea form :
Ce valoare are g 1, 0? Efectul acestui apel de funcµie se poate observa din deniµia ei: g 1, 0
g 0, g 1, 0. Se declan³eaz la nesfâr³it apelul g 1, 0. Deci, calculul nu se va termina niciodat !
CAPITOLUL 4. RECURSIVITATE 49
Aceste câteva linii de program arat foarte bine cum generalizând problema, adic mutarea
de pe oricare tij i pe oricare tij j , un program recursiv de câteva linii poate rezolva o problem
apriori complicat . Aceasta este forµa recursivit µii ³i a raµionamentului prin recurenµ .
pasul 1
A B C A B C
a) b)
pasul 2
pasul 3
A B d) C A B c) C
Capitolul 5
G sirea expresiei lui xn care s satisfac relaµia de recurenµ se nume³te rezolvarea relaµiei de
recurenµ .F când substituµia
n
xn r
obµinem urm toarea ecuaµie, numit ecuaµie caracteristic :
2 k
a0 a1 r a2 r ... ak r 0 (5.1.2)
50
CAPITOLUL 5. ANALIZA ALGORITMILOR RECURSIVI 51
unde txn ¶i " r1, 2, ..., k xz sunt soluµii liniar independente ale relaµiei de recurenµ (se mai numesc
i
1 1 t t
unde c1 , ..., cs , c1 , ..., cp1 1 , ..., c1 , ..., cpt 1 sunt constante, care se pot determina din condiµiile
iniµiale.
a Ecuaµia caracteristic admite r d cini complexe simple
ib
Fie r ae a cos b i sin b o r d cin complex . Ecuaµia caracteristic are coecienµi reali,
ib
deci ³i conjugata r̄ ae a cos b i sin b este r d cin pentru ecuaµia caracteristic . Atunci
soluµiile corespunz toare acestora în sistemul fundamental de soluµii pentru recurenµa liniar ³i
omogen sunt
1 n 2 n
xn a cos bn, xn a sin bn.
a Ecuaµia caracteristic admite r d cini complexe multiple Dac ecuaµia caracteristic
admite perechea de r d cini complexe
bj0
ib ib
r ae , r̄ ae
de ordin de multiplicitate k , atunci soluµiile corespunz toare acestora în sistemul fundamental de
soluµii sunt
1 n 2 n k k1 n
xn a cos bn, xn na cos bn, ..., xn n a cos bn,
k1 n k2 n 2k k1 n
xn a sin bn, xn na sin bn, ..., xn n a sin bn,
Pentru a obµine soluµia general a recurenµei omogene de ordinul n cu coecienµi constanµi se
procedeaz astfel:
1. Se determin r d cinile ecuaµiei caracteristice
2. Se scrie contribuµia ec rei r d cini la soluµia general .
3. Se însumeaz ³i se obµine soluµia general în funcµie de n constante arbitrare.
4. Dac sunt precizate condiµiile iniµiale atunci se determin constantele ³i se obµine o
soluµie unic .
CAPITOLUL 5. ANALIZA ALGORITMILOR RECURSIVI 52
unde b este o constant , iar p n este un polinom în n de grad d. Ideea general este s reducem
un astfel de caz la o form omogen .
De exemplu, o astfel de recurenµ poate :
n
tn 2tn1 3
tn 2tn1 1, n 1
Avem nevoie de dou condiµii iniµiale. tim c t0 0; pentru a g si cea de-a doua condiµie
calcul m
t1 2t0 1 1.
Din condiµiile iniµiale, obµinem
n
tn 2 1.
Dac ne intereseaz doar ordinul lui tn , nu este necesar s calcul m efectiv constantele în
soluµia general . Dac ³tim c tn c1 1 c2 2 , rezult tn " O 2 .
n n n
Din faptul c num rul de mut ri a unor discuri nu poate negativ sau constant, deoarece
avem în mod evident tn ' n, deducem c c2 % 0. Avem atunci tn " Ω 2 ³i deci, tn " Θ 2 .
n n
Putem obµine chiar ceva mai mult. Substituind soluµia general înapoi în recurenµa iniµial , g sim
n n1
1 tn 2tn1 c1 c2 2 2 c1 c2 2 c1
= a Tn
k
n n
j j b1 pd1 n b2 pd2 n ...
j 0
în care
d d1
pd n n c1 n ... cd
Ecuaµia caracteristic complet este:
=a
k
kj d1 1 d2 1
j r r b1 r b2 ... 0
j 0
Exemplul 3: Tn 2T n 1 n 2 , n ' 1, T0 0.
n
T n aT n©b f n (5.2.6)
unde a ³i b sunt constante iar f n este o funcµie (aplicarea metodei Divide et Impera conduce de
obicei la o astfel de ecuaµie recurent ). A³a numita teorem Master d o metod general pentru
rezolvarea unor astfel de recurenµe când f n este un simplu polinom. Soluµia dat de teorema
master este:
cu ε % 0 atunci T n Θ n b
log aε log a
1. dac f n O n b
logb a logb a
2. dac f n Θ n atunci T n Θ n lg n
3. dac f n Ω n
logb aε
³i a f & c f n cu c $ 1 atunci T n Θ f n.
n
b
Din p cate, teorema Master nu funcµioneaz pentru toate funcµiile f n, ³i multe recurenµe
utile nu sunt de forma (5.2.6). Din fericire îns , aceasta este o tehnic de rezolvare a celor mai
multe relaµii de recurenµ provenite din metoda Divide et Impera.
Pentru a rezolva astfel de ecuaµii recurente vom reprezenta arborele generat de ecuaµia recur-
siv . R d cina arborelui conµine valoarea f n, ³i ea are noduri descendente care sunt noduri
r d cin pentru arborele provenit din T n©b.
CAPITOLUL 5. ANALIZA ALGORITMILOR RECURSIVI 54
i i
Pe nivelul i se a nodurile care conµin valoarea a f n©b . Recursivitatea se opre³te când se
obµine un caz de baz pentru recurenµ .
Presupunem c T 1 f 1.
Cu aceast reprezentare este foarte clar c T n este suma valorilor din nodurile arborelui.
Presupunând c ecare nivel este plin, obµinem
2 2 3 3 k k
T n f n af n©b a f n©b a f n©b ... a f n©b
k
unde k este adâncimea arborelui de recursivitate. Din n©b 1 rezult k logb n. Ultimul termen
k log n log a
diferit de zero în sum este de forma a a b n b (ultima egalitate ind întâlnit în liceu!).
Acum putem u³or enunµa ³i demonstra teorema Master.
Demonstraµie: Dac f n este un factor constant mai mare decât f b©n, atunci prin inducµie
se poate ar ta c suma este a unei progresii geometrice descresc toare. Suma în acest caz este o
constant înmulµit cu primul termen care este f n.
Dac f n este un factor constant mai mic decât f b©n, atunci prin inducµie se poate ar ta
c suma este a unei progresii geometrice cresc toare. Suma în acest caz este o constant înmulµit
log a
cu ultimul termen care este n b .
Dac af b©n f n, atunci prin inducµie se poate ar ta c ecare din cei k 1 termeni din
sum sunt egali cu f n.
Exemple.
1. Selecµia aleatoare: T n T 3n©4 n.
Aici af n©b 3n©4 iar f n n, rezult α 3©4, deci T n Θ n.
2. Algoritmul de multiplicare al lui Karatsuba: T n 3T n©2 n.
log 3
Aici af n©b 3n©2 iar f n n, rezult α 3©2, deci T n Θ n 2 .
3. Mergesort: T n 2T n©2 n.
Aici af n©b n, iar f n n, rezult α 1 deci T n Θ n log2 n.
Folosind accea³i tehnic a arborelui recursiv, putem rezolva recurenµe pentru care nu se poate
aplica teorema Master.
T n T n©2$ T *n©20 n.
Metoda transform rii domeniului rescrie funcµia T n sub forma S f n, unde f n este o
funcµie simpl ³i S are o recurenµ mai u³oar .
Urm toarele inegalit µi sunt evidente:
Acum denim o nou funcµie S n T n α, unde α este o constant necunoscut , aleas
astfel încât s e satisf cut recurenµa din teorema Master S n & S n©2 O n. Pentru a obµine
valoarea corect a lui α, vom compara dou versiuni ale recurenµei pentru funcµia S n α:
Pentru ca aceste dou recurenµe s e egale, trebuie ca n©2 α n α©2 1, care implic
α 2. Teorema Master ne spune acum c S n O n log n, deci
T n T n©2 T n©4 1.
Aceasta nu se potrive³te cu teorema master, pentru c cele dou subprobleme au dimensiuniÓ
diferite, ³i utilizând metoda arborelui de recursivitate nu obµinem decât ni³te margini slabe n $$
T n $$ n.
Dac nu au forma standard, ecuaµiile recurente pot aduse la aceast form printr-o schimbare
de variabil . O schimbare de variabil aplicabil pentru ecuaµii de recurenµ de tip multiplicativ
este:
n 2
k
k log n
De exemplu, e
T n 2 T n©2 n log n, n % 1
k
Facem schimbarea de variabil t k T 2 ³i obµinem:
k
t k 2 t k 1 k 2 , deci b 2, p k k, d 1
cu soluµia
k k 2 k
t k c1 2 c2 k 2 c3 k 2
Deci
c1 n c2 n log n c3 n log n " O n log n¶n
2 2 k
T n 2
CAPITOLUL 5. ANALIZA ALGORITMILOR RECURSIVI 56
Uneori, printr-o schimbare de variabil , putem rezolva recurenµe mult mai complicate. În
exemplele care urmeaz , vom nota cu T n termenul general al recurenµei si cu tk termenul noii
recurenµe obµinute printr-o schimbare de variabil .
Presupunem pentru început c n este o putere a lui 2.
Un prim exemplu este recurenta
T n 4T n©2 n, n % 1
k k
în care înlocuim pe n cu 2 , notam tk T 2 T n ³i obµinem
k
tk 4tk1 2
x 4 x 2 0
k k
³i deci, tk c1 4 c2 2 . Înlocuim la loc pe k cu log2 n
2
T n c1 n c2 n
Rezult
4T n©2 n , n % 1
2
T n
cu ecuaµia caracteristic
2
x 4 0
2 2
³i soluµia general tk c1 4 c2 k4 .
Atunci,
2 2
T n c1 n c2 n lg n
³i obµinem
T n " O n log n¶n este o putere a lui 2
2
T n 3T n©2 cn, n % 1
c ind o constant . Obµinem succesiv
k k1 k
T 2 3T 2 c2
k
tk 3tk1 c2
cu ecuaµia caracteristic
x 3 x 2 0
k k
tk c1 3 c2 2
lg n
T n c1 3 c2 n
³i, deoarece
lg b lg a
a b
obµinem
lg 3
T n c1 n c2 n
deci,
T n " O n
lg 3
¶n este o putere a lui 2
CAPITOLUL 5. ANALIZA ALGORITMILOR RECURSIVI 57
Putem enunµa acum o proprietate care este util ca reµet pentru analiza algoritmilor cu
recursivit µi de forma celor din exemplele precedente.
Fie T N º
R o funcµie eventual nedescresc toare
aT n©b cn , n % n0
k
T n
unde: n0 ' 1, b ' 2 si k ' 0 sunt întregi; a ³i c sunt numere reale pozitive; n©n0 este o putere a
lui b. Atunci avem
~ pentru a $ b ;
k k
Θ n ,
T n "
k k
Θ n log n, pentru a b ;
Θ nlogb a , pentru a % b ;
k
Fn2 Fn1 Fn , F0 0, F1 1.
are soluµiile Ó Ó
1 5 1 5
r1 , r2 .
2 2
Soluµia general este
Ó n Ó n
1 5 1 5
Fn c1 c2 .
2 2
Determin m constantele c1 ³i c2 din condiµiile iniµiale F0 0 ³i F1 1. Rezolvând sistemul
c1 c2 0
w Ó Ó
c1 12 5 c2
1 5
2
1
³i are soluµiile: r1 r2 r3 2.
Soluµia general este de forma:
2 n
xn c1 c2 n c3 n 2 .
³i are soluµiile: r1 2, r2 1 i ³i r3 1 i.
Soluµia general este de forma:
n Ó n nπ Ó n nπ
xn c1 2 c2 2 cos c3 2 sin .
4 4
Din condiµiile iniµiale rezult constantele: c1 ,
1
2
c2 1
2
si c3 3
2
.
Soluµia general este:
Ó n
n1 2 nπ nπ
xn 2 cos 3 sin .
2 4 4
T n 3T n 1 4T n 2 0, n ' 2, T 0 0, T 1 1.
2
Ecuaµia caracteristic r 3r 4 0 are soluµiile r1 1, r2 4, deci
n n
T n c1 1 c2 4
CAPITOLUL 5. ANALIZA ALGORITMILOR RECURSIVI 59
wcc
1
c1 c2 0 1
w 1
5
c1 4c2 1 2 5
Soluµia este:
1 n n
T n 4 1 .
5
T n 5T n 1 8T n 2 4T n 3, n ' 3, cu T 0 0, T 1 1, T 2 2.
Ecuaµia caracteristic :
r
3
5r
2
8r 4 0 r 1 1, r2 r3 2
deci
n n
T n c1 1 c2 2 c3 n2n
Determinarea constantelor
~
c1 c2 0 ~
c1 2
c1 2c2 2c3 1
c2 2
c1 4c2 8c3 2
c3
1
2
Deci
n1 n n n1 n1
T n 2 2 2 2 n2 2.
2
T n 4T n©2 n lg n.
În acest caz, avem af n©b 2n lg n 2n, care nu este tocmai dublul lui f n n lg n. Pentru
n sucient de mare, avem 2f n % af n©b % 1.9f n.
Suma este m rginit ³i inferior ³i superior de c tre serii geometrice cresc toare, deci soluµia
log 4 2
este T n Θ n 2 Θ n . Acest truc nu merge în cazurile doi ³i trei ale teoremei Master.
9. S se rezolve relaµia de recurenµ :
T n 2T n©2 n lg n.
= =
lg n1 lg n
n n
T n nHlg n Θ n lg lg n.
lg n i j
i 0 j 1
T n T 3n©4 T n©4 n.
În acest caz nodurile de pe acela³i nivel al arborelui de recursivitate au diferite valori. Nodurile
din orice nivel complet (adic , deasupra oric rei frunze) au suma n, deci este la fel ca în ultimul
caz al teoremei Master ³i orice frunz are nivelul între log4 n ³i log4©3 n.
Pentru a obµine o margine superioar , vom supraevalua T n ignorând cazurile de baz ³i
extinzând arborele în jos c tre nivelul celei mai adânci frunze.
Similar, pentru a obµine o margine inferioar pentru T n, vom subevalua T n contorizând
numai nodurile din arbore pân la nivelul frunzei care este cea mai puµin adânc . Aceste observaµii
ne dau marginile inferioar ³i superioar :
Deoarece aceste margini difer numai printr-un factor constant, avem c T n Θ n log n.
11. (Selecµie determinist ). S se rezolve relaµia de recurenµ :
T n T n©5 T 7n©10 n.
Din nou, avem un arbore recursiv "trunchiat". Dac ne uit m numai la nivelurile complete
ale arborelui, observ m c suma pe nivel formeaz o serie geometric descresc toare T n
n 9n©10 81n©100 ..., deci este ca în primul caz al teoremei Master. Putem s obµinem
o margine superioar ignorând cazurile de baz în totalitate ³i crescând arborele spre innit, ³i
putem obµine o margine inferioar contorizând numai nodurile din nivelurile complete. În ambele
situaµii, seriile geometrice sunt majorate de termenul cel mai mare, deci T n Θ n.
Algoritmi elementari
T n 2T n©2 2 ³i T 2 1.
2a b 1
w
an b 2 an©2 b 2
care are soluµia b 2 ³i a 3©2, deci (pentru n putere a lui 2), T n 3n©2 2, adic 75% din
algoritmul anterior. Se poate demonstra c num rul de comparaµii este 3 *n©20 2 pentru a aa
minimum ³i maximum.
O idee similar poate aplicat pentru varianta secvenµial . Presupunem c ³irul are un
num r par de termeni. Atunci, algoritmul are forma:
vmin = minim(x[1],x[2])
vmax = maxim(x[1],x[2])
for(i=3;i<n;i=i+2)
cmin = minim(x[i],x[i+1])
cmax = maxim(x[i],x[i+1])
if cmin < vmin
vmin = cmin
if vmax > cmax
vmax = cmax
Fiecare iteraµie necesit trei comparaµii, iar iniµializarea variabilelor necesit o comparaµie.
Ciclul se repet de n 2©2 ori, deci avem un total de 3n©2 2 comparaµii pentru n par.
61
CAPITOLUL 6. ALGORITMI ELEMENTARI 62
6.1.2 Divizori
se nume³te descompunere canonic . Dac not m prin d n num rul divizorilor lui n " N, atunci:
d n 1 α1 1 α2 ... 1 αk (6.1.2)
d=2;nd=0;
while(n%d==0){nd++;n=n/d;}
p=p*(1+nd);
d=3;
while(d*d<=n)
{
nd=0;
while(n%d==0){nd++;n=n/d;}
p=p*(1+nd);
d=d+2;
}
if(n!=1) p=p*2;
return p;
}
Un algoritm pentru calculul celui mai mare divizor comun (cmmdc) a dou numere naturale
poate descompunerea lor în factori ³i calculul produsului tuturor divizorilor comuni. De exemplu
dac a 1134 2 3 3 3 3 7 ³i b 308 2 2 7 11 atunci cmmdc a, b 2 7 14.
Descompunerea în factoriÓ
a unui num r natural n poate necesita încercarea tuturor numerelor
naturale din intervalul 2, n.
Un algoritm ecient pentru calculul cmmdc a, b este algoritmul lui Euclid.
CAPITOLUL 6. ALGORITMI ELEMENTARI 63
Pentru orice dou numere intregi pozitive, exist x ³i y (unul negativ) astfel încât x a y b
cmmdc a, b. Aceste numere pot calculate parcurgând înapoi algoritmul clasic al lui Euclid.
Fie ak ³i bk valorile lui a ³i b dup k iteraµii ale buclei din algoritm. Fie xk ³i yk numerele care
indeplinesc relaµia xk ak yk bk cmmdc ak , bk cmmdc a, b. Prin inductie presupunem
c xk ³ yk exist , pentru c la sfâr³it, când bk divide ak , putem lua xk 0 ³ yk 1.
Presupunând c xk ³i yk sunt cunoscute, putem calcula xk1 ³i yk1 .
ak bk1 ³i bk ak1 mod bk1 ak1 dk1 bk1 , unde
dk1 ak1 ©bk1 (împ rµire întreag ).
Substituind aceste expresii pentru ak ³i bk obµinem
cmmdc a, b xk ak yk bk
xk bk1 yk ak1 dk1 bk1
yk ak1 xk yk dk1 bk1 .
Astfel, µinând cont de relaµia xk1 ak1 yk1 bk1 cmmdc a, b, obµinem
xk1 yk ,
yk1 xk yk dk1 .
Pentru 1134 ³i 308, obµinem:
a0 1134, b0 308, d0 3;
a1 308, b1 210, d1 1;
a2 210, b2 98, d2 2;
a3 98, b3 14, d3 7.
³i de asemenea, valorile pentru xk ³i yk :
x3 0, y3 1;
x2 1, y2 012 2;
x1 2, y1 121 3;
x0 3, y1 2 3 3 11.
Desigur relaµia 3 1134 11 308 14 este corect . Soluµia nu este unic . S observ m c
3 k 308 1134 11 k 1134 308 14, pentru orice k , ceea ce arat c valorile calculate
pentru x x0 ³i y y0 nu sunt unice.
Toate operaµiile cu polinoame obi³nuite se fac utilizând ³iruri de numere care reprezint coe-
cienµii polinomului. Not m cu a si b vectorii coecienµilor polinoamelor cu care se opereaz ³i cu
m ³i n gradele lor. Deci
m n
a X am X ... a1 X a0 ³i b X bn X ... b1 X b0 .
Evident, gradul polinomului produs p a b este m n iar coecientul pk este suma tuturor
produselor de forma ai bj unde i j k , 0 & i & m ³i 0 & j & n.
m=a.length-1;
val=a[m];
for(i=m-1;i>=0;i--)
val=val*x+a[i];
return val;
}
Fie
n n1
b X bn X bn1 X ... b1 X b0
Generarea submulµimilor unei multimi A ra1 , a2 , ..., an x, este identic cu generarea submul-
µimilor mulµimii de indici r1, 2, ..., nx.
O submulµime se poate memora sub forma unui vector cu n componente, unde ecare com-
ponent poate avea valori 0 sau 1. Componenta i are valoarea 1 dac elementul ai aparµine
submulµimii ³i 0 în caz contrar. O astfel de reprezentare se nume³te reprezentare prin vector
caracteristic.
Generarea tuturor submulµimilor înseamn generarea tuturor combinaµiilor de 0 ³i 1 care pot
reµinute de vectorul caracteristic V , adic a tuturor numerelor în baza 2 care se pot reprezenta
folosind n cifre.
CAPITOLUL 6. ALGORITMI ELEMENTARI 68
Pentru a genera adunarea în binar, µinem cont c trecerea de la un ordin la urm torul se face
când se obµine suma egal cu 2, adic 1 1 102 .
poziµia 1 2 3 4
De exemplu, pentru n 4, vom folosi un vector v
valoarea
iniµial 0 0 0 0 ³i adun m 1
obµinem 0 0 0 1 ³i adun m 1
obµinem 0 0 0 2 care nu este permis, ³i trecem la ordinul urm tor
obµinem 0 0 1 0 ³i adun m 1
obµinem 0 0 1 1 ³i adun m 1
obµinem 0 0 1 2 care nu este permis, ³i trecem la ordinul urm tor
obµinem 0 0 2 0 care nu este permis, ³i trecem la ordinul urm tor
obµinem 0 1 0 0 ³i a³a mai departe
obµinem pân când
obµinem 1 1 1 1
n
Aceste rezultate se pot reµine într-o matrice cu n linii ³i 2 coloane.
0 1 2 3 4 5 6 7 8 9 A B C D E F
a1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0
a2 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 1
a3 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 2
a4 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 3
Ultima coloan conµine num rul liniei din matrice. Coloana 0 reprezint mulµimea vid , co-
loana F reprezint întreaga mulµime, ³i, de exemplu, coloana 5 reprezint submultimea ra2 , a4 x
iar coloana 7 reprezint submultimea ra2 , a3 , a4 x.
int ny=y.length;
int nz;
if(nx>ny)
nz=nx+1;
else
nz=ny+1;
int[] z=new int[nz];
int t,s,i;
t=0;
for (i=0;i<=nz-1;i++)
{
s=t;
if(i<=nx-1)
s=s+x[i];
if(i<=ny-1)
s=s+y[i];
z[i]=s%10;
t=s/10;
}
if(z[nz-1]!=0)
return z;
else
{
int[] zz=new int[nz-1];
for (i=0;i<=nz-2;i++) zz[i]=z[i];
return zz;
}
}
t=s/10;
}
if(z[nz-1]!=0)
return z;
else
{
int[] zz=new int [nz-1];
for(j=0;j<=nz-2;j++)
zz[j]=z[j];
return zz;
}
}
6.5.3 Puterea
n
Presupunem c vrem s calcul m x . Cum facem acest lucru? Este evident c urm toarea
secvenµ funcµioneaz :
for (p = x, i = 1; i < n; i *= 2) p *= p;
Aici num rul de treceri prin ciclu este egal cu k log2 n.
Acum, s consider m cazul general. Presupunem c n are expresia binar bk , bk1 , ..., b1 , b0 .
Atunci putem scrie
=
k
i
n 2.
i 0,bi 1
Deci,
5
k i
n 2
x x .
i 0,bi 1
return 1;
if (n & 1) /* n este impar */
return x * exponent_3(x, n - 1);
y = exponent_3(x, n / 2);
return y * y;
}
6.6.1 Înmulµirea
O posibilitate este cea din ³coal . Aceasta presupune calculul unor determinanµi. Determi-
nantul det A se dene³te recursiv astfel:
=
n1
ij
det A 1 ai,j det Ai,j .
i 0
unde ai,j este element al matricei iar Ai,j este submatricea obµinut prin eliminarea liniei i ³i a
coloanei j .
Folosind determinanµi, inversa matricei se poate calcula folosind regula lui Cramer. Presupu-
nem c A este inversabil ³i e B bi,j matricea denit prin
ij
bi,j 1 det Ai,j © det A.
1 T T
Atunci A B , unde B este transpusa matricei B .
Capitolul 7
Algoritmi combinatoriali
Fie A ³i B dou mulµimi nite. Not m prin ¶A¶ cardinalul mulµimii A. Se deduce u³or c :
¶A < B ¶ ¶A¶ ¶B ¶ ¶A = B ¶.
Fie A o mulµime nit ³i A1 , A2 , ..., An submulµimi ale sale. Atunci num rul elementelor lui A
care nu apar în nici una din submulµimile Ai (i 1, 2, ..., n) este egal cu:
=A = =
n
n
¶A¶ ¶ i¶ ¶Ai = Aj ¶ ¶Ai = Aj = Ak ¶ ... 1 ¶A1 = A2 = ... = An ¶
i 1 1&i$j &n 1&i$j $k&n
A = A = = A
n n n
n1
¶ i¶ ¶ i¶ ¶Ai = Aj ¶ ¶Ai = Aj = Aj ¶ ... 1 ¶ i¶
i 1 i 1 1&i$j &n 1&i$j $k&n i 1
A = A = = A
n n n
n1
¶ i¶ ¶ i¶ ¶Ai < Aj ¶ ¶Ai < Aj < Aj ¶ ... 1 ¶ i¶
i 1 i 1 1&i$j &n 1&i$j $k&n i 1
=A =
n
n
Sm,n ¶A¶ ¶ i¶ ¶Ai = Aj ¶ ... 1 ¶A1 = A2 = ... = An ¶
i 1 1&i$j &n
m m m
Se poate observa u³or c ¶A¶ n , ¶Ai ¶ n 1 , ¶Ai = Aj ¶ n 2 , etc.
k
Din Y putem elimina k elemente în Cn moduri, deci
= A
k
k m
¶ ij ¶ Cn n k
1&i1 $i2 $...$ik &n j 1
73
CAPITOLUL 7. ALGORITMI COMBINATORIALI 74
Rezult :
m 1 m 2 m n1 n1
Sm,n n Cn n 1 Cn n 2 ... 1 Cn
Observaµii:
1. Deoarece A1 = A2 = ... = An o ³i pentru c nu poate exista o funcµie care s nu ia nici o
valoare, ultimul termen lipse³te.
2. Dac n m atunci num rul funcµiilor surjective este egal cu cel al funcµiilor injective, deci
Sm,n n! ³i se obµine o formul interesant :
=
n1
k k n
n! 1 Cn n k
k 0
class Surjectii
{
public static void main (String[]args)
{
int m, n=5, k, s;
for(m=2;m<=10;m++)
{
s=0;
for(k=0;k<=n-1;k++)
s=s+comb(n,k)*putere(-1,k)*putere(n-k,m);
System.out.println(m+" : "+s);
}
System.out.println("GATA");
}
Fie X r1, 2, ..., nx. Dac p este o permutare a elementelor mulµimii X , spunem c num rul
i este un punct x al permut rii p, dac p i i (1 & i & n).
Se cere s se determine num rul D n al permut rilor f r puncte xe, ale mulµimii X . S
not m cu Ai mulµimea celor n 1! permut ri care admit un punct x în i (dar nu obligatoriu
numai acest punct x!). Folosind principiul includerii ³i al excluderii, num rul permut rilor care
admit cel puµin un punct x este egal cu:
=A = A .
n n
n1
¶A1 < A2 < ... < An ¶ ¶ i¶ ¶Ai = Aj ¶ ... 1 ¶ i¶
i 1 1&i$j &n i 1
Dar
¶Ai1 = Ai2 = ... = Aik ¶ n k !
deoarece o permutare din mulµimea Ai1 = Ai2 = ... = Aik are puncte xe în poziµiile i1 , i2 , ..., ik ,
celelalte poziµii conµinând o permutare a celor n k elemente r mase (care pot avea sau nu puncte
k
xe!). Cele k poziµii i1 , i2 , ..., ik pot alese în Cn moduri, deci
1 2 n1 n
¶A1 < A2 < ... < An ¶ Cn n 1! Cn n 2! ... 1 Cn .
Atunci
De aici rezult c
D n 1
e , lim
n! n
deci, pentru n mare, probabilitatea ca o permutare a n elemente, aleas aleator, s nu aib puncte
xe, este de e 0.3678.
1
class PermutariFixe
{
public static void main(String [] args)
{
long n=10,k,s=0L,xv,xn; // n=22 maxim pe long !
if((n&1)==1) xv=-1L; else xv=1L;
s=xv;
for(k=n;k>=3;k--) { xn=-k*xv; s+=xn; xv=xn; }
System.out.println("f("+n+") = "+s);
}
}
Dac n obiecte trebuie împ rµite în mai puµin de n mulµimi, atunci exist cel puµin o
mulµime în care vor cel puµin dou obiecte.
Fiind date m obiecte, care trebuie împ rµite în n mulµimi, ³i un num r natural k astfel
încât m % kn, atunci, în cazul oric rei împ rµiri, va exista cel puµin o mulµime cu cel
puµin k 1 obiecte.
n =f
b"B
¶
1 n
b¶ $ r r n
Se d un ³ir nit a1 , a2 , ..., an de numere întregi. Exist o subsecvenµ ai , ai1 , ..., aj cu pro-
prietatea c ai ai1 ... aj este un multiplu de n.
S consider m urm toarele sume:
s1 a1 ,
s2 a1 a2 ,
...
sn a1 a2 ... an .
Se d ³irul de numere reale distincte a1 , a2 , ..., amn1 . Atunci, ³irul conµine un sub³ir cresc tor
de m 1 elemente:
ai1 $ ai $ ... $ ai
2 m1
unde 1 & i1 $ i2 $ ... $ im 1 & mn 1,
aj1 $ aj $ ... $ aj
2 n1
unde 1 & j1 $ j2 $ ... $ jn 1 & mn 1,
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, ...
Se poate ar ta c
1 Ó n Ó n
Fn Ó 1 5 1 5 .
n
2 5
Numerele lui Fibonacci satisfac multe identit µi interesante, ca de exemplu:
n
1 1 Fn1 Fn
(7.3.2)
1 0 Fn Fn1
2 n
Fn1 Fn1 Fn 1 (7.3.3)
Fnm Fm Fn1 Fm1 Fn (7.3.4)
Fnk multiplu de Fk (7.3.5)
(7.3.6)
³i
Teorema 2. Orice num r natural n se poate descompune într-o sum de numere Fibonacci. Dac
nu se folosesc în descompunere numerele F0 ³i F1 ³i nici dou numere Fibonacci consecutive,
atunci aceast descompunere este unic abstracµie f când de ordinea termenilor.
19 1 13 0 8 1 5 0 3 0 2 1 1 101001F
import java.io.*;
class DescFibo
{
static int n=92;
static long[] f=new long[n+1];
f[2]=1;
for(k=3;k<=n;k++) f[k]=f[k-1]+f[k-2];
for(k=0;k<=n;k++) System.out.println(k+" : "+f[k]);
System.out.println(" "+Long.MAX_VALUE+" = Long.MAX_VALUE");
System.out.println(" x = "+x);
while(x>0)
{
iy=maxFibo(x);
y=f[iy];
nrt++;
System.out.println(nrt+" : "+x+" f["+iy+"] = "+y);
x=x-y;
}
}
Numerele
1 n
Cn C
n 1 2n
se numesc numerele lui Catalan. Ele apar în multe probleme, ca de exemplu: num rul arborilor
binari, num rul de parantez ri corecte, num rul drumurilor sub diagonal care unesc punctele
0, 0 ³i n, n formate din segmente orizontale ³i verticale, num rul secvenµelor cu n biµi în
care num rul cifrelor 1 nu dep ³e³te num rul cifrelor 0 în nici o poziµie plecând de la stânga spre
dreapta, num rul segmentelor care unesc 2n puncte în plan f r s se intersecteze, num rul ³irurilor
x1 , x2 , ..., x2n în care xi " r1, 1x ³i x1 x2 ... x2n 0 cu proprietatea x1 x2 ... xi ' 0
pentru orice i 1, 2, ..., 2n 1, num rul modurilor de a triangulariza un poligon, ³i multe altele.
Numerele lui Catalan sunt soluµie a urm toarei ecuaµii de recurenµ :
for(i=2;i<=n;i++)
{
d=cmmdc(y[j],x[i]);
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}
rez=nrv(1);
for(i=2;i<=n;i++) rez=inm(rez,nrv(x[i]));
return rez;
}
Not m cu Ai mulµimea numerelor naturale mai mici ca n care sunt multipli de pi . Atunci avem:
n n n
¶Ai ¶
pi , ¶Ai = Aj ¶ pi pj , ¶Ai = Aj = Ak ¶ pi pj pk , ...
Rezult :
= pn = =
m
n n m n
φ n n
pi pj
pi pj pk ... 1
p1 p2 ...pm
i
i 1 1&i$j &m 1&i$j $k&m
1 1 1
φ n n 1 p
1 p
... 1 p
1 2 m
class Euler
{
static long[] fact;
public static void main (String[]args)
{
long n=36L; // Long.MAX_VALUE=9.223.372.036.854.775.807;
long nrez=n;
long[] pfact=factori(n);
// afisv(fact);
// afisv(pfact);
int k,m=fact.length-1;
for(k=1;k<=m;k++) n/=fact[k];
for(k=1;k<=m;k++) n*=fact[k]-1;
System.out.println("f("+nrez+") = "+n);
CAPITOLUL 7. ALGORITMI COMBINATORIALI 81
Fie
e e e
n f1 1 f2 2 ... fk k
descompunerea lui n în factori primi ³i ndiv n num rul divizorilor lui n. Atunci
ndiv n 1 e1 1 e2 ... 1 ek .
Fie
e e e
n f1 1 f2 2 ... fk k
descompunerea lui n în factori primi ³i sdiv n suma divizorilor lui n. Atunci
=d
1e1 1e2 1ek
f1 1 f2 1 fk 1
sdiv n ... .
f1 1 f2 1 fk 1
d¶n
CAPITOLUL 7. ALGORITMI COMBINATORIALI 82
Demonstraµie:
Fie n ab cu a j b ³i cmmdc a, b 1. Pentru orice divizor d al lui n, d ai bj , unde ai este
divizor al lui a iar bj este divizor al lui b. Divizorii lui a sunt: 1, a1 , a2 , ..., a. Divizorii lui b sunt:
1, b1 , b2 , ..., b.
Sumele divizorilor lui a ³i b sunt:
sdiv a 1 a1 a2 ... a
sdiv b 1 b1 b2 ... b.
= d =a b =a =b
Dar
sdiv ab i j i j sdiv a sdiv b
d¶ab i,j i j
P n k, k .
Obµinem relaµia
P n, k P n 1, k 1 P n k, k
care poate transformat astfel:
P n, k P n 1, k 1 P n k, k
P n 1, k 1 P n 2, k 2 P n k, k 1
P n 2, k 2 P n 3, k 3 P n k, k 2
...
P n k 3, 3 P n k 2, 2 P n k, 3
P n k 2, 2 P n k 1, 1 P n k, 2
o mulµime cu n elemente ³i
A1 , A2 , ..., Ak
o partiµie oarecare.
Elementul an poate
(2) într-o submulµime cu cel puµin 2 elemente (printre care se g ³e³te ³i el!).
=b b
n1
bn b0 bn1 b1 bn2 ... bn1 b0 k n1k
k 0
Se obµine
1 n
C bn
n 1 2n
2. Care este num rul permut rilor a n obiecte cu p puncte xe?
p
Rezolvare: Deoarece cele p puncte xe pot alese în Cn moduri, ³i cele n p puncte r mase
nu mai sunt puncte xe pentru permutare, rezult c num rul permut rilor cu p puncte xe este
egal cu
p
Cn D n p
deoarece pentru ecare alegere a celor p puncte xe exist D n p permut ri ale obiectelor
r mase, f r puncte xe.
21
3. Fie X r1, 2, ..., nx. Dac E n reprezint num rul permut rilor pare ale mulµimii X
f r puncte xe, atunci
1 n1
E n D n 1 n 1 .
2
Rezolvare: Fie Ai mulµimea permut rilor pare p astfel încât p i i. Deoarece num rul
permut rilor pare este 12 n!, rezult c
1
E n n! ¶A1 < ... < An ¶
2
1 1 2 n n
n! Cn n 1! Cn n 2! ... 1 Cn .
2
inând cont c
¶Ai1 = Ai2 = ... = Aik ¶ n k !
rezult formula cerut .
21
Dac i $ j, ³i în permutare i apare dup j, spunem c avem o inversiune. O permutare este par dac are un
num r par de inversiuni
Capitolul 8
Algoritmi de c utare
Scrierea procedurii de c utare într-un tabel de nume este în acest caz mai ecient , pentru c
nu se face decât un singur test în plus aici (în loc de dou teste). C utarea secvenµial se mai
nume³te ³i c utare linear , pentru c se execut N ©2 operaµii în medie, ³i N operaµii în cazul cel
mai defavorabil. Într-un tablou cu 10.000 elemente, c utarea execut 5.000 operaµii în medie, ceea
ce înseamn un consum de timp de aproximativ 0.005 ms (milisecunde).
Iat un program complet care utilizeaz c utarea linear într-o tabel .
85
CAPITOLUL 8. ALGORITMI DE CUTARE 86
class Tabela {
Cel mai simplu algoritm care rezolv aceast problem este c utarea liniar , care testeaz
elementele vectorului unul dupa altul, începând cu primul.
p=-1;
for(i=0;i<n;i++)
if(x==a[i]) { p=i; break; }
S analiz m complexitatea acestui algoritm pe cazul cel mai defavorabil, acela în care v nu
se g se³te în a. În acest caz se va face o parcurgere complet a lui a. Considerând ca operaµie
elementar testul v ai, num rul de astfel de operaµii elementare efectuate va egal cu
numarul de elemente al lui a, adic n. Deci complexitatea algoritmului pentru cazul cel mai
defavorabil este O n.
return telefon[i];
if (cmp < 0)
d = i - 1;
else
s = i + 1;
} while (s <= d);
return -1;
}
Num rul CN de comparaµii efectuate pentru o tabel de dimensiune N este CN 1 CN ©2$ ,
unde C0 1. Deci CN log2 N .
De acum înaninte, log2 N va scris mai simplu log N .
Dac tabela are 10.000 elemente, atunci CN 14. Acesta reprezint un beneciu considerabil
în raport cu 5.000 de operaµii necesare la c utarea linear .
Desigur c utarea secvenµial este foarte u³or de programat, ³i ea se poate folosi pentru tabele
de dimensiuni mici. Pentru tabele de dimensiuni mari, c utarea binar este mult mai interesant .
Se poate obµine un timp sub-logaritmic dac se cunoa³te distribuµia obiectelor. De exemplu, în
cartea de telefon, sau în dicµionar, se ³tie apriori c un nume care începe cu litera V se a c tre
sfâr³it. Presupunând distribuµia uniform , putem face o "regul de trei simpl " pentru g sirea
indicelui elementului de referinµ pentru comparare, în loc s alegem mijlocul, ³i s urm m restul
algoritmului de c utare binar . Aceast metod se nume³te c utare prin interpolare. Timpul de
c utare este O log log N , ceea ce înseamn cam 4 operaµii pentru o tabel de 10.000 elemente ³i
9
5 operaµii pentru 10 elemente în tabel . Este spectaculos!
O implementare iterativ a c ut rii binare într-un vector ordonat (cresc tor sau descresc tor)
este:
int st, dr, m;
boolean gasit;
st=0;
dr=n-1;
gasit=false;
while((st < dr) && !gasit)
{
m=(st+dr)/2;
if(am]==x)
gasit=true;
else if(a[m] > x)
dr=m-1;
else st=m+1;
}
if(gasit) p=m; else p=-1;
Algoritmul poate descris, foarte elegant, recursiv.
8.5 Dispersia
O alt metod de c utare în tabele este dispersia. Se utilizeaz o funcµie h de grupare a cheilor
(adesea ³iruri de caractere) într-un interval de numere întregi. Pentru o cheie x, h x este locul
unde se af x în tabel . Totul este în ordine dac h este o aplicaµie injectiv . De asemenea, este
bine ca funcµia aleas s permit evaluarea cu un num r mic de operaµii. O astfel de funcµie este
m1 m2
h x x1 B x2 B ... xm1 B xm mod N.
Astfel, procedura de c utare consum un timp mai mare sau egal ca lungimea medie a claselor
de echivalenµ denite pe tabel de valorile h x, adic de lungimea medie a listei de coliziuni.
Dac funcµia de dispersie este perfect uniform , nu apar coliziuni ³i determin toate elemen-
tele printr-o singur comparaµie. Acest caz este foarte puµin probabil. Dac num rul mediu de
elemente având aceea³i valoare de dispersie este k N ©M , unde M este num rul claselor de echi-
valenµ denite de h, c utarea ia un timp de N ©M . Dispersia nu face decât s reduc printr-un
factor constant timpul c ut rii secvenµiale. Interesul faµ de dispersie este pentru c adesea este
foarte ecace, ³i u³or de programat.
Capitolul 9
9.1 Introducere
Ce este sortarea? Presupunem c se d un ³ir de N numere întregi ai , ³i se dore³te aranjarea
lor în ordine cresc toare, în sens larg. De exemplu, pentru N 10, ³irul
va deveni
3, 3, 8, 9, 10, 11, 13, 18, 23, 25.
Aceast problem este clasic în informatic ³i a fost studiat în detaliu, de exemplu, în [20].
În proctic se întâlne³te adesea aceast problem . De exemplu, stabilirea clasamentului între
studenµi, construirea unui dicµionar, etc. Trebuie f cut o distinµie între sortarea unui num r
mare de elemente ³i a unui num r mic de elemente. În acest al doilea caz, metoda de sortare este
puµin important .
Un algoritm amuzant const în a vedea dac setul de c rµi de joc din mân este deja ordonat.
Dac nu este, se d cu ele de p mânt ³i se reîncepe. Dup un anumit timp, exist riscul de a avea
c rµile ordonate. Desigur, poate s nu se termine niciodat , pentru noi, aceast sortare.
O alt tehnic (discutând serios de data aceasta), frecvent utilizat la un joc de c rµi, const
în a vedea dac exist o transpoziµie de efectuat. Dac exist , se face interschimbarea c rµilor
de joc ³i se caut o alt transpoziµie. Procedeul se repet pân când nu mai exist transpoziµii.
Aceast metod funcµioneaz foarte bine pentru o bun distribuµie a c rµilor.
Este u³or de intuit c num rul obiectelor de sortat este important. Nu este cazul s se caute o
metod sosticat pentru a sorta 10 elemente. Cu alte cuvinte, nu se trage cu tunul într-o musc !
Exemplele tratate într-un curs sunt întotdeauna de dimensiuni limitate, din considerente pe-
dagogice nu este posibil de a prezenta o sortare a mii de elemente.
89
CAPITOLUL 9. ALGORITMI ELEMENTARI DE SORTARE 90
C utarea celui mai mic element într-un tablou este un prim exerciµiu de programare. Deter-
minarea poziµiei acestui element este foarte simpl , ea se poate efectua cu ajutorul instrucµiunilor
urm toare:
m = 0;
for (int j = 1; j < N; ++j)
if (a[j] < a[m])
m = i;
Schimbarea între ele a celor dou elemente necesit o variabil temporar t ³i se efectueaz
prin:
class SortSelectie
{
final static int N = 10;
static int[] a = new int[N];
afisare();
}
}
i m i m
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
3 12
4 30 7 4 23 14
3 4
12 30 7 4 23 14
i m i m
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
3 4 12
30 7 4
23 14
3 4 4
30 7 12
23 14
i m i m
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
3 4 4 30
7 12 23 14
3 4 4 7
30 12 23 14
i m i m
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
3 4 4 7 30
12 23 14
3 4 4 7 12
30
23 14
i m i m
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 14 23 30
i m i m
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
3 4 4 7 12 14 23
30
3 4 4 7 12 14 23
30
im im
3
0 1
4
2
4
3
7
4
12
5
14
6
23
7
30
0
3
1
4
2
4 7
3
12
4
14
5 6
23
7
30
Este u³or de calculat num rul de operaµii necesare. La ecare iteraµie, se pleac de la elementul
ai ³i se compar succesiv cu ai1 , ai2 , ..., aN . Se fac deci N i comparaµii. Se începe cu i 1 ³i
se termin cu i N 1. Deci, se fac N 1 N 2 ... 2 1 N N 1©2 comparaµii ³i
2
N 1 interschimb ri. Sortarea prin selecµie execut un num r de comparaµii de ordinul N .
O variant a sort rii prin selecµie este metoda bulelor. Principiul ei este de a parcurge ³irul
a1 , a2 , ..., aN inversând toate perechile de elemente consecutive aj 1, aj neordonate. Dup
prima parcurgere, elementul maxim se va aa pe poziµia N . Se reîncepe cu prexul a, a2 , ..., aN 1 ,
..., a1 , a2 .
Procedura corespunz toare utilizeaz un indice i care marcheaz sfâr³itul prexului în sortare,
³i un indice j care permite deplasarea c tre marginea i.
Se poate calcula de asemenea foarte u³or num rul de operaµii ³i se obµine un num r de ordinul
2
O N comparaµii ³i, eventual interschimb ri (dac , de exemplu, tabloul este iniµial în ordine
descresc toare).
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
7 12 4 30 3 4 23 14 7 4 12 30 3 4 23 14
j i j i
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
7 4 12 30 3 4 23 14 7 4 12 3 30 4 23 14
j i j i
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
7 4 12 3 30 4 23 14 7 4 12 3 4 30 23 14
j i j i
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
7 4 12 3 4 30 23 14 7 4 12 3 4 23 30 14
j i j i
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
7 4 12 3 4 23 30 14 7 4 12 3 4 23 14 30
ji ji
Dup prima parcurgere 30 a ajuns pe locul s u (ultimul loc în vector).
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
7 4 12 3 4 23 14 30
4 7 12 3 4 23 14
30
j i j i
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
4 7 12 3
4 23 14
30
4 7 3 12
4 23 14
30
j i j i
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
4 7 3 12 4
23 14
30
4 7 3 4 12
23 14
30
j i j i
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
4 7 3 4 12 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 în vector).
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 s u de la începutul
acestei parcurgeri; s-au mai aranjat totu³i câteva elemente!).
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 de la începutul
acestei parcurgeri; oricum, la aceast parcurgere s-au mai aranjat câteva elemente!).
La urm toarea parcurgere nu se efectueaz nici o interschimbare de elemente. Vectorul este
deja sortat, a³a c urm toarele parcurgeri se fac, de asemenea, f r s se execute nici o interschim-
bare de elemente. O idee bun este introducerea unei variabile care s contorizeze num rul de
interschimb ri din cadrul unei parcurgeri. Dac nu s-a efectuat nici o interschimbare atunci vec-
torul este deja sortat a³a c se poate întrerupe execuµia urm toarelor parcurgeri. Programul
CAPITOLUL 9. ALGORITMI ELEMENTARI DE SORTARE 93
modicat este:
j i j i
CAPITOLUL 9. ALGORITMI ELEMENTARI DE SORTARE 94
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
3 4 7
12 30 4
23 14
3 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 comparaµii pentru inserarea unui element în secvenµa deja sortat este egal cu
num rul de inversiuni plus 1.
Fie ci num rul de comparaµii. Atunci
ci 1 cardraj ¶aj % ai , j $ ix
Pentru o permutare π corespunz toare unui ³ir de sort ri, unde num rul de inversiuni este
inv π , num rul total de comparaµii pentru sortarea prin inserµie este
=c
N
Cπ i N 1 inv π .
i 2
CN
1
=
N ! π"S
Cπ N 1
N N 1
4
N N 3
4
1
N
Metoda anterioar se poate îmbunat µi dac µinem cont de faptul c secvenµa în care se face
inserarea este deja ordonat , iar în loc s se fac inserµia direct în aceast secvenµ , c utarea
poziµiei pe care se face inserarea se face prin c utare binar . Un program complet este:
import java.io.*;
class SortInsBinApl
{
static int[] a={3,8,5,4,9,1,6,4};
while (p <= u)
{
i=(p+u)/2;
if (x>a[i])
p=i+1;
else if (x<a[i])
u=i-1;
else return i;
}
return p;
}
schimb=true;
}
}
}
Se grupeaz elementele aate la 4 poziµii distanµ , ³i se sorteaz separat. Acest proces este
numit numit 4 sort. Rezult ³irul:
Se observ urm toarele:
în exemplul anterior s-a folosit secventa de incremenµi 4, 2, 1 dar orice secvenµ , cu condiµia
ca cea mai n sortare s e 1 sort. În cazul cel mai defavorabil, în ultimul pas se face
totul, dar cu multe comparaµii ³i interschimb ri.
Liste
Scopul listelor este de a genera un ansamblu nit de elemente al c rui num r nu este xat
apriori. Elementele acestui ansamblu pot numere întregi sau reale, ³iruri de caractere, obiecte
informatice complexe, etc. Nu suntem acum interesaµi de elementele acestui ansamblu ci de
operaµiile care se efectueaz asupra acestuia, independent de natura elementelor sale.
Listele sunt obiecte dinamice, în sensul c num rul elementelor variaz în cursul execuµiei
programului, prin ad ug ri sau ³tergeri de elemente pe parcursul prelucr rii. Mai precis, operaµiile
permise sunt:
testarea dac ansamblul este vid
ad ugarea de elemente
vericarea dac un element este în ansamblu
³tergerea unui element
97
CAPITOLUL 10. LISTE 98
cap cap
e4 e3 e2 e1 e4 e3 e2 e1
Funcµia cauta, care veric dac elementul x este în lista a, efectueaz o parcurgere a listei.
Variabila a este modicat iterativ prin a=a.urmator pentru a parcurge elementele listei pân
se g se³te x sau pân se g se³te sfâr³itul listei (a null).
static boolean cauta (int x, Lista a) {
while (a != null) {
if (a.continut == x)
return true;
a = a.urmator;
}
return false;
}
Funcµia cauta poate scris în mod recursiv:
static boolean cauta (int x, Lista a) {
if (a == null)
return false;
else if (a.continut == x)
return true;
else
return cauta(x, a.urmator);
}
sau sub forma:
static boolean cauta (int x, Lista a) {
if (a == null)
return false;
else return (a.continut == x) || cauta(x, a.urmator);
}
sau sub forma:
static boolean cauta (int x, Lista a) {
return a != null && (a.continut == x || cauta(x, a.urmator));
}
Aceast sciere recursiv este sistematic pentru funcµiile care opereaz asupra listelor. Tipul
list veric ecuaµia urm toare:
unde h este sau exclusiv iar este produsul cartezian. Toate procedurile sau funcµiile care
opereaz asupra listelor se pot scrie recursiv în aceast manier . De exemplu, lungimea listei se
poate determina prin:
static int lungime(Lista a) {
if (a == null) return 0;
else return 1 + longime(a.urmator);
}
CAPITOLUL 10. LISTE 99
cap cap
e4 e3 e2 e1 e4 e3 e2 e1
În cadrul funcµiilor anterioare, care modic lista a, nu se dispune de dou liste distincte, una
care conµine x ³i alta identic cu precedenta dar care nu mai conµine x. Pentru a face acest lucru,
trebuie recopiat o parte a listei a într-o nou list , cum face programul urm tor, unde exist
o utilizare puµin important de memorie suplimentar . Oricum, spaµiul de memorie pierdut este
recuperat de culeg torul de spaµiu de memorie GC (Garbage Colection) din Java, dac nu mai este
folosit lista a. Cu tehnicile actuale ale noilor versiuni ale GC, recuperarea se efectueaz foarte
rapid. Aceasta este o diferenµ important faµ de limbajele de programare precum Pascal sau C,
unde trebuie s m preocupaµi de spaµiul de memorie pierdut, dac trebuie s - l reutiliz m.
CAPITOLUL 10. LISTE 100
Exist o tehnic , utilizat adesea, care permite evitarea unor teste pentru eliminarea unui
element dintr-o list ³i, în general, pentru simplicarea program rii operaµiilor asupra listelor.
Aceasta const în utilizarea unui fanion / santinel care permite tratarea omogen a listelor indi-
ferent dac sunt vide sau nu. În reprezentarea anterioar lista vid nu a avut aceea³i structur ca
listele nevide. Se utilizeaz o celul plasat la începutul listei, care nu are informaµie semnicativ
în câmpul continut; adresa adev ratei prime celule se a în câmpul urmator al acestei celule.
Astfel obµinem o list p zit , avantajul ind c lista vid conµine numai garda ³i prin urmare un
num r de programe, care f ceau un caz special din lista vid sau din primul element din list , de-
vin mai simple. Aceast noµiune este un pic echivalent cu noµiunea de santinel pentru tablouri.
Se utilizeaz aceast tehnic în proceduri asupra ³irurilor prea lungi.
De asemenea, se pot deni liste circulare cu gard / paznic în care în prima celul în câmpul
urmator este ultima celul din list .
Pentru implementarea listelor se pot folosi perechi de tablouri : primul tablou, numit continut,
conµine elementele de informaµii, iar al doilea, numit urmator, conµine adresa elementului urm tor
din tabloul continut.
Procedura adauga efectueaz numai 3 operaµii elementare. Este deci foarte ecient . Procedu-
rile cauta ³i elimina sunt mai lungi pentru c trebuie s parcurg întreaga list . Se poate estima
c num rul mediu de operaµii este jum tate din lungimea listei. C utarea binar efectueaz un
num r logaritmic iar c utarea cu tabele hash (de dispersie) este ³i mai rapid .
Exemplu 1. Consider m construirea unei liste de numere prime mai mici decât un num r
natural n dat. Pentru construirea acestei liste vom începe, în prima faz , prin ad ugarea numerelor
de la 2 la n începând cu n ³i terminând cu 2. Astfel numerele vor în list în ordine cresc toare.
Vom utiliza metoda clasic a ciurului lui Eratostene: consider m succesiv elementele listei în
ordine cresc toare ³i suprim m toµi multiplii lor. Aceasta se realizeaz prin procedura urm toare:
10.2 Cozi
Cozile sunt liste liniare particulare utilizate în programare pentru gestionarea obiectelor care
sunt în a³teptarea unei prelucr ri ulerioare, de exemplu procesele de a³teptare a resurselor unui
sistem, nodurile unui graf, etc. Elementele sunt ad ugate sistematic la coad ³i sunt eliminate din
capul listei. În englez se spune strategie FIFO (First In First Out), în opoziµie cu strategia LIFO
(Last In Firs Out) utilizat la stive.
Mai formalizat, consider m o mulµime de elemente E , mulµimea cozilor cu elemente din E
este notat Coada E , coada vid (care nu conµine nici un element) este C0 , iar operaµiile asupra
cozilor sunt: esteV ida, adauga, valoare, elimina:
esteV ida este o aplicaµie denit pe Coada E cu valori în rtrue, f alsex, esteV ida C aste
egal cu true dac ³i numai dac coada C este vid .
valoare este o aplicaµie denit pe Coada E C0 cu valori în E care asociaz unei cozi C ,
nevid , elementul aat în cap.
elimina este o aplicaµie denit pe Coada E C0 cu valori în Coada E care asociaz unei
cozi nevide C o coad obµinut plecând de la C ³i eliminând primul element.
Pentru C j C0
elimina adauga x, C adauga x, elimina C
valoare adauga x, C valoare C
Pentru toate cozile C
elimina adauga x, C0 C0
valoare adauga x, C0 x
esteV ida C0 true
O prim idee de realizare sub form de programe a operaµiilor asupra cozilor este de a împru-
muta tehnica folosit în locurile unde clienµii stau la coad pentru a serviµi, de exemplu la gar
pentru a lua bilete, sau la cas într-un magazin. Fiecare client care se prezint obµine un num r ³i
clienµii sunt apoi chemaµi de c tre funcµionarul de la ghi³eu în ordinea cresc toare a numerelor de
ordine primite la sosire. Pentru gestionarea acestui sistem, gestionarul trebuie s cunoasc dou
numere: num rul obµinut de c tre ultimul client sosit ³i num rul obµinut de c tre ultimul client
servit. Not m aceste dou numere inceput ³i sf arsit ³i gestion m sistemul în modul urm tor:
atunci când funcµionarul este liber el poate servi un alt client, dac coada nu este vid ,
incrementeaz inceput ³i cheam posesorul acestui num r.
În cele ce urmeaz sunt prezentate toate operaµiile în Java utilizând tehnicile urm toare: se
reatribuie num rul 0 unui nou client atunci când se dep ³e³te un anumit prag pentru valoarea
sf arsit. Se spune c este un tablou (sau tampon) circular, sau coad circular .
class Coada {
final static int MaxC = 100;
int inceput;
int sfarsit;
boolean plina, vida;
int continut[];
Coada () {
inceput = 0; sfarsit = 0;
plina= false; vida = true;
info = new int[MaxC];
}
c.plina = false;
}
}
inceput sfarsit
e1 e2 ... en
O alt modalitate de gestionare a cozilor const în utilizarea listelor înl nµuite cu gard în care se
cunosc adresele primului ³i ultimului element. Aceasta d operaµiile urm toare:
class Coada {
Lista inceput;
Lista sfarsit;
puleaz tablouri sau liste. Utilizarea cozilor se face cu ajutorul funcµiilor vida, adauga, valoare,
elimina. Aceasta este deci interfaµa cozilor care are importanµ în programe complexe.
10.3 Stive
Noµiunea de stiv intervine în mod curent în programare, rolul s u principal ind la imple-
mentarea apelurilor de proceduri. O stiv se poate imagina ca o cutie în care sunt plasate obiecte
³i din care se scot în ordinea invers faµa de cum au fost introduse: obiectele sunt puse unul peste
altul în cutie ³i se poate avea acces numai la obiectul situat în vârful stivei. În mod formalizat,
se consider o mulµime E , mulµimea stivelor cu elemente din E este notat Stiva E , stiva vid
(care nu conµine nici un element) este S0 , operaµiile efectuate asupra stivelor sunt vida, adauga,
valoare, elimina, ca ³i la re. De aceast dat , relaµiile satisf cute sunt urm toarele:
elimina adauga x, S S
esteV ida adauga x, S f alse
valoare adauga x, S x
esteV ida S0 true
Cu ajutorul acestor relaµii se pot exprima toate operaµiile relative la stive.
Realizarea operaµiilor asupra stivelor se poate face utilizând un tablou care conµine elementele
³i un indice care indic poziµia vârfului stivei.
class Stiva {
final static int maxP = 100;
int inaltime;
Element continut[];
Stiva() {
inaltime = 0;
continut = new Element[maxP];
}
return s.continut[s.inaltime-1];
}
class Element {
boolean esteOperator;
int valoare;
char simbol;
}
Vom utiliza funcµiile denite pentru stiv ³i procedurile denite în cele ce urmeaz .
În acest caz, este util prezentarea unui program principal care utilizeaz aceste funcµii.
a Tail(a)
e1 e2 e3 e4 e1 e2 e3 e4
class Lista {
Object info;
Lista next;
Lista(Object x, Lista a) {
info = x;
next = a;
}
a b
a1 a2 a3 b1 b2 b3 b4
append(a,b}
a1 a2 a3
a b
a1 a2 a3 b1 b2 b3 b4
concat(a,b}
b c
e1 e2 e3 e4 e5 nill
Se poate scrie o versiune iterativ a versiunii recursive, graµie unei funcµii auxiliare care acumuleaz
rezultatul într-unul din argumentele sale:
CAPITOLUL 10. LISTE 109
Un alt exerciµiu formator const în a administra liste în care elementele sunt aranjate în ordine
cresc toare. Procedura de ad ugare devine ceva mai complex pentru c trebuie g sit poziµia
celulei care trebuie ad ugat dup parcurgerea unei p rµi a listei.
Nu trat m acest exerciµiu decât în cazul listelor circulare cu gard . Pentru o astfel de list ,
valoarea câmpului inf o din prima celul nu are nici o semnicaµie (este celula de gard ). Câmpul
next din ultima celul conµine adresa primei celule.
a1 a2 a3 a4 a5 a6
garda
O 1 , dac n & n0
T n w (11.2.1)
O n , dac n % n0
n k
a T b
~ a%b
log a k
O n b , dac
k k
T n
O n logb n , dac a b (11.2.2)
O nk a$b
k
, dac
22
divide-and-conquer, în englez
110
CAPITOLUL 11. ALGORITMI DIVIDE ET IMPERA 111
m
Demonstraµie:Putem presupune, f r a restrânge generalitatea, c n b n0 . De asemenea,
presupunem c T n c n0 dac n & n0 ³i T n a T nb c n dac n % n0 . Pentru n % n0
k k
avem:
n k
T n aT cn
b
m1 k
aT b n0 cn
m2 n k k
a aT b n0 c
cn
b
2 m2 n k k
a T b n 0 c a n
b
...
m m1 n k n k k
a T n 0 c a m1
... a n
b b
m k m1 k k m1 k k m k k
a cn0 c a b n0 ... a b n0 b n0
k k m
k m b b
cn0 a 1 a ... a
=
m k i
m b
ca
a
i 0
k
unde am notat cn0 prin c. Distingem cazurile:
1. a % bk . Seria <m
i 0
b
k
a
i
este convergent ³i deci ³irul sumelor parµiale este convergent. De
m logb n logb a
aici rezult c T n O a O a O n
k m km k k k
2. a b . Rezult c a b cn ³i de aici T n O n m O n logb n.
3. a $ b . Avem T n
k m bk m km k
O a a
O b O n .
11.3 Exemple
Dintre problemele clasice care se pot rezolva prin metoda divide et impera menµion m:
c utare binar
Se bazeaz pe metoda interschimb rii, îns din nou, interschimbarea se face pe distanµe mai
mari. Astfel, având tabloul a, se aplic urm torul algoritm:
5. se repet pa³ii 2, 3, 4 pân când scan rile se vor întâlni pe undeva la mijlocul tabloului. În
acel moment, tabloul a va partitionat în 2 astfel, la stânga lui x se vor g si elemente mai
mici ca ³i x, la dreapta, elemente mai mari ca ³i x. Dup aceasta, se aplic acela³i proces
sub³irurilor de la stânga ³i de la dreapta lui x, pân când aceste sub³iruri sunt sucient de
mici (se reduc la un singur element).
class QuickSort
{
static int x[]={3,5,2,6,4,1,8,2,4,3,5,3};
class MergeSort
{
static int x[]={3,5,2,6,4,1,8,2,4,3,5,3};
int m,aux;
if((dr-st)<=1)
{
if(a[st]>a[dr]) { aux=a[st];a[st]=a[dr];a[dr]=aux;}
}
else
{
m=(st+dr)/2;
mergeSort(st,m,a);
mergeSort(m+1,dr,a);
interclasare(st,m,dr,a);
}
return a;
}// mergeSort(...)
k=0;
for(i=st;i<=dr;i++) { a[i]=b[k]; k++; }
return a;
}//interclasare(...)
}//class
1
4
3
2
import java.io.*;
CAPITOLUL 11. ALGORITMI DIVIDE ET IMPERA 114
class drArieMaxima
{
static int x1,y1,x2,y2,n,x1s,y1s,x2s,y2s,amax;
static int[] x;
static int[] y;
boolean gasit=false;
for(i=1;i<=n;i++)
if((x1<x[i])&&(x[i]<x2)&&(y1<y[i])&&(y[i]<y2))
{
gasit=true;
break;
}
if(gasit)
{
dr(x1,y1,x[i],y2);
dr(x[i],y1,x2,y2);
dr(x1,y[i],x2,y2);
dr(x1,y1,x2,y[i]);
}
else { amax=s; x1s=x1; y1s=y1; x2s=x2; y2s=y2; }
}
}
Se dau trei tije verticale A, B2 ³i C. Pe tija A se g sesc n discuri de diametre diferite, aranjate
în ordine descresc toare a diametrelor de la baz spre vârf. Se cere s se g seasc o strategie de
CAPITOLUL 11. ALGORITMI DIVIDE ET IMPERA 115
a la un moment dat se va muta un singur disc (cel care se a deasupra celorlalte
discuri pe o tij );
a un disc poate a³ezat doar peste un disc de diametru mai mare decât al s sau pe
o tij goal .
pasul 1
A B C A B C
a) b)
pasul 2
pasul 3
A B d) C A B c) C
import java.io.*;
class Hanoi
{
static int nrMutare=0;
public static void main(String[] args) throws IOException
{
int nrDiscuri;
2 Disk 2 A --> C
3 Disc 1 B ==> C
4 Disk 3 A --> B
5 Disc 1 C ==> A
6 Disk 2 C --> B
7 Disc 1 A ==> B
8 Disk 4 A --> C
9 Disc 1 B ==> C
10 Disk 2 B --> A
11 Disc 1 C ==> A
12 Disk 3 B --> C
13 Disc 1 A ==> B
14 Disk 2 A --> C
15 Disc 1 B ==> C
import java.io.*;
class HanoiDisc
{
static int nrMutare=0;
static final int MAX_NR_DISCURI=9;
static int[] a=new int[MAX_NR_DISCURI],
b=new int[MAX_NR_DISCURI],
c=new int[MAX_NR_DISCURI];
static int na,nb,nc; // na = nr. discuri pe tija A; etc. ...
static int discMutat;
switch (tijaDestinatie)
{
case ’A’: a[(++na)-1]=discMutat; break;
case ’B’: b[(++nb)-1]=discMutat; break;
case ’C’: c[(++nc)-1]=discMutat;
}
}// mutaDiscul()
Numar discuri[1<=...<=9]: 4
A(4321) B() C()
1 Disc 1 A ==> B A(432) B(1) C()
2 Disc 2 A --> C A(43) B(1) C(2)
3 Disc 1 B ==> C A(43) B() C(21)
4 Disc 3 A --> B A(4) B(3) C(21)
5 Disc 1 C ==> A A(41) B(3) C(2)
6 Disc 2 C --> B A(41) B(32) C()
7 Disc 1 A ==> B A(4) B(321) C()
8 Disc 4 A --> C A() B(321) C(4)
9 Disc 1 B ==> C A() B(32) C(41)
10 Disc 2 B --> A A(2) B(3) C(41)
11 Disc 1 C ==> A A(21) B(3) C(4)
12 Disc 3 B --> C A(21) B() C(43)
13 Disc 1 A ==> B A(2) B(1) C(43)
14 Disc 2 A --> C A() B(1) C(432)
15 Disc 1 B ==> C A() B() C(4321)
Deciziile se aplic în ordine, una dup alta, pân când r mâne un singur element. Se cere acest
element.
class Injumatatire1
{
static int n=10;
static char[] d={’X’,’S’,’D’,’S’,’S’}; // nu folosesc d_0 ... !
while(st<dr)
{
System.out.println(st+" ... "+dr+" elimin "+d[i]);
m=(st+dr)/2;
if((dr-st+1)%2==1) impar=true; else impar=false;
1 ... 10 elimin S
6 ... 10 a ramas!
6 ... 10 elimin D
6 ... 7 a ramas!
6 ... 7 elimin S
7 ... 7 a ramas!
class Injumatatire2
{
static int n=10;
static char[] d=new char[10];
if(st0==dr0)
{
CAPITOLUL 11. ALGORITMI DIVIDE ET IMPERA 119
for(m=1;m<=i;m++) System.out.print(d[m]);
System.out.println(" --> "+st0);
return;
}
m=(st0+dr0)/2;
if((dr0-st0+1)%2==1) impar=true; else impar=false;
SSS --> 10
SSD --> 9
SDS --> 7
SDD --> 6
DSS --> 5
DSD --> 4
DDS --> 2
DDD --> 1
Capitolul 12
Algoritmi BFS-Lee
import java.io.*;
class BFS // nodurile sunt de la 1 la n
{ // distanta minima dintre nod "sursa" si nod "dest"
static final int oo=0x7fffffff; // infinit
static final int WHITE=0, GRAY=1, BLACK=2;
static int[][] a; // matricea de adiacenta
static int[] color; // pentru bfs
static int[] p; // predecesor
static int[] d; // distante catre sursa
static int[] q; // coada
static int ic; // inceput coada = prima pozitie ocupata din care scot !
static int sc; // sfarsit coada = prima pozitie libera pe care voi pune !
static int n,m; // varfuri, muchii
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
st.nextToken(); nods=(int)st.nval;
st.nextToken(); nodd=(int)st.nval;
a=new int[n+1][n+1];
color=new int[n+1];
p=new int[n+1];
d=new int[n+1];
q=new int[m+1];
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
a[i][j]=1;
120
CAPITOLUL 12. ALGORITMI BFS-LEE 121
a[j][i]=1;
}
bfs(nods,nodd);
System.out.println("Distanta("+nods+","+nodd+") = "+d[nodd]);
System.out.print("drum : "); drum(nodd); System.out.println();
out.close();
}//main
videzcoada();
incoada(start);
while(!coadaEsteVida())
{
u=dincoada();
p[v]=u;
if(color[fin]!=WHITE) break; // optimizare; ies din for
incoada(v);
}
}
}//for
color[u]=BLACK;
if(color[fin]!=WHITE) break; // am ajuns la nod_destinatie
}//while
}//bfs
/*
9 12 2 8 Distanta(2,8) = 4
2 5 drum : 2 3 4 7 8
1 2
2 3
3 1
3 4
4 5
5 6
6 4
7 8
8 9
9 7
7 4
*/
În ultima ecranizare a celebrei piese shakespeariene Romeo ³i Julieta tr iesc într-un ora³ mo-
dern, comunic prin e-mail ³i chiar învaµ s programeze. Într-o secvenµ tulbur toare sunt pre-
zentate fr mât rile interioare ale celor doi eroi încercând f r succes s scrie un program care s
determine un punct optim de întâlnire.
Ei au analizat harta ora³ului ³i au reprezentat-o sub forma unei matrice cu n linii ³i m coloane,
în matrice ind marcate cu spaµiu zonele prin care se poate trece (str zi lipsite de pericole) ³i cu
X zonele prin care nu se poate trece. De asemenea, în matrice au marcat cu R locul în care se
a locuinµa lui Romeo, iar cu J locul în care se a locuinµa Julietei.
Ei se pot deplasa numai prin zonele care sunt marcate cu spaµiu, din poziµia curent în oricare
dintre cele 8 poziµii învecinate (pe orizontal , vertical sau diagonale).
Cum lui Romeo nu îi place s a³tepte ³i nici s se lase a³teptat n-ar tocmai bine, ei au hot rât
c trebuie s aleag un punct de întâlnire în care atât Romeo, cât ³i Julieta s poat ajunge în
acela³i timp, plecând de acas . Fiindc la întâlniri amândoi vin într-un suet, ei estimeaz timpul
necesar pentru a ajunge la întâlnire prin num rul de elemente din matrice care constituie drumul
cel mai scurt de acas pân la punctul de întâlnire. i cum probabil exist mai multe puncte de
întâlnire posibile, ei vor s îl aleag pe cel în care timpul necesar pentru a ajunge la punctul de
întâlnire este minim.
Cerinµ
CAPITOLUL 12. ALGORITMI BFS-LEE 123
Scrieµi un program care s determine o poziµie pe hart la care Romeo ³i Julieta pot s ajung
în acela³i timp. Dac exist mai multe soluµii, programul trebuie s determine o soluµie pentru
care timpul este minim.
Datele de intrare
Fi³ierul de intrare rj.in conµine:
pe prima linie numerele naturale N M , care reprezint num rul de linii ³i respectiv de coloane
ale matricei, separate prin spaµiu;
pe ecare dintre urm toarele N linii se a M caractere (care pot doar R, J , X sau spaµiu)
reprezentând matricea.
Datele de ie³ire
Fi³ierul de ie³ire rj.out va conµine o singur linie pe care sunt scrise trei numere naturale
separate prin câte un spaµiu tmin x y , având semnicaµia:
x y reprezint punctul de întâlnire (x - num rul liniei, y - num rul coloanei);
tmin este timpul minim în care Romeo (respectiv Julieta) ajunge la punctul de întâlnire.
Restricµii ³i preciz ri
a 1 $ N, M $ 101
a Liniile ³i coloanele matricei sunt numerotate începând cu 1.
a Pentru datele de test exist întotdeauna soluµie.
Exemple
rj.in
5 8
XXR XXX
X X X
J X X X
XX
XXX XXXX
rj.out
4 4 4
Explicaµie:
Traseul lui Romeo poate : (1,3), (2,4), (3,4), (4,4). Timpul necesar lui Romeo pentru a ajunge
de acas la punctul de întâlnire este 4.
Traseul Julietei poate : (3,1), (4,2), (4,3), (4,5). Timpul necesar Julietei pentru a ajunge de
acas la punctul de întâlnire este deasemenea 4.
În plus 4, este punctul cel mai apropiat de ei cu aceast proprietate.
Timp maxim de executare: 1 secund /test
Indicaµii de rezolvare *
Romeo dup cel mult k pa³i, iar cele marcate cu 2 reprezint toate locurile în care se poate aa
Julieta dup cel mult k pa³i. Dac k nu reprezint momentul de timp minim la care cei doi se
întâlnesc înseamn c acesta a fost determinat mai devreme ³i algoritmul s-a oprit deja.
Analiza complexit µii
Ordinul de complexitate al operaµiei de citire a datelor de intrare este O M N .
Ordinul de complexitate al acestui algoritm este O kM N , unde k reprezint momentul în
care cei doi se întâlnesc.
Ordinul de complexitate al operaµiei de scriere a rezultatului este O 1.
În concluzie, ordinul de complexitate al algoritmului de rezolvare a acestei probleme este
O kM N .
Rezolvare detaliat
Codul surs *
import java.io.*;
class RJ
{
static final int zid=10000;
static int m,n;
static int[][] xr,xj;
static int[] qi=new int[5000]; // coada sau coada circulara mai bine !
static int[] qj=new int[5000]; // coada
st.nextToken(); m=(int)st.nval;
st.nextToken(); n=(int)st.nval;
for(i=0;i<=m+1;i++) xr[i][0]=xr[i][n+1]=xj[i][0]=xj[i][n+1]=zid; // E si V
for(j=0;j<=n+1;j++) xr[0][j]=xr[m+1][j]=xj[0][j]=xj[m+1][j]=zid; // N si S
CAPITOLUL 12. ALGORITMI BFS-LEE 125
tmin=10000;
imin=jmin=0;
for(i=1;i<=m;i++)
for(j=1;j<=n;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]; imin=i; jmin=j;}
Fermierul Ion deµine un teren de form p trat , împ rµit în N N p trate de latur unitate,
pe care cultiv carto. Pentru recoltarea cartolor fermierul folose³te un robot special proiectat
în acest scop. Robotul porne³te din p tratul din stânga sus, de coordonate 1, 1 ³i trebuie s
ajung în p tratul din dreapta jos, de coordonate N, N .
Traseul robotului este programat prin memorarea unor comenzi pe o cartel magnetic . Fiecare
comand specic direcµia de deplasare (sud sau est) ³i num rul de p trate pe care le parcurge
în direcµia respectiv . Robotul strânge recolta doar din p tratele în care se opre³te între dou
comenzi.
Din p cate, cartela pe care se a programul s-a deteriorat ³i unitatea de citire a robotului
nu mai poate distinge direcµia de deplasare, ci numai num rul de pa³i pe care trebuie s -i fac
robotul la ecare comand . Fermierul Ion trebuie s introduc manual, pentru ecare comand ,
CAPITOLUL 12. ALGORITMI BFS-LEE 126
direcµia de deplasare.
Cerinµ
Scrieµi un program care s determine cantitatea maxim de carto pe care o poate culege
robotul, în ipoteza în care Ion specic manual, pentru ecare comand , direcµia urmat de
robot. Se va determina ³i traseul pe care se obµine recolta maxim .
Datele de intrare
Fi³ierul de intrare sudest.in are urm toarea structur :
Pe urm toarele N linii se a câte N numere naturale, separate prin spaµii, reprezentând
cantitatea de carto din ecare p trat unitate.
Datele de ie³ire
Fi³ierul de iesire sudest.out va conµine pe prima linie cantitatea maxim de carto recoltat
de robot. Pe urm toarele K 1 linii vor scrise, în ordine, coordonatele p tratelor unitate ce
constituie traseul pentru care se obµine cantitate maxim de carto, câte un p trat unitate pe o
linie. Coordonatele scrise pe aceea³i linie vor separate printr-un spaµiu. Primul p trat de pe
traseu va avea coordonatele 11, iar ultimul va avea coordonatele N N . Dac sunt mai multe trasee
pe care se obµine o cantitate maxim de carto recoltat se va a³a unul dintre acestea.
Restricµii ³i preciz ri
5&N & 100
2&K &2N 2
1 & C1 , ..., CK & 10
Cantitatea de carto dintr-un p trat de teren este num r natural între 0 ³i 100.
Pentru ecare set de date de intrare se garanteaz c exist cel puµin un traseu.
Se consider c robotul strânge recolta ³i din p tratul de plecare 1, 1 ³i din cel de sosire
N, N ).
Pentru determinarea corect a cantit µii maxime recoltate se acord 50% din punctajul
alocat testului respectiv; pentru cantitate maxim recoltat ³i traseu corect se acord 100%.
Exemplu
sudest.in sudest.out Explicaµii
6 29 Un alt traseu posibil este:
121041 11 11
133511 31 13
2 2 1 2 1 10 51 15
453926 61 25
113201 65 65
10 2 4 6 5 10 66 66
5 dar costul s u este 1 1 4 1 5 10 22
22141
Timp maxim de execuµie/test: 1 secund
CAPITOLUL 12. ALGORITMI BFS-LEE 127
Indicaµii de rezolvare *
soluµia comisiei
Reprezentarea informaµiilor
N - num rul de linii
K - num rul de comenzi
Parcurg ³irul celor k mut ri. La ecare mutare marchez poziµiile în care pot ajunge la mutarea
respectiv .
Mai exact, parcurg toate poziµiile în care am putut ajunge la pasul precedent (cele marcate în
matricea P corespunz tor cu num rul pasului precedent) ³i pentru ecare poziµie veric dac la
pasul curent pot s execut mutarea la sud.
În caz armativ, veric dac în acest caz obµin o cantitate de carto mai mare decât cea
obµinut pân la momentul curent (dac da, reµin noua cantitate, ³i marchez în matricea P
poziµia în care am ajuns cu indicele mut rii curente).
În mod similar procedez pentru o mutare spre est.
Codul surs *
import java.io.*;
class Sudest1
{
static StreamTokenizer st;
static PrintWriter out;
static final int Nmax=101;
static int N, K;
static int[][] A=new int[Nmax][Nmax]; // A[i][j]=cantitatea de cartofi
static int[][] C=new int[Nmax][Nmax]; // C[i][j]=cantitatea maxima ...
static int[][] P=new int[Nmax][Nmax]; // pas
static int[] Move=new int[2*Nmax]; // comenzile
}// read_data()
static boolean posibil(int x,int y) {return 1<=x && 1<=y && x<=N && y<=N;}
}// class
Variant folosind coad :
p[ii][jj]=(i-1)*n+j;
q[sc]=(ii-1)*n+jj; sc=(sc+1)%qmax; // pun in coada
}
Indicaµii de rezolvare *
Codul surs
import java.io.*;
class Muzeu
{
static final int qdim=200; // dimensiune coada circulara
static final int sus=1, dreapta=2, jos=3, stanga=4;
st.nextToken(); m=(int)st.nval;
st.nextToken(); n=(int)st.nval;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++) { st.nextToken(); x[i][j]=(int)st.nval; }
amAjuns=false;
matriceCosturi();
if(!amAjuns) continue;
if(cc>cmin) continue;
if(cc<cmin) { cmin=cc; ncmin=c[m][n]; copieDirectii(); }
else // costuri egale
if(c[m][n]<ncmin) { ncmin=c[m][n]; copieDirectii(); }
}
int i,j;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++) c[i][j]=0; // curat "numai" traseul !
ic=sc=0; // coada vida
qi[sc]=1; qj[sc]=1; sc=(sc+1)%qdim; // (1,1) --> coada circulara !
c[1][1]=1; // cost 1 pentru pozitia (1,1) (pentru marcaj!)
while(ic!=sc) // coada nevida
{
i=qi[ic]; j=qj[ic]; ic=(ic+1)%qdim;
vecini(i,j);
if(amAjuns) break;
}
}//matriceCosturi()
if((i-1>=1)&&(c[i-1][j]==0)&&amBilet[x[i-1][j]]) // N
{
c[i-1][j]=t+1; qi[sc]=i-1; qj[sc]=j; sc=(sc+1)%qdim;
d[i-1][j]=jos;
if((i-1==m)&&(j==n)) {amAjuns=true; 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; qj[sc]=j+1; sc=(sc+1)%qdim;
d[i][j+1]=stanga;
if((i==m)&&(j+1==n)) {amAjuns=true; return;}
}
if((i+1<=m)&&(c[i+1][j]==0)&&amBilet[x[i+1][j]])// S
{
c[i+1][j]=t+1; qi[sc]=i+1; qj[sc]=j; sc=(sc+1)%qdim;
d[i+1][j]=sus;
if((i+1==m)&&(j==n)) {amAjuns=true; 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; qj[sc]=j-1; sc=(sc+1)%qdim;
d[i][j-1]=dreapta;
if((i==m)&&(j-1==n)) {amAjuns=true; return;}
}
}// vecini(...)
out.println(cmin);
CAPITOLUL 12. ALGORITMI BFS-LEE 134
out.println(ncmin-1);
Un p ianjen a µesut o plas , în care nodurile sunt dispuse sub forma unui caroiaj cu m linii
(numerotate de la 0 la m 1) ³i n coloane (numerotate de la 0 la n 1) ca în gur . Iniµial, oricare
dou noduri vecine (pe orizontal sau vertical ) erau unite prin segmente de plas de lungime 1.
În timp unele porµiuni ale plasei s-au deteriorat, devenind nesigure. Pe plas , la un moment dat,
se g sesc p ianjenul ³i o musc , în noduri de coordonate cunoscute.
0 1 2 3 4 5 6
0
2 pozitie paianzen
7 pozitie musca
Cerinµ
S se determine lungimea celui mai scurt traseu pe care trebuie s -l parcurg p ianjenul,
folosind doar porµiunile sigure ale plasei, pentru a ajunge la musc . De asemenea, se cere un astfel
de traseu.
Datele de intrare
Fi³ierul de intrare paianjen.in conµine:
pe prima linie dou numere naturale m n, separate printr-un spaµiu, reprezentând num rul
de linii ³i respectiv num rul de coloane ale plasei;
pe a doua linie dou numere naturale lp cp, separate printr-un spaµu, reprezentând linia ³i
respectiv coloana nodului în care se a iniµial p ianjenul;
pe linia a treia dou numere naturale lm cm separate printr-un spaµiu, reprezentând linia
³i respectiv coloana pe care se a iniµial musca;
pe linia a patra, un num r natural k , reprezentând num rul de porµiuni de plas deteriorate;
pe ecare dintre urm toarele k linii, câte patru valori naturale l1 c1 l2 c2, separate prin câte
un spaµiu, reprezentând coordonatele capetelor celor k porµiuni de plas deteriorate (linia ³i apoi
coloana pentru ecare cap t).
CAPITOLUL 12. ALGORITMI BFS-LEE 135
Datele de ie³ire
Fi³ierul de ie³ire paianjen.out va conµine pe prima linie un num r natural min reprezentând
lungimea drumului minim parcurs de p ianjen, exprimat în num r de segmente de lungime 1. Pe
urm toarele min 1 linii sunt scrise nodurile prin care trece p ianjenul, câte un nod pe o linie.
Pentru ecare nod sunt scrise linia ³i coloana pe care se a , separate printr-un spaµiu.
Restricµii ³i preciz ri
a 1 & m, n & 140
a 1 & k & 2 m n m n 1
a Lungimea drumului minim este cel mult 15000
a Pentru datele de test exist întotdeauna soluµie. Dac problema are mai multe soluµii, se va
a³a una singur .
a Porµiunile nesigure sunt specicate în ³ierul de intrare într-o ordine oarecare. Oricare dou
porµiuni nesigure orizontale se pot intersecta cel mult într-un cap t. De asemenea, oricare dou
porµiuni nesigure verticale se pot intersecta cel mult într-un cap t.
a Se acord 30% din punctaj pentru determinarea lungimii drumului minim ³i 100% pentru
rezolvarea ambelor cerinµe.
Exemplu
paianjen.in paianjen.out Explicaµie
9 7 8 Problema corespunde gurii de mai sus.
2 3 2 3 Traseul optim este desenat cu linie groas ,
7 4 2 2 iar porµiunile nesigure sunt desenate punctat.
8 3 2
2 4 2 5 4 2
2 3 3 3 5 2
3 0 3 1 6 2
3 3 3 5 6 3
4 4 5 4 7 3
6 4 6 5 7 4
6 5 7 5
7 2 7 3
Timp maxim de execuµie/test: 1 secund pentru Windows ³i 0.1 secunde pentru Linux.
Indicaµii de rezolvare *
TESTE
CAPITOLUL 12. ALGORITMI BFS-LEE 136
# m n k min Obs
0 10 8 11 16 dimensiune mic , re puµine rupte
1 10 8 46 74 soluµie unic , foarte multe re rupte
2 2 2 0 2 caz particular, nici un r rupt
3 140 140 20 278 re puµine rupte, dimensiune mare
4 131 131 2000 103 multe re rupte, dimensiune mare
5 100 130 12771 12999 traseu în spiral solutie unic
6 100 7 23 15 traseu scurt, greu de g sit cu backtracking
7 138 138 9381 9050 multe re rupte, drum în "serpentine"
8 140 140 38365 555 rele interioare rupte, traseul pe frontier
9 138 138 273 274 o "barier " pe mijlocul tablei, cu o "fant "
O soluµie backtracking simplu obµine maxim 20 de puncte, iar îmbun t µit maxim 30 de puncte.
Codul surs
i1=min(l1,l2); i2=max(l1,l2);
j1=min(c1,c2); j2=max(c1,c2);
if((i-1>=0)&&(c[i-1][j]==0)&&ok(i,j,sus)) // N
{
c[i-1][j]=t+1;
qi[sc]=i-1;
qj[sc]=j;
sc=(sc+1)%qdim;
CAPITOLUL 12. ALGORITMI BFS-LEE 138
d[i-1][j]=jos;
}
if((j+1<=n-1)&&(c[i][j+1]==0)&&ok(i,j,dreapta)) // E
{
c[i][j+1]=t+1;
qi[sc]=i;
qj[sc]=j+1;
sc=(sc+1)%qdim;
d[i][j+1]=stanga;
}
if((i+1<=m-1)&&(c[i+1][j]==0)&&ok(i,j,jos)) // S
{
c[i+1][j]=t+1;
qi[sc]=i+1;
qj[sc]=j;
sc=(sc+1)%qdim;
d[i+1][j]=sus;
}
if((j-1>=0)&&(c[i][j-1]==0)&&ok(i,j,stanga)) // V
{
c[i][j-1]=t+1;
qi[sc]=i;
qj[sc]=j-1;
sc=(sc+1)%qdim;
d[i][j-1]=dreapta;
}
}// fill(...)
i=lm;
j=cm;
while((i!=lp)||(j!=cp)) // folosesc matricea c care nu mai e necesara !
if(d[i][j]==sus) { c[i-1][j]=jos; i--; }
else if(d[i][j]==jos) { c[i+1][j]=sus; i++;}
else if(d[i][j]==dreapta) { c[i][j+1]=stanga; j++; }
else if(d[i][j]==stanga) { c[i][j-1]=dreapta; j--; }
CAPITOLUL 12. ALGORITMI BFS-LEE 139
i=lp;
j=cp;
while((i!=lm)||(j!=cm))
{
out.println(i+" "+j);
if(c[i][j]==sus) i--;
else if(c[i][j]==jos) i++;
else if(c[i][j]==dreapta) j++;
else if(c[i][j]==stanga) j--;
else System.out.println("Eroare la traseu ... !");
}
out.println(i+" "+j); // pozitia pentru musca !
out.close();
}//afisSolutia()
}// class
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); c[i][j]=(int)st.nval;
}
fluxm=fluxMax(s,t);
out.print(fluxm); out.close();
}// main()
maxf += min;
System.out.print("drum : ");
drum(t);
System.out.println(" min="+min+" maxf="+maxf+"\n");
}// while(...)
while(ic!=sc)
{
u=dincoada();
if(gasitt) break;
}//while
return gasitt;
}// bfs(...)
/*
6 10 1 6 drum : 1 2 4 6 min=12 maxf=12
1 2 16 drum : 1 3 5 6 min= 4 maxf=16
1 3 13 drum : 1 3 5 4 6 min= 7 maxf=23
2 3 4 Nu mai exista drum de crestere a fluxului !!!
2 4 12 fluxMax(1,6) = 23 :
3 2 10 0 12 11 0 0 0
3 5 14 0 0 0 12 0 0
4 3 9 0 0 0 0 11 0
4 6 20 0 0 0 0 0 19
5 4 7 0 0 0 7 0 4
5 6 4 0 0 0 0 0 0
*/
poate fi amplasat pe un patratel daca exista cel putin o culoare care apare
pe ambele. Ei doresc sa formeze perechi din care fac parte un cerculet si un
patratel astfel incat sa se obtina cat mai multe perechi.
f=new int[n+m+2][n+m+2];
p=new int[n+m+2];
q=new int[n+m+2];
color=new int[n+m+2];
for(i=0;i<=n+m+1;i++)
for(j=0;j<=n+m+1;j++) f[i][j]=0;
while(bfs(s,t))
{
min=oo;
for(u=t;p[u]!=-1;u=p[u]) min=minim(min,c[p[u]][u]-f[p[u]][u]);
for(u=t;p[u]!=-1;u=p[u])
{
f[p[u]][u]+=min;
f[u][p[u]]-=min; // sau f[u][p[u]]=-f[p[u]][u];
}
CAPITOLUL 12. ALGORITMI BFS-LEE 144
maxf+=min;
}// while(...)
return maxf;
}// fluxMax(...)
return gasitt;
}// bfs()
1. Se pleac de la soluµia vid pentru multimea S ³i se ia pe rând câte un element din mulµimea
C . Dac elementul ales îndepline³te condiµia de optim local, el este introdus în mulµimea S .
2. Se ordoneaz elementele mulµimii C ³i se veric dac un element îndepline³te condiµia de
apartenenµ la mulµimea S .
o funcµie care veric dac o anumit mulµime de candidaµi constituie o soluµie posibil , nu
neap rat optim , a problemei
o funcµie care veric dac o mulµime de candidaµi este fezabil , adic dac este posibil s
complet m aceast mulµime astfel încât s obµinem o soluµie posibil , nu neap rat optim ,
a problemei
o funcµie de selecµie care indic la orice moment care este cel mai promiµ tor dintre candidaµii
înc nefolosiµi
o funcµie obiectiv care d valoarea unei soluµii (timpul necesar execut rii tuturor lucr rilor
într-o anumit ordine, lungimea drumului pe care l-am g sit, etc) ³i pe care urm rim s o
optimiz m (minimiz m/maximiz m)
Pentru a rezolva problema de optimizare, c ut m o soluµie posibil care s optimizeze valoarea
funcµiei obiectiv.
146
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 147
13.3 Exemple
Dintre problemele clasice care se pot rezolva prin metoda greedy menµion m: plata restului
cu num r minim de monezi, problema rucsacului, sortare prin selecµie, determinarea celor mai
scurte drumuri care pleac din acela³i punct (algoritmul lui Dijkstra), determinarea arborelui de
cost minim (algoritmii lui Prim ³i Kruskal), determinarea mulµimii dominante, problema color rii
intervalelor, codicarea Human, etc.
Se consider n obiecte. Obiectul i are greutatea gi ³i valoarea vi (1 & i & n). O persoan are
un rucsac. Fie G greutatea maxim suportat de rucsac. Persoana în cauz dore³te s pun în
rucsac obiecte astfel încât valoarea celor din rucsac s e cât mai mare. Se permite fracµionarea
obiectelor (valoarea p rµii din obiectul fracµionat ind direct proporµional cu greutatea ei!).
Not m cu xi " 0, 1 partea din obiectul i care a fost pus în rucsac. Practic, trebuie s
maximiz m funcµia
=x c .
n
f x i i
i 1
Pentru rezolvare vom folosi metoda greedy. O modalitate de a ajunge la soluµia optim este
de a considera obiectele în ordinea descresc toare a valorilor utilit µilor lor date de raportul vgi
i
(i 1, ..., n) ³i de a le înc rca întregi în rucsac pân când acesta se umple. Din aceast cauz
presupunem în continuare c
v1 v2 vn
g ' g ' ... ' g .
1 2 n
then Gp G p gi
else
xi Gp
gi
for ji 1, n
xj 0
Vectorul x are forma x 1, ..., 1, xi , 0, ..., 0 cu xi " 0, 1 ³i 1 & i & n.
Propoziµia 1. Procedura rucsac furnizeaz o soluµie optim .
= =L
n k
1
f p n p i
k 1i 1
Se dore³te determinarea unei permut ri care s asigure o valoare minim a timpului mediu de
citire. Rezolvarea problemei este foarte simpl :
optim . Aplicând de un num r nit de ori acest raµionament, trecem de la o permutare optim
la alt permutare optim pân ajungem la permutarea identic . Rezult c permutarea identic
corespunde unei plas ri optime.
Presupunând c L1 & L2 & ... & Ln , se poate observa c textul Ti va plasat pe banda
i im & m în continuarea celor aate deja pe aceast band iar dup textul Ti , pe aceea³i band
1
Se dau mulµimile de numere întregi A ra1 , a2 , ..., an x ³i B rb1 , b2 , ..., bm x, unde n & m.
¬
S se determine o submulµime B rx1 , x2 , ..., xn x a lui B astfel încât valoarea expresiei E
a1 x1 a2 x2 an xn s e maxim .
Vom sorta cresc tor elementele celor dou mulµimi. Putem presupune acum c a1 & a2 & ... &
an ³i b1 & b2 & ... & bm .
Dac toate elementele din A sunt pozitive vom lua xn bm , xn1 bm1 , ³i a³a mai departe.
Dac toate elementele din A sunt negative vom lua x1 b1 , x2 b2 , ³i a³a mai departe.
Dac primele n1 elemente din A sunt strict negative ³i ultimele n2 elemente din A sunt pozitive
sau nule (n1 n2 n) atunci vom lua x1 b1 , x2 b2 , ..., xn1 bn1 ³i xn bm , xn1 bm1 , ...,
xnn2 1 bmn2 1 .
a se alege staµia 1,
a se parcurge ³irul staµiilor ³i se alege prima staµie întâlnit care este la distanµ cel
puµin d faµ de staµia aleas anterior; se repet acest pas pân când s-au vericat toate
staµiile.
Demonstraµie: Fie B rai1 , ai2 , ..., aim x o soluµie a problemei care nu conµine staµia 1 (deci
ai1 % a1 ). Evident ¶ai2 ai1 ¶ ' d. Staµia i1 se poate înlocui cu staµia 1 pentru c ¶ai2 a1 ¶
¶ai2 ai1 ai1 a1 ¶ ¶ai2 ai1 ¶ ¶ai1 a1 ¶ % d.
Dup selectarea staµie 1 se pot selecta (pentru obµinerea unei soluµii optime) numai staµii
situate la distanµe cel puµin d fat de staµia 1. Pentru aceste staµii repet m strategia sugerat de
propoziµia anterioar .
Vom proceda astfel: cutia goal din conguraµia iniµial se a pe poziµia 3 dar pe aceast
poziµie, în conguraµia nal , se a num rul 3; c ut m num rul 3 din conguraµia iniµial (îl
g sim pe poziµia 1) ³i îl mut m pe poziµia cutiei goale; acum, cutia goal se a pe poziµia 1; vom
repeta acest raµionament pân când poziµiile cutiilor goale, în cele dou conguraµii, coincid.
1 2 3 4 5 6 7
modicat 1 3 2 6 5 4
nal 1 2 3 4 5 6
rezolvat
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 150
1 2 3 4 5 6 7
modicat 1 3 2 6 5 4
nal 1 2 3 4 5 6
rezolvat
1 2 3 4 5 6 7
modicat 1 2 3 6 5 4
nal 1 2 3 4 5 6
rezolvat
Acum vom c uta o cutie nerezolvat ³i vom muta num rul din acea cutie în cutia goal .
1 2 3 4 5 6 7
modicat 1 2 3 6 5 4
nal 1 2 3 4 5 6
rezolvat
Repet m raµionamentul prezentat la început ³i vom continua pân când toate numerele ajung
pe poziµiile din conguraµia nal .
1 2 3 4 5 6 7
modicat 1 2 3 6 4 5
nal 1 2 3 4 5 6
rezolvat
1 2 3 4 5 6 7
modicat 1 2 3 4 5 6
nal 1 2 3 4 5 6
rezolvat
S se descompun un ³ir de n numere întregi în sub³iruri strict cresc toare astfel încât numerele
lor de ordine din ³irul iniµial s e ordonate cresc tor în sub³irurile formate ³i num rul sub³irurilor
s e minim.
Metota de rezolvare este urm toarea: se parcurg elementele ³irului iniµial unul dup altul ³i
pentru ecare element xi se caut un sub³ir existent la care xi se poate ad uga la sfâr³it; dac
nu exist un astfel de sub³ir se creeaz un sub³ir nou care va conµine ca prim element pe xi ; dac
exist mai multe sub³iruri la care se poate ad uga xi , se va alege acela care are cel mai mare ultim
element.
Observaµie: Ultimele elemente din ecare sub³ir (care sunt ³i elemente maxime din aceste
sub³iruri) formeaz un ³ir descresc tor. Acest fapt permite utilizarea c ut rii binare pentru
determinarea sub³irului potrivit pentru elementul xi atunci când prelucr m acest element.
Se consider n intervale închise pe axa real a1 , b1 , a2 , b2 , ..., an , bn . Se cere selectarea
unor intervale disjuncte astfel încât num rul acestora s e maxim.
Metoda de rezolvare este urm toarea: se sorteaz intervalele cresc tor dup cap tul din
dreapta; se selecteaz primul interval; se parcurg intervalele, unul dup altul, pân se g se³te
un interval ai , bi disjunct cu ultimul interval ales ³i se adaug acest interval la soluµie, el deve-
nind astfel "ultimul interval ales"; acest procedeu continu pân când nu mai r mân intervale de
analizat.
Propoziµia 4. Exist o soluµie optim care conµine primul interval dup sortare.
Se dau dou ³iruri cu câte 2n numere întregi ecare, x x1 , x2 , ..., x2n ³i y y1 , y2 , ..., y2n
reprezentând taxe. S se determine ³irul z z1 , z2 , ..., z2n , unde zi " rxi , yi x 1 & i & 2n, astfel
încât suma tuturor elementelor din ³irul z s e minim ³i acest ³ir s conµin exact n elemente
din ³irul x ³i n elemente din ³irul y .
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 151
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
cost=new int[n+1][n+1];
esteInArbore=new boolean [n+1];
p=new int[n+1];
esteInArbore[nods]=true;
for(k=1;k<=n-1;k++) // sunt exact n-1 muchii in arbore !!!
O(n)
{
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 152
min=oo;
for(i=1;i<=n;i++) // O(n)
{
if(!esteInArbore[i]) continue;
for(j=1;j<=n;j++) // O(n)
{
if(esteInArbore[j]) continue;
if(min>cost[i][j]) { min=cost[i][j]; imin=i; jmin=j; }
}//for j
}//for i
esteInArbore[jmin]=true;
p[jmin]=imin;
costArbore+=min;
}//for k
/*
6 7 1 3
1 2 3 2 3
1 3 1 4 3
2 3 2 5 4
3 4 1 6 4
4 5 1 cost=7
5 6 3
4 6 2
*/
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
cost=new int[n+1][n+1];
esteInArbore=new boolean [n+1];
p=new int[n+1];
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 153
d=new int[n+1];
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cost[i][j]=oo;
for(i=1;i<=n;i++) d[i]=oo;
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost[i][j]=cost[j][i]=(int)st.nval;
}
d[nodStart]=0;
for(k=1;k<=n;k++) // O(n)
{
min=oo;
for(j=1;j<=n;j++) // O(n)
{
if(esteInArbore[j]) continue;
if(min>d[j]) { min=d[j]; jmin=j; }
}//for j
esteInArbore[jmin]=true;
d[jmin]=0;
costArbore+=min;
/*
6 7 1 3
1 2 3 2 3
1 3 1 4 3
2 3 2 5 4
3 4 1 6 4
4 5 1 cost=7
5 6 3
4 6 2
*/
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
st.nextToken(); nods=(int)st.nval;
w=new int[n+1][n+1];
for(i=1;i<=n;i++) for(j=1;j<=n;j++) w[i][j]=oo;
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost=(int)st.nval;
w[j][i]=w[i][j]=cost;
}
prim(nods);
for(u=1;u<=n;u++)
{
hnod[u]=pozh[u]=u;
hd[u]=d[u]=oo;
p[u]=0;
}
d[nods]=0;
hd[pozh[nods]]=0;
urcInHeap(pozh[nods]);
while(q>0) // la fiecare pas adaug un varf in arbore
{
u=extragMin(q);
if(u==-1) { System.out.println("graf neconex !!!"); break; }
q--;
for(v=1;v<=q;v++) // noduri nefinalizate = in heap 1..q
if(w[u][hnod[v]]<oo) // cost finit ==> exista arc (u,v)
relax(u,hnod[v]); // relax si refac heap
}
}//prim(...)
int aux,fiu1,fiu2,fiu,tata,nod;
pozh[hnod[q]]=q;
pozh[hnod[1]]=1;
tata=1;
fiu1=2*tata;
fiu2=2*tata+1;
if(hd[tata]<=hd[fiu])
break;
pozh[hnod[fiu]]=tata;
pozh[hnod[tata]]=fiu;
tata=fiu;
fiu1=2*tata;
fiu2=2*tata+1;
}
nod=hnod[nodh];
fiu=nodh;
tata=fiu/2;
while((tata>0)&&(hd[fiu]<hd[tata]))
{
pozh[hnod[tata]]=fiu;
hnod[fiu]=hnod[tata];
fiu=tata;
tata=fiu/2;
}
pozh[nod]=fiu;
hnod[fiu]=nod;
}
}//class
/*
6 7 3
1 2 3 3 1
1 3 1 3 2
2 3 2 3 4
3 4 1 4 5
4 5 1 4 6
5 6 3 costa=7
4 6 2
*/
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 157
x=new int[m+1];
y=new int[m+1];
z=new int[m+1];
et=new int[n+1];
for(k=1;k<=m;k++)
{
st.nextToken(); x[k]=(int)st.nval;
st.nextToken(); y[k]=(int)st.nval;
st.nextToken(); z[k]=(int)st.nval;
}
kruskal();
System.out.println("cost="+cost);
out.println(cost);
out.close();
}//main
for(k=1;k<=n;k++) et[k]=k;
qsort(1,m,z);
for(k=1;k<=m;k++)
{
if(et[x[k]]!=et[y[k]])
{
nm++;
cost+=z[k];
System.out.println(x[k]+" "+y[k]);
etg1=et[x[k]];
etg2=et[y[k]];
for(int i=1;i<=n;i++)
if(et[i]==etg2) et[i]=etg1;
}
if(nm==n-1)break;
}
}//kruskal
i=p; j=u;
while(i<j)
{
while((z[i]<=z[j])&&(i<j)) i++;
while((z[i]<=z[j])&&(i<j)) j--;
if(i<j) { invers(i,j,z); invers(i,j,x); invers(i,j,y); }
}
return i; //i==j
}//poz
}//class
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
cost=new int[n+1][n+1];
esteFinalizat=new boolean [n+1];
p=new int[n+1];
d=new int[n+1];
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost[i][j]=cost[j][i]=(int)st.nval;
}
d[nodSursa]=0;
for(k=1;k<=n;k++) // O(n)
{
min=oo;
for(j=1;j<=n;j++) // O(n)
{
if(esteFinalizat[j]) continue;
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 159
st.nextToken(); m=(int)st.nval;
w=new int[n+1][n+1];
for(i=1;i<=n;i++) for(j=1;j<=n;j++) w[i][j]=oo;
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost=(int)st.nval;
w[j][i]=w[i][j]=cost;
}
nodSursa=1;
dijkstra(nodSursa);
for(k=1;k<=n;k++)
{
System.out.print(nodSursa+"-->"+k+" dist="+d[k]+" drum: ");
drum(k);
System.out.println();
}
out.close();
}//main
for(u=1;u<=n;u++)
{
hnod[u]=pozh[u]=u;
hd[u]=d[u]=oo;
p[u]=0;
}
q=n; // q = noduri nefinalizate = dimensiune heap
d[nods]=0;
hd[pozh[nods]]=0;
urcInHeap(pozh[nods]);
d[v]=d[u]+w[u][v];
p[v]=u;
hd[pozh[v]]=d[v];
urcInHeap(pozh[v]);
}
}// relax(...)
tata=1;
fiu1=2*tata;
fiu2=2*tata+1;
pozh[hnod[fiu]]=tata;
pozh[hnod[tata]]=fiu;
tata=fiu;
fiu1=2*tata;
fiu2=2*tata+1;
}
nod=hnod[nodh];
fiu=nodh;
tata=fiu/2;
while((tata>0)&&(hd[fiu]<hd[tata]))
{
pozh[hnod[tata]]=fiu;
hnod[fiu]=hnod[tata];
aux=hd[fiu]; hd[fiu]=hd[tata]; hd[tata]=aux;
fiu=tata;
tata=fiu/2;
}
pozh[nod]=fiu;
hnod[fiu]=nod;
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 162
} // urcInHeap(...)
/*
6 7 1-->1 dist=0 drum: 1
1 2 4 1-->2 dist=3 drum: 1 3 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=3 drum: 1 3 4 5
5 4 1 1-->6 dist=4 drum: 1 3 4 6
5 6 3
4 6 2
*/
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
cost=new int[n+1][n+1];
esteFinalizat=new boolean [n+1];
p=new int[n+1];
d=new int[n+1];
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cost[i][j]=oo;
for(i=1;i<=n;i++) d[i]=oo;
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost[i][j]=(int)st.nval;
}
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 163
d[nodSursa]=0;
for(k=1;k<=n;k++) // O(n)
{
min=oo;
for(j=1;j<=n;j++) // O(n)
{
if(esteFinalizat[j]) continue;
if(min>d[j]) { min=d[j]; jmin=j; }
}//for j
esteFinalizat[jmin]=true;
for(j=1;j<=n;j++) // actualizez distantele nodurilor // O(n)
{
if(esteFinalizat[j]) continue; // 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];
p[j]=jmin;
}
}
}//for k
for(k=1;k<=n;k++)
{
System.out.print(nodSursa+"-->"+k+" dist="+d[k]+" drum: ");
if(d[k]<oo) drum(k); else System.out.print("Nu exista drum!");
System.out.println();
}
out.close();
}//main
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
w=new int[n+1][n+1];
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
w[i][j]=oo;
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost=(int)st.nval;
w[i][j]=cost;
}
nodSursa=1;
dijkstra(nodSursa);
for(k=1;k<=n;k++)
{
if(d[k]<oo)
{
System.out.print(nodSursa+"-->"+k+" dist="+d[k]+" drum: ");
drum(k);
}
else System.out.print(nodSursa+"-->"+k+" Nu exista drum! ");
System.out.println();
}
out.close();
}//main
urcInHeap(pozh[nods]);
q--;
for(v=1;v<=q;v++) // noduri nefinalizate = in heap 1..q
if(w[u][hnod[v]]<oo) // cost finit ==> exista arc (u,v)
relax(u,hnod[v]); // relax si refac heap
}
}//dijkstra(...)
tata=1;
fiu1=2*tata;
fiu2=2*tata+1;
while(fiu1<=q-1) //refac heap de sus in jos pana la q-1
{
fiu=fiu1;
if(fiu2<=q-1)
if(hd[fiu2]<hd[fiu1]) fiu=fiu2;
if(hd[tata]<=hd[fiu]) break;
pozh[hnod[fiu]]=tata;
pozh[hnod[tata]]=fiu;
aux=hd[fiu]; hd[fiu]=hd[tata]; hd[tata]=aux;
aux=hnod[fiu]; hnod[fiu]=hnod[tata]; hnod[tata]=aux;
tata=fiu;
fiu1=2*tata;
fiu2=2*tata+1;
}
nod=hnod[nodh];
fiu=nodh;
tata=fiu/2;
while((tata>0)&&(hd[fiu]<hd[tata]))
{
pozh[hnod[tata]]=fiu;
hnod[fiu]=hnod[tata];
fiu=tata;
tata=fiu/2;
}
pozh[nod]=fiu;
hnod[fiu]=nod;
} // urcInHeap(...)
/*
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 Nu exista drum!
5 4 1 1-->6 dist=4 drum: 1 3 4 6
5 6 3
4 6 2
*/
Date de ie³ire
Fi³ierul de ie³ire URGENTA.OUT va avea urm torul format:
gravmax - gravitatea maxim
C - num rul de c i de acces întrerupte de calamitate
k1 h1 - între punctele k1 ³i h1 a fost întrerupt calea de acces
k2 h2 - între punctele k2 ³i h2 a fost întrerupt calea de acces
...
kC hC - între punctele kC ³i hC a fost întrerupt calea de acces
Restricµii ³i preciz ri
0 $ N $ 256
N 2 $ M $ 32385
0$K $N 1
Priorit µile c ilor de acces sunt întregi strict pozitivi mai mici decât 256.
Un grup de puncte poate conµine între 1 ³i N puncte inclusiv.
Dac exist mai multe soluµii, programul va determina una singur .
Exemplu
URGENTA.IN URGENTA.OUT
7 11 4 27
121 8
132 13
173 17
243 24
342 34
351 37
361 45
375 56
455 67
564
673
Timp maxim de executare: 1 secund / test
Codul surs
import java.io.*; // arbore minim de acoperire: algoritmul lui Prim O(n^2)
class Urgenta // sortare O(n^2) ... si una slaba merge!
{
static final int oo=0x7fffffff;
static int n,m,ncc,gravmax,costmax,nrm; // ncc = nr componente conexe
static int[][] cost;
static boolean[] esteInArbore;
static int[] p; // predecesor in arbore
static int[] d; // distante de la nod catre arbore
static int[] a1; // a1[k]=varful 1 al muchiei k din arbore
static int[] a2; // a2[k]=varful 2 al muchiei k din arbore
static int[] ac; // a1[k]=costul muchiei k din arbore
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
st.nextToken(); ncc=(int)st.nval;
cost=new int[n+1][n+1];
esteInArbore=new boolean [n+1];
p=new int[n+1];
d=new int[n+1];
a1=new int[n];
a2=new int[n];
ac=new int[n];
costmax=0;
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost[i][j]=cost[j][i]=(int)st.nval;
costmax+=cost[i][j];
}
// alg Prim
d[nodStart]=0;
for(k=1;k<=n;k++) // O(n)
{
min=oo;
for(j=1;j<=n;j++) // O(n)
{
if(esteInArbore[j]) continue;
if(min>d[j]) { min=d[j]; jmin=j; }
}//for j
esteInArbore[jmin]=true;
d[jmin]=0;
costArbore+=min;
k=0;
for(i=1;i<=n;i++)
if(p[i]!=0)
{
//System.out.println(i+" "+p[i]+" --> "+cost[i][p[i]]);
k++;
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 169
a1[k]=i;
a2[k]=p[i];
ac[k]=cost[i][p[i]];
}
//System.out.println("cost="+costArbore);
//System.out.println("gravmax ="+gravmax);
out.println(gravmax);
out.close();
}//main
}//class
Restricµii ³i preciz ri
a 1&N & 8000
a 100 & minx & maxx & 100 (numere întregi, reprezentând grade Celsius), pentru orice x de
la 1 la N
a un frigider poate conµine un num r nelimitat de reactivi
Exemple
react.in react.out react.in react.out react.in react.out
3 2 4 3 5 2
-10 10 25 -10 10
-25 57 10 12
20 50 10 20 -20 10
30 40 7 10
78
Timp maxim de execuµie: 1 secund /test
Facem o prim observaµie: pentru orice soluµie optim exist o soluµie cu acela³i num r de
puncte (frigidere), în care ecare punct s e sfâr³itul unui interval. Aceasta se poate obµine
mutând ecare punct spre dreapta, pân când ar ajunge la sfâr³itul intervalului care se termin
cel mai repede, dintre intervalele care îl conµin. Se observ c noua soluµie respect restricµiile din
enunµ.
În continuare ne vom concentra pe g sirea unei soluµii de acest tip.
Sort m reactivii dup sfâr³itul intervalului. Pentru intervalul care se termin cel mai repede,
alegem ultimul punct al s u ca temperatur a unui frigider. Se observ c aceast alegere este
cea mai bun , dintre toate alegerile unui punct în intervalul respectiv, în sensul c mulµimea
intervalelor care conµin punctul este mai mare (conform relaµiei de incluziune), decât mulµimea
corespunz toare oric rei alte alegeri. Acest fapt este adev rat, deoarece mutarea punctului mai la
stânga nu duce la selectarea unor noi intervale.
Intervalele care conµin punctul respectiv sunt eliminate (reactivii corespunz tori pot plasaµi
într-un frigider), iar procesul continu cu intervalele r mase, în acela³i mod.
Analiza complexit µii
Not m cu D num rul de temperaturi întregi din intervalul care conµine temperaturile din
enunµ. Se observ c D este cel mult 201.
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 171
Codul surs
import java.io.*;
class Reactivi
{
static int n; // n=nr reactivi
static int ni=0; // ni=nr interschimbari in quickSort
static int nf=0; // n=nr frigidere
static int[] ngf; // ng=nr grade in frigider
static int[] x1,x2; // limite inferioara/superioara
i=p; j=u;
while(i<j)
{
while((i<j)&&((x2[i]<x2[j])||
((x2[i]==x2[j])&&(x1[i]>=x1[j])))) i++;
if(i!=j)
{
aux=x1[i]; x1[i]=x1[j]; x1[j]=aux;
aux=x2[i]; x2[i]=x2[j]; x2[j]=aux;
}
while((i<j)&&((x2[i]<x2[j])||
((x2[i]==x2[j])&&(x1[i]>=x1[j])))) j--;
if(i!=j)
{
aux=x1[i]; x1[i]=x1[j]; x1[j]=aux;
aux=x2[i]; x2[i]=x2[j]; x2[j]=aux;
}
}
if(p<i-1) quickSort(p,i-1);
if(i+1<u) quickSort(i+1,u);
}
}
a Pentru 30% dintre teste, num rul total de cifre va cel mult 7; pentru alte 40% din teste
num rul total de cifre va cel mult 18, iar pentru restul de 30% din teste num rul total de cifre
va mai mare sau egal cu 30.
a Fiecare linie din ³ierul de intrare ³i din ³ierul de ie³ire se termin cu marcaj de sfâr³it de
linie.
Exemplu
pal.in pal.out Explicaµie
5 10001 Pentru acest exemplu avem L 5,
3 222 3 cifre de 0, 2 cifre de 1³i 3 cifre de 2.
2 Cifrele de la 3 la 9 lipsesc
3 de pe u³a turnului.
0
0 Cele dou palindroame cu care
0 se genereaz combinaµia magic
0 sunt 10001 ³i 222.
0 Combinaµia magic va suma acestora
0 ³i anume 10223 (care este suma minim
0 pe care o putem obµine).
Timp maxim de execuµie/test: 1 sec sub Windows ³i 1 sec sub Linux
Codul surs
st.nextToken();L=(int)st.nval;
for(i=0;i<=9;i++) { st.nextToken(); fc[i]=(int)st.nval;}
nc1=L;
nc2=NC-nc1;
while(nc1<nc2) {nc1++; nc2--;}
if((ncfi==2)&&(nc1%2==0)) {nc1++; nc2--;}
p1=new int[nc1];
p2=new int[nc2];
switch(ncfi)
{
case 0: impare0(); break;
case 1: impare1(); break;
case 2: impare2(); break;
default: System.out.println("Date de intrare eronate!");
}
corectez(p1);
corectez(p2);
for(i=0;i<p1.length;i++) out.print(p1[i]);
out.println();
for(i=0;i<p2.length;i++) out.print(p2[i]);
out.println();
int[] p12=suma(p1,p2);// pentru ca in teste rezultat = suma!
for(i=p12.length-1;i>=0;i--) out.print(p12[i]);
out.println();
out.close();
}//main
for(i=0;i<=9;i++)
if(fc[i]%2==1) { p1[imp1]=i; fc[i]--; break;}
for(i=0;i<=9;i++)
if(fc[i]%2==1) { p2[imp2]=i; fc[i]--; break;}
k1=0;
k2=0;
while(k1<nc1-nc2) {incarcPozitia(k1,p1); k1++;} // incarc numai p1
while((k1<imp1)||(k2<imp2))
{
if(k1<imp1) {incarcPozitia(k1,p1); k1++;}
if(k2<imp2) {incarcPozitia(k2,p2); k2++;}
}
}
for(i=0;i<=9;i++)
if(fc[i]%2==1) { p1[imp1]=i; fc[i]--; break;}
for(i=0;i<=9;i++)
if(fc[i]%2==1) { p2[imp2]=i; fc[i]--; break;}
k1=0;
k2=0;
while(k1<nc1-nc2) {incarcPozitia(k1,p1); k1++;}// incarc numai p1
while((k1<imp1)||(k2<imp2))
{
if(k1<imp1) { incarcPozitia(k1,p1); k1++; }
if(k2<imp2) { incarcPozitia(k2,p2); k2++; }
}
}
x[k]=i; fc[i]--;
x[n-k-1]=i; fc[i]--;
break;
}
}
Cei n deµinuµi ai unei închisori, numerotaµi de la 1 la n, trebuie s sape un ³anµ dispus în linie
dreapt între dou puncte A ³i B , situate la distanµa de 250 km unul de cel lalt, pe care exist
251 borne kilometrice numerotate de la 0 la 250. Fiecare deµinut are îns o pretenµie, "e doar
democraµie, nu?": el dore³te s sape doar undeva în zona dintre borna x ³i borna y . Pe lâng
aceste pretenµii închisoarea se confrunt ³i cu o alt problem : nu are sucienµi paznici angajaµi.
Cerinµ
Cunoscându-se num rul n de deµinuµi ³i pretenµiile lor, s se determine locul (locurile) unde
vor pu³i deµinuµii s sape într-o zi de munc , respectându-se pretenµiile lor, astfel încât num rul
de paznici necesari pentru a p zi cei n deµinuµi s e minim. Intervalul în care poate p zi un
paznic nu poate conµine dou sau mai multe zone disjuncte dintre cele exprimate de deµinuµi în
preferinµele lor.
Date de intrare
Fi³ierul de intrare sant.in are pe prima linie num rul n de deµinuµi. Pe ecare dintre urm -
toarele n linii exist câte dou numere naturale, ai bi , separate printr-un spaµiu (ai & bi ), care
reprezint pretenµia deµinutului. Mai exact pe linia i 1 (1 & i & n) este descris pretenµia
deµinutului cu num rul de ordine i.
Date de ie³ire
Fi³ierul de ie³ire sant.out va conµine pe prima linie num rul natural k reprezentând num rul
minim de paznici necesari pentru paza celor n deµinuµi sco³i la lucru. Pe urm toarele 2k linii vor
descrise locurile unde vor pu³i s sape deµinuµii, astfel: ecare pereche de linii 2j, 2j 1
va conµine pe linia 2j trei numere naturale p xj yj , separate prin câte un spaµiu reprezentând
num rul de ordine al paznicului ³i bornele kilometrice xj c si yj unde va p zi paznicul p, iar pe
linia 2j 1 vor scrise numerele de ordine ale deµinuµilor care sap în aceast zon , separate prin
câte un spaµiu, ordonate cresc tor.
Restricµii ³i preciz ri
1 & n & 10000
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 177
Exemplu
.in .out Explicaµie
3 2 sunt necesari 2 paznici: paznicul 1 va p zi între
0 20 1 8 13 borna 8 ³i borna 13, iar deµinuµii p ziµi sunt 1 ³i 2;
8 13 1 2 paznicul 2 va p zi între borna 30 ³i borna 60, iar
30 60 2 30 60 deµinutul p zit este 3
3
4 3 sunt necesari 3 paznici: paznicul 1 va p zi între
10 203 1 10 20 borna 10 ³i borna 20, iar deµinutul p zit este 1;
2 53 1 paznicul 2 va p zi la borna 5, iar deµinuµii p ziµi
30 403 2 55 sunt 2 ³i 4; paznicul 3 va p zi între borna 30 ³i
5 7 33 2 4 borna 40, iar deµinutul p zit este 3
3 30 40
3
5 2 sunt necesari 2 paznici: paznicul 1 va p zi la
10 30 1 30 30 borna 30, iar deµinuµii p ziµi sunt 1, 2, 3 ³i 4;
30 32 1 234 paznicul 2 va p zi între borna 27 ³i borna 28,
0 30 2 27 28 iar deµinutul p zit este 5
27 30 5
27 28 !Soluµia nu este unic !
Soluµia comisiei
Problema cere, de fapt, determinarea num rului minim de intersecµii între segmentele deter-
minate de kilometrul minim ³i maxim între care sap un deµinut.
Pentru a le determina procedez astfel:
ordonez intervalele cresc tor dup kilometrul minim ³i descresc tor dup kilometrul maxim
pun primul deµinut (deci cel cu intervalul de s pare cel mai mare) în grija primului paznic
caut un paznic care mai p ze³te deµinuµi ³i care poate p zi ³i acest deµinut (adic
intersecµia segmentelor s e nevid )
dac g sesc
ajustez intervalul de s pare ca s poat s pa ³i acest deµinut
altfel (dac nu g sesc)
mai am nevoie de un paznic ³i îl dau în grija acestuia
Datorit faptului c intervalele sunt sortate, cresc tor dup cap tul inferior, în momentul când
un interval nu se mai intersecteaz cu cel deja determinat, este sigur c nici un alt interval ce îl
urmeaz nu se mai intersecteaz , lucru ce asigur determinarea num rului minim de paznici.
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 178
Codul surs
import java.io.*;
class Sant
{
static int n;
static int[] x1=new int[10001];
static int[] x2=new int[10001];
static int[] o=new int[10001];
i=li;
j=ls;
val=x2[(i+j)/2];
while(i<j)
{
while(x2[i]<val) i++;
while(x2[j]>val) j--;
if(i<=j)
{
aux=x1[i]; x1[i]=x1[j]; x1[j]=aux;
aux=x2[i]; x2[i]=x2[j]; x2[j]=aux;
aux=o[i]; o[i]=o[j]; o[j]=aux;
i++;
j--;
}
}// while
if(li<j) qsort(li,j);
if(i<ls) qsort(i,ls);
}// qsort(...)
st.nextToken(); n=(int)st.nval;
for(k=1;k<=n;k++)
{
st.nextToken(); x1[k]=(int)st.nval;
st.nextToken(); x2[k]=(int)st.nval;
o[k]=k;
}
qsort(1,n);
np=1;
xp=x2[1];
for(k=2;k<=n;k++) if(x1[k]>xp) { np++; xp=x2[k]; }
out.println(np);
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 179
În Roma antic exist n a³ez ri senatoriale distincte, câte una pentru ecare dintre cei n
senatori ai Republicii. A³ez rile senatoriale sunt numerotate de la 1 la n, între oricare dou
a³ez ri existând leg turi directe sau indirecte. O leg tur este direct dac ea nu mai trece
prin alte a³ez ri senatoriale intermediare. Edilii au pavat unele dintre leg turile directe dintre
dou a³ez ri (numind o astfel de leg tur pavat "strad "), astfel încât între oricare dou a³ez ri
senatoriale s existe o singur succesiune de str zi prin care se poate ajunge de la o a³ezare
senatorial la cealalt .
Toµi senatorii trebuie s participe la ³edinµele Senatului. În acest scop, ei se deplaseaz cu
lectica. Orice senator care se deplaseaz pe o strad pl te³te 1 ban pentru c a fost transportat
cu lectica pe acea strad .
La alegerea sa ca prim consul, Cezar a promis c va dota Roma cu o lectic gratuit care s
circule pe un num r de k str zi ale Romei astfel încât orice senator care va circula pe str zile
respective, s poat folosi lectica gratuit f r a pl ti. Str zile pe care se deplaseaz lectica
gratuit trebuie s e legate între ele (zborul, metroul sau teleportarea neind posibile la acea
vreme).
În plus, Cezar a promis s stabileasc sediul s lii de ³edinµe a Senatului într-una dintre a³ez rile
senatoriale aate pe traseul lecticii gratuite. Problema este de a alege cele k str zi ³i amplasarea
sediului s lii de ³edinµe a Senatului astfel încât, prin folosirea transportului gratuit, senatorii, în
drumul lor spre sala de ³edinµe, s fac economii cât mai însemnate. În calculul costului total de
transport, pentru toµi senatorii, Cezar a considerat c ecare senator va c l tori exact o dat de
la a³ezarea sa pân la sala de ³edinµe a Senatului.
Cerinµ
Scrieµi un program care determin costul minim care se poate obµine prin alegerea adecvat
a celor k str zi pe care va circula lectica gratuit ³i a locului de amplasare a s lii de ³edinµ a
Senatului.
Date de intrare
Fi³ierul cezar.in conµine
a pe prima linie dou valori n k separate printr-un spaµiu reprezentând num rul total de
senatori ³i num rul de strazi pe care circul lectica gratuit
a pe urm torele n 1 linii se a câte dou valori i j separate printr-un spaµiu, reprezentând
numerele de ordine a dou a³ez ri senatoriale între care exist strad .
Date de ie³ire
Pe prima linie a ³ierului cezar.out se va scrie costul total minim al transport rii tuturor
senatorilor pentru o alegere optim a celor k str zi pe care va circula lectica gratuit ³i a locului
unde va amplasat sala de ³edinµe a Senatului.
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 180
Restricµii ³i preciz ri
a 1 $ n & 10000, 0 $ k $ n
a 1 & i, j & n , i j j
a Oricare dou perechi de valori de pe liniile 2, 3, ..., n din ³ierul de intrare reprezint dou
str zi distincte.
a Perechile din ³ierul de intrare sunt date astfel încât respect condiµiile din problem .
a Pentru 25% din teste n & 30, pentru 25% din teste 30 $ n & 1000, pentru 25% din teste
1000 $ n & 3000, pentru 10% din teste 3000 $ n & 5000, pentru 10% din teste 5000 $ n & 10000.
Exemplu
Codul surs
Varianta 1:
import java.io.*;
class cezar
{
static StreamTokenizer st;
static PrintWriter out;
st.nextToken(); n=(int)st.nval;
st.nextToken(); ns=(int)st.nval;
for(k=1;k<=n-1;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
v1[k]=i;
v2[k]=j;
g[i]++; g[j]++;
}
}// citire(...)
min=Integer.MAX_VALUE;
imin=-1; // initializare aiurea ...
for(i=1;i<=n;i++) // mai bine cu heapMIN ...
if(g[i]==1) // i=frunza
if(nde[i]+1<min) // ... aici este testul CORECT ... !!!
{
min=nde[i]+1;
imin=i;
}
timin=tata(imin);
g[imin]--; g[timin]--;
ca[timin]=ca[timin]+ca[imin]+nde[imin]+1;
nde[timin]=nde[timin]+nde[imin]+1; // nr descendenti eliminati
}// elimin()
CAPITOLUL 13. METODA OPTIMULUI LOCAL - GREEDY 182
citire();
for(k=1;k<=n-1-ns;k++) eliminm();
for(k=1;k<=n;k++)
if(g[k]>0)
{
cost+=ca[k];
senat=k;
}
System.out.println("\n"+cost+" "+senat);
out.println(cost+" "+senat);
out.close();
//afisv(g,1,n);
}// main
}// class
Varianta 2:
import java.io.*;
class cezar // cost initial = 1 pentru toate nodurile ...
{
static StreamTokenizer st;
static PrintWriter out;
st.nextToken(); n=(int)st.nval;
st.nextToken(); ns=(int)st.nval;
g[i]=0;
ca[i]=1; // cost initial in nodul i
}
for(k=1;k<=n-1;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
v1[k]=i;
v2[k]=j;
g[i]++; g[j]++;
}
//afisv(v1,1,n-1);
//afisv(v2,1,n-1);
//afisv(g,1,n);
}// citire(...)
min=Integer.MAX_VALUE;
imin=-1; // initializare aiurea ...
for(i=1;i<=n;i++) // mai bine cu heapMIN ...
if(g[i]==1) // i=frunza
if(ca[i]<min) // cost acumulat
{
min=ca[i];
imin=i;
}
timin=tata(imin);
g[imin]--; g[timin]--;
ca[timin]=ca[timin]+min;
s+=min;
int k,senat=0;
st=new StreamTokenizer(new BufferedReader(new FileReader("cezar20.in")));
out=new PrintWriter(new BufferedWriter(new FileWriter("cezar.out")));
citire();
for(k=1;k<=n-1-ns;k++)
{
eliminm();
}
//afisv(c,1,n);
for(k=1;k<=n;k++)
if(g[k]>0)
{
senat=k;
break;
}
System.out.println("\n"+s+" "+senat);
out.println(s+" "+senat);
out.close();
}// main
}// class
Capitolul 14
Metoda backtracking
Metoda backtracking se utilizeaz pentru determinarea unei submulµimi a unui produs cartezian
de forma S1 S2 ... Sn (cu mulµimile Sk nite) care are anumite propriet µi. Fiecare element
s1 , s2 , ..., sn al submulµimii produsului cartezian poate interpretat ca soluµie a unei probleme
concrete.
În majoritatea cazurilor nu oricare element al produsului cartezian este soluµie ci numai cele
care satisfac anumite restricµii. De exemplu, problema determin rii tuturor combin rilor de m
elemente luate câte n (unde 1 & n & m) din mulµimea r1, 2, ..., mx poate reformulat ca problema
n
determin rii submulµimii produsului cartezian r1, 2, ..., mx denit astfel:
Presupunem c mulµimile Si sunt formate din numere întregi consecutive cuprinse între o
valoare min ³i o valoare max. De exemplu, dac min 0, max 9 atunci Si r0, 1, ..., 9x. Dorim
s gener m produsul cartezian S1 S2 ... Sn (de exemplu, pentru n 4). Folosim un vector
cu n elemente a a1 , a2 , ..., an ³i vom atribui ec rei componente ai valori cuprinse între min
³i max.
0 1 2 3 4 5
185
CAPITOLUL 14. METODA BACKTRACKING 186
0 1 2 3 4 5
0 0 0 1 dup care urmeaz
0 1 ... ... n n+1
0 1 2 3 4 5
0 0 0 2
0 1 ... ... n n+1
Folosim o variabil k pentru a urm ri poziµia din vector pe care se schimb valorile. La început
k n ³i, pe aceast poziµie k , valorile se schimb crescând de la min c tre max. Se ajunge astfel
la conguraµia (k 4 n aici!)
0 1 2 3 4 5
0 0 0 9
0 1 ... ... n n+1
Ce se întâmpl mai departe? Apare conguraµia
0 1 2 3 4 5
0 0 1 0 într-un mod ciudat!
0 1 ... ... n n+1
Ei bine, se poate spune c aici este cheia metodei backtraching! Dac reu³im s înµelegem acest
pas de trecere de la o conguraµie la alta ³i s formaliz m ce am observat, atunci am descoperit
singuri aceast metod !
Pe poziµia k n, odat cu apariµia valorii 9 max, s-au epuizat toate valorile posibile, a³a
c vom considera c poziµia k este goal ³i vom trece pe o poziµie mai la stânga, deci k k 1
(acum k 3).
0 1 2 3 4 5
0 0 0
0 1 ... ... n n+1
Pe poziµia k 3 se af valoarea 0 care se va schimba cu urm toarea valoare (dac exist o
astfel de valoare & max), deci în acest caz cu 1.
0 1 2 3 4 5
0 0 1
0 1 ... ... n n+1
Dup plasarea unei valori pe poziµia k , se face pasul spre dreapta (k k 1).
0 1 2 3 4 5
0 0 1
0 1 ... ... n n+1
Aici (dac nu am ie³it în afara vectorului, în urma pasului f cut spre dreapta!), în cazul nostru
k 4 ³i poziµia este goal , se plaseaz prima valoare valid (deci valoarea min).
0 1 2 3 4 5
0 0 1 0
0 1 ... ... n n+1
Cum trecerea de la o valoare la alta se face prin cre³terea cu o unitate a valorii vechi iar acum
facem trecerea gol min, înµelegem de ce este util s iniµializ m variabila gol cu valoarea
min 1.
S consider m c s-a ajuns la conguraµia
0 1 2 3 4 5
0 0 9 9
0 1 ... ... n n+1
Aici k 4 n ³i 9 max. Pe poziµia k nu se mai poate pune o nou valoare ³i atunci:
a se pune gol pe poziµia k
a se face pasul spre stânga, deci k k1
0 1 2 3 4 5
0 0 9
0 1 ... ... n n+1
Aici k 3 ³i 9 max. Pe poziµia k nu se mai poate pune o nou valoare ³i atunci:
a se pune gol pe poziµia k
a se face pasul spre stânga, deci k k1
0 1 2 3 4 5
0 0
0 1 ... ... n n+1
Aici k 2 ³i 0 $ max. Pe poziµia k se poate pune o nou valoare ³i atunci:
a se pune 0 1 1 urm toarea valoare pe poziµia k
a se face pasul spre dreapta, deci k k1
CAPITOLUL 14. METODA BACKTRACKING 187
0 1 2 3 4 5
0 1
0 1 ... ... n n+1
Aici k 3 ³i gol=-1$ max. Pe poziµia k se poate pune o nou valoare:
a se pune gol+1 1 1 0 urm toarea valoare pe poziµia k
a se face pasul spre dreapta, deci k k1
0 1 2 3 4 5
0 1 0
0 1 ... ... n n+1
Aici k 4 ³i gol=-1$ max. Pe poziµia k se poate pune o nou valoare:
a se pune gol+1 1 1 0 urm toarea valoare pe poziµia k
a se face pasul spre dreapta, deci k k1
0 1 2 3 4 5
0 1 0 0
0 1 ... ... n n+1
iar aici k n 1 ³i am ajuns în afara vectorului! Nu-i nimic! Se a³eaz soluµia 0100 ³i
se face pasul la stânga. Aici k 4 ³i 0 $ max. Pe poziµia k se poate pune o nou valoare:
a se pune 0 1 1 urm toarea valoare pe poziµia k
a se face pasul spre dreapta, deci k k1
0 1 2 3 4 5
0 1 0 1
0 1 ... ... n n+1
A ap rut în mod evident urm toarea regul : ajungând pe poziµia k & n, încerc m s punem
urm toarea valoare (gol are ca "urm toarea valoare" pe min) pe aceast poziµie ³i
a dac se poate: se pune urm toarea valoare ³i se face pasul spre dreapta;
a dac nu se poate: se pune gol=min 1 ³i se face pasul spre stânga.
Presupunem c s-a ajuns la conguraµia
0 1 2 3 4 5
9 9 9 9 care se a³eaz ca soluµie. Ajungem la k 4
0 1 ... ... n n+1
0 1 2 3 4 5
9 9 9 9 se gole³te poziµia ³i ajungem la k 3
0 1 ... ... n n+1
0 1 2 3 4 5
9 9 9 se gole³te poziµia ³i ajungem la k 2
0 1 ... ... n n+1
0 1 2 3 4 5
9 9 se gole³te poziµia ³i ajungem la k 1
0 1 ... ... n n+1
0 1 2 3 4 5
9 se gole³te poziµia ³i ajungem la k 0
0 1 ... ... n n+1
0 1 2 3 4 5
iar aici k 0 ³i am ajuns în afara vectorului! Nu-i nimic! Aici
0 1 ... ... n n+1
s-a terminat generarea produsului cartezian!
n
Putem descrie acum algoritmul pentru generarea produsului cartezian S , unde S
rmin, min 1, ..., maxx:
a structuri de date:
a1..n vectorul soluµie
k "poziµia" în vectorul soluµie, cu convenµiile k 0 precizeaz terminarea gener rii, iar
k n 1 precizeaz faptul c s-a construit o soluµie care poate a³at ;
a proces de calcul:
1. se construie³te prima soluµie
2. se plaseaz poziµia în afara vectorului (în dreapta)
3. cât timp nu s-a terminat generarea
4. dac este deja construit o soluµie
5. se a³eaz soluµia ³i se face pasul spre stânga
6. altfel, dac se poate m ri valoarea pe poziµia curent
7. se m re³te cu 1 valoarea ³i se face pasul spre dreapta
CAPITOLUL 14. METODA BACKTRACKING 188
O alt variant ar putea iniµializarea vectorului soluµie cu gol ³i plasarea pe prima po-
ziµie în vector. Nu mai avem în acest caz o soluµie gata construit dar algoritmul este acela³i.
Implementarea în acest caz este urm toarea:
class ProdCartCar1
{
static char[] x;
CAPITOLUL 14. METODA BACKTRACKING 190
class ProdCartCar2
{
static char[] x;
static int n;
static int[] m;
static char[][] a = { {0},
{0,’A’,’B’},
{0,’1’,’2’,’3’}
};
2. soluµia poate pus sub forma unui vector x x1 , x2 , ..., xn cu xi " Ai , i 1, ..., n;
dac la un moment dat nu mai am nici o ³ans s ajung la soluµia c utat , renunµ s
continui pentru o valoare pentru care ³tiu c nu ajung la nici un rezultat.
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
class GenAranjI1
{
static int n=2, min=1,max=4, gol=min-1;
static int[] a=new int[n+1];
class GenAranjI2
{
CAPITOLUL 14. METODA BACKTRACKING 194
class GenAranjR1
{
static int n=2, m=4, nsol=0;
static int[] a=new int[n+1];
{
gasit=true;
break; // in for j
}
if(gasit) continue; // in for i
}
a[k]=i;
if(k<n) f(k+1); else afis();
}
}
class GenAranjR2
{
static int[] a;
static int n,m;
Sunt prezentate mai multe variante (iterative ³i recursive) cu scopul de a vedea diferite moda-
lit µi de a câ³tiga timp în execuµie (mai mult sau mai puµin semnicativ!).
class GenCombI1a // 4550 ms cu 12 22 // 40 ms cu 8 14 fara afis
{ // 7640 ms cu 8 14 cu afis
static int n=8, min=1,max=14;
static int gol=min-1,nv=0;
static int[] a=new int[n+1];
int k=1;
while (k>0)
{
if(a[k]<max)
if(1+a[k]>a[k-1])
if(k<n) ++a[k++]; // maresc si pas dreapta (k>1) ...
else { ++a[k]; /* afis(a); */}// maresc, afisez si raman pe pozitie
else ++a[k]; // maresc si raman pe pozitie
else a[k--]=gol;
}
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}
}// class
{
static int n=12,m=22;
static int[] a=new int[n+1];
}
}
int i;
System.out.print(++nsol+" ==> ");
for(i=1;i<=n;i++) System.out.print(x[i]+" ");
System.out.println();
}
}
}
O problem clasic de generare a conguraµiilor ce respect anumite restricµii este cea a am-
plas rii damelor pe o tabl de ³ah astfel încât s nu se atace reciproc.
Reprezentarea soluµiei: un vector x unde xi reprezint coloana pe care se a regina dac linia
este i.
Restricµii ³i condiµii de continuare: xi j xj pentru oricare i j j (reginele nu se atac pe
coloan ) ³i ¶xi xj ¶ j ¶i j ¶ (reginele nu se atac pe diagonal ).
Sunt prezentate o variant iterativ ³i una recursiv .
class RegineI1
{
static int[] x;
static int n,nv=0;
class RegineR1
{
static int[] x;
static int n,nv=0;
import java.io.*;
class Calut
{
static final int LIBER=0,SUCCES=1,ESEC=0,NMAX=8;
static final int[] a={0,2,1,-1,-2,-2,-1,1,2}; // miscari posibile pe Ox
static final int[] b={0,1,2,2,1,-1,-2,-2,-1}; // miscari posibile pe Oy
static int[][] tabla=new int[NMAX+1][NMAX+1]; // tabla de sah
CAPITOLUL 14. METODA BACKTRACKING 205
1 6 15 10 21
14 9 20 5 16
19 2 7 22 11
8 13 24 17 4
25 18 3 12 23
Se consider o hart cu n µ ri care trebuie colorat folosind m $ n culori, astfel încât oricare
dou µ ri vecine s e colorate diferit. Relaµia de vecin tate dintre µ ri este reµinut într-o matrice
n n ale c rei elemente sunt:
class ColorareHartiI1
{
static int nrCulori=3;// culorile sunt 1,2,3
static int[][] harta=
{// CoCaIaBrTu
{2,1,1,1,1},// Constanta (0)
{1,2,1,0,0},// Calarasi (1)
{1,1,2,1,0},// Ialomita (2)
{1,0,1,2,1},// Braila (3)
{1,0,0,1,2} // Tulcea (4)
};
static int n=harta.length;
static int[] culoare=new int[n];
static int nrVar=0;
if (okk) break;
}
if (!okk)
k--;
else if (k == (n-1))
afis();
else culoare[++k]=0;
}
} // harta
1 1 2 3 2 3
2 1 3 2 3 2
3 2 1 3 1 3
4 2 3 1 3 1
5 3 1 2 1 2
6 3 2 1 2 1
class ColorareHartiR1
{
static int[] x;
static int[][] a=
{
{0,0,0,0,0,0},
{0,2,1,1,1,1},// Constanta (1)
{0,1,2,1,0,0},// Calarasi (2)
{0,1,1,2,1,0},// Ialomita (3)
{0,1,0,1,2,1},// Braila (4)
{0,1,0,0,1,2} // Tulcea (5)
};
static int n,m,nv=0;
int i,j;
for(i=1;i<=m;i++)
{
ok=true;
for(j=1;j<k;j++)
if((i==x[j])&&(a[j][k]==1)) {ok=false; break;}
if(!ok) continue;
x[k]=i;
if(k<n) f(k+1); else afisv();
}
}
Un grup de n persoane sunt a³ezate pe un rând de scaune. Între oricare doi vecini izbucnesc
conicte. Rearanjaµi persoanele pe scaune astfel încât între oricare doi vecini "certaµi" s existe
una sau cel mult dou persoane cu care nu au apucat s se certe! A³aµi toate variantele de
rea³ezare posibile.
Vom rezolva problema prin metada backtracking. Presupunem c persoanele sunt numerotate
la început, de la stanga la dreapta cu 1, 2, ..., n. Consider m ca soluµie un vector x cu n componente
pentru care xi reprezint "poziµia pe care se va aa persoana i dup rea³ezare".
Pentru k % 1 dat, condiµiile de continuare sunt:
class VeciniR1
{
static int[] x;
static int n,m,nsol=0;
x[k]=i;
if(k<n) f(k+1); else afis();
}
}
import java.io.*;
class Vecini
{
static int nrVar=0,n;
static int[] x,y;
import java.io.*;
class Labirint
{
static final char coridor=’.’, start=’x’,
gard=’H’, pas=’*’, iesire=’E’;
static char[][] l;
static int m,n,x0,y0;
static boolean ok;
st.nextToken(); x0=(int)st.nval;
st.nextToken(); y0=(int)st.nval;
CAPITOLUL 14. METODA BACKTRACKING 211
ok=false;
gi(x0,y0);
l[x0][y0]=start;
PrintWriter out = new PrintWriter(
new BufferedWriter(new FileWriter("labirint.out")));
for(i=0;i<m;i++)
{
if (i>0) out.println();
for(j=0;j<n;j++) out.print(l[i][j]);
}
if (!ok) out.println("NU exista iesire !");
out.close();
}// main()
Iniµial, procedura este apelat pentru nivelul 1 ³i valoarea n. Imediat ce este apelat , procedura
va apela o alta pentru a³area vectorului (iniµial a³eaz n).
Din valoarea care se g se³te pe un nivel, S k , se scad pe rând valorile 1, 2, ..., S k 1, valori
cu care se apeleaz procedura pentru nivelul urm tor.
La revenire se reface valoarea existent .
f(n,maxi,1);
t2=System.currentTimeMillis();
System.out.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n");
}
int maxi=((val-1)/2)*2+1;
int maxiok=min(maxip,maxi);
for(int i=maxiok;i>=3;i=i-2)
{
x[poz]=i;
f(val-i,i,poz+1);
}
nsol++;
// dim=poz-1;
// afis2(val);
}
{
long t1,t2;
t1=System.currentTimeMillis();
f(1,0,0);
t2=System.currentTimeMillis();
System.out.println("nsol = "+nsol+" timp = "+(t2-t1)+"\n");
}
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
st.nextToken(); nods=(int)st.nval;
st.nextToken(); nodd=(int)st.nval;
a=new int[n+1][n+1];
d=new int[n+1];
f=new int[n+1];
p=new int[n+1];
color=new int[n+1];
CAPITOLUL 14. METODA BACKTRACKING 216
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
a[i][j]=1;
a[j][i]=1;
}
t=0;
dfs(nods);
/*
6 7 3 4 drum : 3 1 4
1 4 Descoperit : 2 5 1 3 4 8
4 6 Finalizat : 11 6 12 10 7 9
6 1
5 3
2 5
1 3
CAPITOLUL 14. METODA BACKTRACKING 217
4 5
*/
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
a=new int[n+1][n+1];
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
a[i][j]=a[j][i]=1;
}
cc=new int[n+1];
ncc=0;
for(i=1;i<=n;i++)
if(cc[i]==0)
{
ncc++;
conex(i);
}
for(i=1;i<=ncc;i++)
{
System.out.print(i+" : ");
for(j=1;j<=n;j++)
if(cc[j]==i)
System.out.print(j+" ");
System.out.println();
}
}//main
/*
9 7 1 : 1 2 3
CAPITOLUL 14. METODA BACKTRACKING 218
1 2 2 : 4 5
2 3 3 : 6 7 8 9
3 1
4 5
6 7
7 8
8 9
*/
import java.io.*;
class CompTareConexe
{
static final int WHITE=0, GRAY=1, BLACK=2;
static int n,m,t=0,nctc,pozLista;
static int [] ctc,f,color,lista;
static int[][] a; // matricea grafului
static int[][] at; // matricea grafului transpus (se poate folosi numai a !)
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
a[i][j]=1;
at[j][i]=1; // transpusa
}
for(i=1;i<=n;i++) color[i]=WHITE;
pozLista=n;
for(i=1;i<=n;i++) if(color[i]==WHITE) dfsa(i);
nctc=0;
for(i=1;i<=n;i++) color[i]=WHITE;
for(i=1;i<=n;i++)
if(color[lista[i]]==WHITE) { nctc++; dfsat(lista[i]); }
for(i=1;i<=nctc;i++)
{
System.out.print(i+" : ");
for(j=1;j<=n;j++) if(ctc[j]==i) System.out.print(j+" ");
System.out.println();
CAPITOLUL 14. METODA BACKTRACKING 219
}
}//main
/*
9 10 1 : 6 7 8
1 2 2 : 9
2 3 3 : 4 5
3 1 4 : 1 2 3
4 5
6 7
7 8
8 9
5 4
7 6
8 7
*/
import java.io.*;
class SortTopoDFS
{
static final int WHITE=0, GRAY=1, BLACK=2;
static int n,m,t,pozl; // varfuri, muchii, time, pozitie in lista
static int[] d; // descoperit
static int[] f; // finalizat
static int[] color; // culoare
static int[] lista; // lista
static int[][] a; // matricea de adiacenta
CAPITOLUL 14. METODA BACKTRACKING 220
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
a[i][j]=1;
}
for(i=1;i<=n;i++) color[i]=WHITE;
t=0;
pozl=n;
for(nods=1;nods<=n;nods++)
if(color[nods]==WHITE) dfs(nods);
/*
6 4 5 6 3 4 1 2
6 3
1 2
3 4
5 6
*/
import java.io.*;
class SortTopoGRAD
{
static final int WHITE=0, BLACK=1; // color[u]=BLACK ==> u in lista
static int n,m,pozl; // varfuri, muchii, pozitie in lista
static int[] color; // culoare
static int[] lista; // lista
static int[] gi; // grad interior
static int[][] a; // matricea de adiacenta
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
a=new int[n+1][n+1];
color=new int[n+1];
lista=new int[n+1];
gi=new int[n+1];
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
a[i][j]=1;
gi[j]++;
}
for(i=1;i<=n;i++) color[i]=WHITE;
pozl=1;
for(k=1;k<=n;k++) // pun cate un nod in lista
{
u=nodgi0();
micsorezGrade(u);
lista[pozl++]=u;
color[u]=BLACK;
}
/*
6 4 1 2 5 6 3 4
6 3
1 2
3 4
5 6
*/
for(i=1;i<=n;i++)
if(i!=nodscos)
if(cc[i]==0)
{
ncc++;
if(ncc>1) break;
conex(i,ncc,cc,nodscos);
}
if(ncc>1) return false; else return true;
}// econex()
cc[u]=et;
for(int v=1;v<=n;v++)
if(v!=nodscos)
if((a[u][v]==1)&&(cc[v]==0)) conex(v,et,cc,nodscos);
}//conex
}//class
for(int v=1;v<=n;v++)
if((a[u][v]==1)&&(cc[v]==0))
conex(v,et);
}//conex
}//class
/*
9 10 1 8
7 2 2 7
5 1 3 9
1 8 7 9
9 4
6 9
6 4
4 1
9 5
9 7
9 3
*/
afisv(B[i]);
}
}
}//main()
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
G[i][++grad[i]]=j; G[j][++grad[j]]=i;
}
}//Init()
d[u]=++t;
color[u]=GRAY;
low[u]=d[u];
for(i=1;i<=grad[u];i++)
{
fiuu=G[u][i]; // fiuu = un descendent al lui u
if(fiuu==tatau) continue; // este aceeasi muchie
if((color[fiuu]==WHITE)|| // fiuu nedescoperit sau
(d[fiuu]<d[u])) // (u,fiuu) este muchie de intoarcere
{
/* insereaza in stiva muchia (u,fiuu) */
vs++;
fs[vs]=fiuu;
ts[vs]=u;
}
if(color[fiuu]==WHITE) /* fiuu nu a mai fost vizitat */
{
if(u==root) ndr++; // root=caz special (maresc nrfiiroot)
dfs(fiuu,u);
/*
8 9 <-- n m Bridge: 8 1
1 8 8 Bridge: 5 3
1 2 | Graful NU este Biconex
1 3 6 1 Puncte de articulare : 1 3 5
3 4 | \ / \ Numar componente Biconexe : 4
2 4 | 5 --- 3 2 Componenta Biconexa 1 : 1 8
3 5 | / \ / Componenta Biconexa 2 : 1 2 3 4
5 7 7 4 Componenta Biconexa 3 : 5 6 7
5 6 Componenta Biconexa 4 : 3 5
6 7
*/
O triangulaµie a unui poligon convex este o mulµime format din diagonale ale poligonului
care nu se intersecteaz în interiorul poligonului ci numai în vârfuri ³i care împart toat suprafaµa
poligonului în triunghiuri.
Fiind dat un poligon cu n vârfuri notate 1, 2, ..., n s se genereze toate triangulaµiile distincte
ale poligonului. Dou triangulaµii sunt distincte dac difer prin cel puµin o diagonal .
Datele de intrare: în ³ierul text triang.in se a pe prima linie un singur num r natural
reprezentând valoarea lui n (n & 11).
Datele de ie³ire: în ³ierul text triang.out se vor scrie:
- pe prima linie, num rul de triangulaµii distincte;
- pe ecare din urm toarele linii câte o triangulaµie descris prin diagonalele ce o compun. O
diagonal va precizat prin dou numere reprezentând cele dou vârfuri care o denesc; cele
CAPITOLUL 14. METODA BACKTRACKING 227
dou numere ce denesc o diagonal se despart prin cel puµin un spaµiu, iar între perechile de
numere ce reprezint diagonalele dintr-o triangulaµie se va l sa de asemenea minimum un spaµiu.
Exemplu
triang.in triang.out
5 5
1 3 1 4
2 4 2 5
5 2 5 3
3 5 3 1
4 2 1 4
Rezolvare detaliat
if(n==3) out.println(0);
else
{
out.println(catalan(n-2));
diagonale();
f(1);
}
out.close();
t2=System.currentTimeMillis();
System.out.println("nsol = "+nsol+" Timp = "+(t2-t1));
}
CAPITOLUL 14. METODA BACKTRACKING 228
for(i=2;i<=n-2;i++)
for(j=i+2;j<=n;j++){v1[++k]=i; v2[k]=j;}
}
for(i=2;i<=n;i++) x[i]=n+i;
for(j=2;j<=n;j++) y[j]=j;
for(j=2;j<=n;j++)
for(i=2;i<=n;i++)
{
d=cmmdc(y[j],x[i]);
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}
rez=1;
CAPITOLUL 14. METODA BACKTRACKING 229
for(i=2;i<=n;i++) rez*=x[i];
return rez;
}
Se dene³te o partiµie a unui num r natural n ca ind o scriere a lui n sub forma:
n n1 n2 ... nk , k ' 1
unde n1 , n2 , ..., nk sunt numere naturale care veric urm toarea relaµie:
Cerinµ
Fiind dat un num r natural n, s se determine câte partiµii ale lui se pot scrie, conform
cerinµelor de mai sus, ³tiind c oricare num r ni dintr-o partiµie trebuie s e un num r impar.
Datele de intrare
Fi³ierul partitie.in conµine pe prima linie num rul n
Datele de ie³ire
Fi³erul partitie.out va conµine pe prima linie num rul de partiµii ale lui n conform cerinµelor
problemei.
Restricµii ³i preciz ri
a 1&N & 160
Exemplu
partitie.in partitie.out
7 5
Explicaµii:
Cele cinci partiµii sunt:
a 1+1+1+1+1+1+1
a 1+1+1+1+3
a 1+1+5
a 1+3+3
a 7
Indicaµii de rezolvare *
Stelian Ciurea
Problema se poate rezolva în mai multe moduri:
Soluµia comisiei se bazeaz pe urm toarele formule ³i teoreme de combinatoric :
num rul de partiµii ale unui num r n în k p rµi (nu neap rat distincte) este
P n, k P n k, 1 P n k, 2 ... P n k, k
cu P n, 1 P n, n 1 ³i P n, k 0 dac n $ k .
num rul de partiµii ale unui num n în k p rµi distincte este
P n, k P n k k 1©2, k
CAPITOLUL 14. METODA BACKTRACKING 230
num rul de partiµii ale unui num r n în k p rµi impare care se pot ³i repeta este egal cu
num rul de partiµii ale unui num r n în k p rµi distincte.
Problema se poate rezolva ³i prin backtracking; f r prea mari optimiz ri se poate obµine
rezultatul în mai puµin de 3 secunde pentru n $ 120. Pentru valori mai mari ale lui n, se poate
l sa programul s ruleze ³i se reµin rezultatele într-un vector cu valori iniµiale.
Rezultatul pentru n 160 are 8 cifre, deci nu este necesar implementarea operaµiilor cu
numere mari!
Ai,k =
M 1,...,k;M &i;
AiM,M
M impar
Iniµial A0,0 este 1. La implementare, pentru a economisi spaµiu, se poate alege o variant în
care Ai,k s reprezinte num rul de partiµii ale lui i cu numere impare, din care ultimul este cel
mult egal cu al k -lea num r impar, adic 2 k 1.
Dup calcularea elementelor matricei, soluµia pentru num ul i se g se³te în Ai,i . Aceste valori
pot salvate întrun vector de constante, care este transformat într-un nou program, în acela³i mod
ca la problema "Circular" de la clasa a IX-a. Aceast metod conduce la o rezolvare instantanee
a testelor date în concurs.
O alt metod , bazat pe vectorul de constante, ar însemnat generarea soluµiilor folosind
metoda backtracking. Un backtracking l sat s se execute în timpul concursului ar putut genera
soluµiile pentru toate valorile lui N , dup care se putea folosi metoda vectorului de constante, în
timp ce un backtracking folosit ca soluµie a problemei ar obþinut punctajul maxim doar pentru
testele mici ³i medii (pentru valori ale lui N mai mici decât 130).
Limitele problemei ³i timpul de execuþie de 3 secunde permit rezolv rii prin backtracking ³
bµinerea unui punctaj mulµumitor.
Analiza complexit µii
Pentru o rezolvare care se bazeaz pe metoda vectorului de constante, ordinul de complexitate
al soluµiei nale ar fost O 1; soluµia const înn citirea valorii lui N ³i a³area rezultatului
memorat.
Soluµia descris anterior, bazat pe metoda program rii dinamice, are ordinul de complexitate
3
O n . Invit m cititorul s caute metode mai eciente.
Ordinul de complexitate al unei soluµii care se bazeaz pe metoda backtracking este exponen-
µial.
Codul surs
int i;
int maxok=min(maxp,val);
for(i=maxok;i>=1;i--)
{
x[poz]=i;
f(val-i,min(val-i,i),poz+1);
}
}// f(...)
{
if(maxip==1)
{
nsol++;
// dim=poz-1;
// afis2(val,maxip);
return;
}
if(val==0)
{
nsol++;
// dim=poz-1;
// afis1();
return;
}
int maxi=((val-1)/2)*2+1;
int maxiok=min(maxip,maxi);
int i;
for(i=maxiok;i>=1;i=i-2)
{
x[poz]=i;
f(val-i,min(maxiok,i),poz+1);
}
}// f(...)
import java.io.*;
class PartitieNrImpare2 // n = suma de numere impare ;
{ // nsol = 38328320 timp = 4787
static int dim=0,nsol=0;
static int[] x=new int[161];
int n,i;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("partitie.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(
CAPITOLUL 14. METODA BACKTRACKING 233
new FileWriter("partitie.out")));
st.nextToken(); n=(int)st.nval;
int maxi=((n-1)/2)*2+1;
f(n,maxi,1);
out.print(nsol);
out.close();
t2=System.currentTimeMillis();
System.out.println("nsol = "+nsol+" timp= "+(t2-t1));
}// main(...)
int maxi=((val-1)/2)*2+1;
int maxiok=min(maxip,maxi);
int i;
for(i=maxiok;i>=3;i=i-2)
{
x[poz]=i;
f(val-i,i,poz+1);
}
nsol++;
dim=poz-1;
//afis2(val);
}// f(...)
Exemplu
scuta.in scuta.out
4 137
2234 SSSEEENVVNEENVV
5678
9 10 11 12
13 14 15 16
1234
5678
9 10 11 12
13 14 15 16
Explicaµie:
Scuµa Ro³ie a efectuat acelea³i mut ri cu cele efectuate de Lup ³i a avut tot timpul o ciuper-
cuµ în plus. În nal ea a cules toate ciupercuµele din matrice.
Timp maxim de executare: 1 secund /test
Indicaµii de rezolvare *
Codul surs
import java.io.*;
class Scufita
{
static int n, maxc=0;
static int[][] a,b;
static int[] x,y;
static char[] tc=new char[16];
static char[] tmax=new char[16];
st.nextToken(); n=(int)st.nval;
a=new int[n+2][n+2];
b=new int[n+2][n+2];
x=new int[17];
y=new int[17];
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) { st.nextToken(); a[i][j]=(int)st.nval; }
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) { st.nextToken(); b[i][j]=(int)st.nval; }
culegeLupul(1,1,1);
f(1,1,1);
out.println(maxc);
for(i=1;i<=15;i++) out.print(tmax[i]);
out.println();
out.close();
}// main()
x[k]=x[k-1]+a[i][j];
a[i][j]=0;
if(k==16)
{
if(x[16]>maxc)
{
maxc=x[16];
for(int ii=1;ii<=15;ii++) tmax[ii]=tc[ii];
}
a[i][j]=aij;
return;
}
a[i][j]=aij;
}//f(...)
culegeLupul(i+1,j,k+1);
else if(b[i][j-1]>b[i][j+1])
culegeLupul(i,j-1,k+1);
else culegeLupul(i,j+1,k+1);
}// culegeLupul(...)
}// class
Capitolul 15
Programare dinamic
Problema se poate descompune recursiv în mai multe subprobleme care sunt caracterizate
de optime parµiale, iar optimul global se obµine prin combinarea acestor optime parµiale.
Subproblemele respective se suprapun. La un anumit nivel, dou sau mai multe subprobleme
necesit rezolvarea unei aceea³i subprobleme. Pentru a evita risipa de timp rezultat în urma
unei implement ri recursive, optimele parµiale se vor reµine treptat, în maniera bottom-up,
în anumite structuri de date (tabele).
A doua variant de prezentare face apel la conceptele intuitive de sistem, stare ³i decizie.
O problem este abordabil folosind tehnica program rii dinamice dac satisface principiul de
optimalitate sub una din formele prezentate în continuare.
Fie secvenµa de st ri S0 , S1 , ..., Sn ale sistemului.
Dac d1 , d2 , ..., dn este un ³ir optim de decizii care duc la trecerea sistemului din starea S0 în
starea Sn , atunci pentru orice i (1 & i & n) di1 , di2 , ..., dn este un ³ir optim de decizii care
duc la trecerea sistemului din starea Si în starea Sn . Astfel, decizia di depinde de deciziile
anterioare di1 , ..., dn . Spunem c se aplic metoda înainte.
Dac d1 , d2 , ..., dn este un ³ir optim de decizii care duc la trecerea sistemului din starea S0
în starea Sn , atunci pentru orice i (1 & i & n) d1 , d2 , ..., di este un ³ir optim de decizii care
duc la trecerea sistemului din starea S0 în starea Si . Astfel, decizia di1 depinde de deciziile
anterioare d1 , ..., di . Spunem c se aplic metoda înapoi.
Dac d1 , d2 , ..., dn este un ³ir optim de decizii care duc la trecerea sistemului din starea S0
în starea Sn , atunci pentru orice i (1 & i & n) di1 , di2 , ..., dn este un ³ir optim de decizii
care duc la trecerea sistemului din starea Si în starea Sn ³i d1 , d2 , ..., di este un ³ir optim
de decizii care duc la trecerea sistemului din starea S0 în starea Si . Spunem c se aplic
metoda mixt .
238
CAPITOLUL 15. PROGRAMARE DINAMIC 239
1. caracterizarea unei soluµii optime (identicarea unei modalit ti optime de rezolvare, care
satisface una dintre formele principiului optimalit µii),
1. Pentru a calcula A1 A2 ... An , în nal trebuie s înmulµim dou matrice, deci vom
paranteza produsul astfel: A1 A2 ... Ak Ak1 ... An . Aceast observaµie se
aplic ³i produselor dintre paranteze. Prin urmare, subproblemele problemei iniµiale constau
în determinarea parantez rii optimale a produselor de matrice de forma Ai Ai1 ... Aj ,
1 & i & j & n. Observ m c subproblemele nu sunt independente. De exemplu, calcularea
produsului Ai Ai1 ... Aj ³i calcularea produsului Ai1 Ai2 ... Aj 1 , au ca
subproblem comun calcularea produsului Ai1 ... Aj .
M ii 0, i 1, 2, ..., n.
Cum interpret m aceast relaµie de recurenµ ? Pentru a determina num rul minim de înmul-
µiri elementare pentru calculul produsului Ai Ai1 ... Aj , x m poziµia de parantezare
k în toate modurile posibile (între i ³i j 1), ³i alegem varianta care ne conduce la minim.
Pentru o poziµie k xata, costul parantez rii este egal cu numarul de înmulµiri elementare
necesare pentru calculul produsului Ai Ai1 ... Ak , la care se adaug num rul de înmul-
µiri elementare necesare pentru calculul produsului Ak1 ... Aj ³i costul înmulµirii celor
dou matrice rezultate (di1 dk dj ).
Observ m c numai jum tatea de deasupra diagonalei principale din M este utilizat . Pentru
a construi soluµia optim este util ³i reµinerea indicelui k , pentru care se obtine minimul.
Nu vom considera un alt tablou, ci-l vom reµine, pe poziµia simetric faµ de diagonala
principala (M j i).
import java.io.*;
class InmOptimalaMatrice
{
static int nmax=20;
static int m[][]=new int[100][100];
static int p[]=new int[100];
static int n,i,j,k,imin,min,v;
System.out.print(" * ");
if(k+1!=j)
{
System.out.print("(");
paranteze(k+1,j);
System.out.print(")");
}//if
else paranteze(k+1,j);
}//if
else System.out.print("A"+i);
}//paranteze
CAPITOLUL 15. PROGRAMARE DINAMIC 241
System.out.println("Dimensiunile matricelor:");
for(i=1;i<=n+1;i++)
{
System.out.print("p["+i+"]=");
p[i]=Integer.parseInt(br.readLine());
}
for(i=n;i>=1;i--)
for(j=i+1;j<=n;j++)
{
min=m[i][i]+m[i+1][j]+p[i]*p[i+1]*p[j+1];
imin=i;
for(k=i+1;k<=j-1;k++)
{
v=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];
if(min>v) { min=v; imin=k; }
}
m[i][j]=min;
m[j][i]=imin;
}//for i,j
o soluµie poate
3, 6, 10, 30, 60, 80.
Rezolvare
1. Fie Ai1 ai1 & ai2 & ... & aik cel mai lung sub³ir cresc tor al ³irului A. Observ m c
el coincide cu cel mai lung sub³ir cresc tor al ³irului ai1 , ai1 1 , ..., an . Evident Ai2 ai 2 &
ai3 & ... & aik este cel mai lung sub³ir cresc tor al lui ai2 , ai2 1 , ..., an , etc. Prin urmare,
o subproblem a problemei iniµiale const în determinarea celui mai lung sub³ir cresc tor care
începe cu ai , i r1, .., nx.
Subproblemele nu sunt independente: pentru a determina cel mai lung sub³ir cresc tor care
începe cu ai , este necesar s determin m cele mai lungi sub³iruri cresc toare care încep cu aj ,
ai & aj , j ri 1, .., nx.
2. Pentru a reµine soluµiile subproblemelor vom considera doi vectori l ³i poz , ecare cu n
componente, având semnicaµia:
li lungimea celui mai lung sub³ir cresc tor care începe cu ai;
poz i poziµia elementului care urmeaz dup ai în cel mai lung sub³ir cresc tor care începe
cu ai, dac un astfel de element exist , sau 1 dac un astfel de element nu exist .
3. Relaµia de recurenµ care caracterizeaz substructura optimal a problemei este:
int i, j;
l[n]=1;
poz[n]=-1;
for (i=n-1; i>0; i--)
for (l[i]=1, poz[i]=-1, j=i+1; j<=n; j++)
if (a[i] <= a[j] && l[i]<1+l[j])
{
l[i]=1+l[j];
poz[i]=j;
}
Pentru a determina soluµia optim a problemei, determin m valoarea maxim din vectorul l,
apoi a³ m soluµia, începând cu poziµia maximului ³i utilizând informaµiile memorate în vectorul
poz :
import java.io.*;
class SubsirCrescatorNumere
{
static int n;
static int[] a;
static int[] lg;
CAPITOLUL 15. PROGRAMARE DINAMIC 243
st.nextToken();n=(int)st.nval;
a=new int[n+1];
lg=new int[n+1];
poz=new int[n+1];
for(i=1;i<=n;i++) { st.nextToken(); a[i]=(int)st.nval; }
int max,jmax;
lg[n]=1;
for(i=n-1;i>=1;i--)
{
max=0;
jmax=0;
for(j=i+1;j<=n;j++)
if((a[i]<=a[j])&&(max<lg[j])) { max=lg[j]; jmax=j; }
if(max!=0) { lg[i]=1+max; poz[i]=jmax; }
else lg[i]=1;
}
max=0; jmax=0;
for(j=1;j<=n;j++)
if(max<lg[j]){ max=lg[j]; jmax=j; }
out.println(max);
int k;
j=jmax;
for(k=1;k<=max;k++) { out.print(a[j]+" "); j=poz[j]; }
out.close();
}//main
}//class
import java.io.*;
class SubsirCrescatorLitere
{
{
jmax=i;
for(j=i+1;j<n;j++)
if((a.charAt(i)<=a.charAt(j))&&(1+l[j]>1+l[jmax])) jmax=j;
l[i]=1+l[jmax];
poz[i]=jmax;
if(l[i]>lmax) { lmax=l[i]; pozmax=i; }
}
out.print("Solutia ");
k=pozmax;
out.print("("+l[pozmax]+") : ");
for(j=1;j<=lmax;j++)
{
out.print(a.charAt(k));
k=poz[k];
}
out.close();
} // main
}// class
S consider m un triunghi format din n linii (1 $ n & 100), ecare linie conµinând numere
întregi din domeniul 1, 99, ca în exemplul urm tor:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Problema const în scrierea unui program care s determine cea mai mare sum de numere
aate pe un drum între num rul de pe prima linie ³i un num r de pe ultima linie. Fiecare num r
din acest drum este situat sub precedentul, la stânga sau la dreapta acestuia. (IOI, Suedia 1994)
Rezolvare
1. Vom reµine triunghiul într-o matrice p tratic T , de ordin n, sub diagonala principal . Sub-
problemele problemei date constau în determinarea sumei maxime care se poate obµine din numere
aate pe un drum între numarul T ij , pân la un num r de pe ultima linie, ecare num r din
acest drum ind situat sub precedentul, la stânga sau la dreapta sa. Evident, subproblemele nu
sunt independente: pentru a calcula suma maxim a numerelor de pe un drum de la T ij la
ultima linie, trebuie s calcul m suma maxim a numerelor de pe un drum de la T i 1j la
ultima linie ³i suma maxim a numerelor de pe un drum de la T i 1j 1 la ultima linie.
2. Pentru a reµine soluµiile subproblemelor, vom utiliza o matrice S , p tratic de ordin n, cu
semnicaµia
S ij suma maxim ce se poate obµine pe un drum de la T ij la un element de pe
ultima linie, respectând condiµiile problemei.
Evident, soluµia problemei va S 11.
3. Relaµia de recurenµa care caracterizeaz substructura optimal a problemei este:
int i, j;
for (i=1; i<=n; i++) S[n][i]=T[n][i];
for (i=n-1; i>0; i--)
CAPITOLUL 15. PROGRAMARE DINAMIC 245
2. Dac Xk j Y h atunci
LCS Xk, Y h max LCS Xk1 , Yh , LCS Xk , Yh1 .
Secvenµele de cod de mai sus sunt scrise în C/C++. Programul urm tor este scris în Java
³i determin toate soluµiile. Sunt determinate cea mai mic ³i cea mai mare soluµie, în ordine
lexicograc . De asemenea sunt a³ate matricele auxiliare de lucru pentru a se putea urm rii mai
u³or modalitatea de determinare recursiv a tuturor soluµiilor.
int i,j;
matrad();
out.println(a[n][m]);
afism(a);
afism(d);
z=new char[a[n][m]+1];
zmin=new char[z.length];
zmax=new char[z.length];
System.out.println("\nToate solutiile");
toatesol(n,m,a[n][m]);// toate solutiile
out.println(nsol);
System.out.print("SOLmin = ");
afisv(zmin);
System.out.print("SOLmax = ");
CAPITOLUL 15. PROGRAMARE DINAMIC 247
afisv(zmax);
out.close();
}
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));
}
{
z[k]=x.charAt(i-1);
toatesol(i-1,j-1,k-1);
}
}//while
}
System.out.print(" ");
for(j=0;j<m;j++) System.out.print(y.charAt(j)+" ");
System.out.println();
System.out.print(" ");
for(j=0;j<=m;j++) System.out.print(a[0][j]+" ");
System.out.println();
for(i=1;i<n;i++)
{
CAPITOLUL 15. PROGRAMARE DINAMIC 249
System.out.print(x.charAt(i-1)+" ");
m=a[i].length;
System.out.print(" ");
for(j=0;j<m;j++) System.out.print(y.charAt(j)+" ");
System.out.println();
System.out.print(" ");
for(j=0;j<=m;j++) System.out.print(d[0][j]+" ");
System.out.println();
for(i=1;i<n;i++)
{
System.out.print(x.charAt(i-1)+" ");
m=d[i].length;
1 3 2 4 6 5 a c b d f e
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
2 0 1 1 2 2 2 2 2 2 2 2 2 2
3 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
5 0 1 2 2 3 3 4 4 4 4 4 4 4
6 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
b 0 1 2 2 3 4 4 5 5 6 6 6 6
c 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
e 0 1 2 2 3 4 4 5 6 6 7 7 8
f 0 1 2 2 3 4 4 5 6 6 7 8 8
CAPITOLUL 15. PROGRAMARE DINAMIC 250
1 3 2 4 6 5 a c b d f e
1 * - - - - - - - - - - -
2 | - * - - - - - - - - -
3 | * - - - - - - - - - -
4 | | - * - - - - - - - -
5 | | - | - * - - - - - -
6 | | - | * - - - - - - -
a | | - | | - * - - - - -
b | | - | | - | - * - - -
c | | - | | - | * - - - -
d | | - | | - | | - * - -
e | | - | | - | | - | - *
f | | - | | - | | - | * -
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
i ai operaµia j bj
1 f m 1 m
2 i m 2 a
3 z m 3 t
i 4 e
i 5 m
i 6 a
i 7 t
4 i 8 i
5 c 9 c
Tabela 15.2: f izic º matematic
Dac trebuie s transform m cuvântul matematic în cuvântul zic, folosind cât mai
puµine operaµii permise asupra literelor primului cuvânt, putem proceda astfel (folosim acela³i
desen!):
înlocuim literele m, a, t cu f, i, z
³tergem literele e, m, a, t
p str m neschimbate literele i, c
Au fost efectuate 7 operaµii permise (3 înlocuiri ³i 4 ³tergeri).
i ai operaµia j bj
1 m m 1 f
2 a m 2 i
3 t m 3 z
4 e s
5 m s
6 a s
7 t s
8 i 4 i
9 c 5 c
Tabela 15.3: matematic º f izic
Din aceste dou exemple nu trebuie tras concluzia c , atunci când invers m ordinea cuvintelor,
este sucient s permut m între ele numai i
s (inserare
³tergere).
i ai operaµia j bj i ai operaµia j bj
1 b s 1 a s
2 a 1 a 2 b 1 b
3 b 2 b 3 a 2 a
4 a 3 a 4 a m 3 b
5 b m 4 a 5 c s
i 5 c 6 a 4 a
6 a 6 a 7 b 5 b
i 7 b i 6 a
Tabela 15.4: bababa º abaacab Tabela 15.5: abaacab º bababa
COST transformare = 4 în ambele situaµii.
Pa³ii (parcurgând primul cuvânt: bababa) prezentaµi în Tabela 15.4 sunt:
CAPITOLUL 15. PROGRAMARE DINAMIC 252
b se ³terge
a se p streaz
b se p streaz
a se p streaz
b se modic în a
se insereaz c
a se p streaz
se insereaz b
23
Desenul corespunz tor celor dou tabele este:
i ai operaµia j bj i ai operaµia j bj
0 i 1 b 1 b s
1 a 2 a 2 a 1 a
2 a 3 a 3 a 2 a
3 b 4 b 4 b 3 b
4 a 5 a 5 a 4 a
5 a m 6 c 6 c m 5 a
6 c 7 c 7 c 6 c
Tabela 15.6: aabaac º baabacc Tabela 15.7: baabacc º aabaac
COST transformare = 2 în ambele situaµii.
Pa³ii (parcurgând primul cuvânt: aabaac) prezentaµi în Tabela 15.6 sunt:
se insereaz b
a se p streaz
a se p streaz
b se p streaz
a se p streaz
a se modic în c
c se p streaz
24 25
³ir de caractere, se nume³te distanµ Levenshtein ³i m soar gradul de similitudine între cele
dou ³iruri de caractere.
S presupunem c sunt date dou cuvinte (³iruri de caractere) a ³i b.
S presupunem c a are na caractere (a = a1 a2 ...ana ) iar b are nb caractere (b = b1 b2 ...bnb ).
Pentru exemplicare vom considera a = f izic ³i b = matematic. Pentru acest exemplu avem:
na 5, nb 9, a1 f , ..., a5 c, b1 m, b2 a, ..., b9 c.
Vom nota Ai a1 ..ai (0 & i & na) prexul de lungime i al cuvântului a ³i prin Bj b1 ..bj
(0 & j & nb) prexul de lungime j al cuvântului b.
De exemplu: A3 a1 a2 a3 f iz ³i B4 b1 b2 b3 b4 mate.
Evident Ana =a ³i Bnb =b, deci d Ana , Bnb = d a, b.
A0 ε ³i B0 ε reprezint cuvinte vide (³iruri de caractere cu lungimea egal cu 0).
26
ε este simbolul folosit pentru ³irul de caractere vid (Empty string ).
Vom nota prin d Ai , Bj distanµa Levenshtein între cele dou prexe Ai ³i Bj .
Ne gândim dac exist vechiul ³ablon din gura al turat !
Adic :
se poate ajunge în poziµia i, j din poziµiile i 1, j 1 sau
i 1, j sau i, j 1?
pentru d Ai , Bj sunt utile d Ai1 , Bj 1 sau d Ai1 , Bj sau
d Ai , Bj 1 ?
pentru AiÀ Bj sunt necesare Ai1 À
Bj 1 sau Ai1 Bj À
sau Ai À Bj 1 ?
Algoritmul este:
1. Iniµializare (pentru i 0 sau j 0):
d Ai , Bj min i, j (15.2.1)
24
https://en.wikipedia.org/wiki/Levenshtein_distance
25
https://en.wikipedia.org/wiki/String_metric
26
https://en.wikipedia.org/wiki/Empty_string
CAPITOLUL 15. PROGRAMARE DINAMIC 254
def
Folosim o matrice c0..na0..nb unde cij d Ai , Bj .
Algoritmul în pseudocod este:
Când niciunul dintre cele dou prexe nu este vid, avem tot dou situaµii:
dac ai bj , costul este cij ci 1j 1; (trecerea de la transformarea Ai1 º Bj 1
la Ai º Bj nu aduce costuri suplimentare pentru c nu sunt necesare nici inser ri, nici
³tergeri ³i nici inlocuiri).
dac ai j bj , costul este ... puµin mai complicat de calculat! Trebuie s ne gândim ... de
unde putem obµine transformarea Ai º Bj ? Sunt trei posibilit µi ³i o vom alege pe cea
mai bun !
transform m Ai1 º Bj 1 ³i înlocuim ai cu bj ;
costul este ci 1j 1 1 (+1 este costul inlocuirii)
transform m Ai1 º Bj ... ³i ³tergem ai de la sfâr³itul lui Ai ;
costul este ci 1j 1 (+1 este costul ³tergerii)
transform m Ai º Bj 1 ³i inser m bj la sfâr³itul lui Ai ;
costul este cij 1 1 (+1 este costul ³tergerii)
Dac ni se cere ³i un set de operaµii, pornim de la coordonatele na, nb ³i mergem la stânga sau
în sus, astfel:
inserarea corespunde unei deplas ri la stânga;
³tergerea corespunde unei deplas ri în sus;
modicarea corespunde unei deplas ri pe diagonal (dac ai j bj .
Dac se cere doar distanµa, putem folosi doar memorie de ordinul O n (ultimele dou linii
2
din matrice) în loc s folosim memorie de ordinul O n (întreaga matrice).
Programul surs :
11 sus=’|’,
12 stanga=’-’,
13 diags=’x’;
14
15 static int na,nb;
16 static int [][] c; // c[i][j] = cost a1..ai --> b1..bj
17
18 static char [][] op; // op = operatia efectuata
19 static char [][] dir; // dir = directii pentru minim !!!
20
21 static String a,b; // a --> b (OBS: a1=a.char(0), ...)
22
23 public static void main(String[] args) throws IOException
24 {
25 int i,j,cmin=0;
26 BufferedReader br=new BufferedReader(
27 new FileReader("distedit5.in"));
28 PrintWriter out=new PrintWriter(
29 new BufferedWriter(new FileWriter("distedit5.out")));
30
31 a=br.readLine();
32 b=br.readLine();
33
34 na=a.length(); // a[0], a[1], ..., a[na-1]
35 nb=b.length(); // b[0], b[1], ..., b[nb-1]
36
37 c=new int[na+1][nb+1];
38 op=new char[na+1][nb+1];
39 dir=new char[na+1][nb+1];
40
41 //System.out.println(a+" --> "+na);
42 //System.out.println(b+" --> "+nb);
43
44 System.out.print("a : ");
45
46 for(i=1;i<=na;i++)
47 {
48 c[i][0]=i; // stergeri din a; b=vid !!!
49 op[i][0]=sterg; // a_i
50 dir[i][0]=sus;
51 System.out.print(a.charAt(i-1));
52 }
53 System.out.println();
54
55 System.out.print("b : ");
56 for(j=1;j<=nb;j++)
57 {
58 c[0][j]=j; // inserari in a; a=vid !!!
59 op[0][j]=inserez; //b_j
60 dir[0][j]=stanga;
61 System.out.print(b.charAt(j-1));
62 }
63 System.out.println("\n");
64
65 op[0][0]=nimic;
66 dir[0][0]=nimic;
67 for(i=1;i<=na;i++)
68 {
69 for(j=1;j<=nb;j++)
70 {
71 if(a.charAt(i-1)==b.charAt(j-1))
72 {
73 c[i][j]=c[i-1][j-1];
74 op[i][j]=nimic;
75 dir[i][j]=diags;
76 }
77 else
78 {
79 cmin=min(c[i-1][j-1],c[i-1][j],c[i][j-1]);
80 c[i][j]=1+cmin;
81 if(cmin==c[i][j-1]) // inserez b_j
82 {
83 op[i][j]=inserez;
84 dir[i][j]=stanga;
85 }
86 else
CAPITOLUL 15. PROGRAMARE DINAMIC 257
163 if(op[i][j]==sterg)
164 out.println(i+" "+a.charAt(i-1)+" "+op[i][j]);
165 else
166 if(op[i][j]==inserez)
167 out.println(i+" "+" "+op[i][j]+" "+j+" "+b.charAt(j-1));
168 else
169 out.println(i+" "+a.charAt(i-1)+" "+op[i][j]+" "+j+" "+b.charAt(j-1));
170 }
171 else
172 if(i==0)
173 out.println(i+" "+a.charAt(i)+" "+op[i][j]+" "+j+" "+b.charAt(j-1));
174 else
175 if(j==0)
176 out.println(i+" "+a.charAt(i-1)+" "+op[i][j]+" "+j+" "+b.charAt(j));
177 }//afissol(...)
178
179 static void afissol(int i,int j)
180 {
181 if(i==0&&j==0) return;
182
183 if(dir[i][j]==diags) afissol(i-1,j-1);
184 else
185 if(dir[i][j]==stanga) afissol(i,j-1);
186 else
187 if(dir[i][j]==sus) afissol(i-1,j);
188
189 if((i>0)&&(j>0))
190 {
191 if(op[i][j]==sterg)
192 System.out.println(i+" "+a.charAt(i-1)+" "+op[i][j]);
193 else
194 if(op[i][j]==inserez)
195 System.out.println(i+" "+" "+op[i][j]+" "+j+" "+b.charAt(j-1));
196 else
197 System.out.println(i+" "+a.charAt(i-1)+" "+op[i][j]+" "+j+" "+b.charAt(j-1))
;
198 }
199 else
200 if(i==0)
201 //System.out.println(i+" "+a.charAt(i)+" "+op[i][j]+" "+j+" "+b.charAt(j-1));
202 System.out.println(i+" "+op[i][j]+" "+j+" "+b.charAt(j-1));
203 else
204 if(j==0)
205 //System.out.println(i+" "+a.charAt(i-1)+" "+op[i][j]+" "+j+" "+b.charAt(j));
206 System.out.println(i+" "+a.charAt(i-1)+" "+op[i][j]+" ");
207 }//afissolEcran(...)
208
209 static int min(int a, int b) { return a<b ? a:b; }
210
211 static int min(int a, int b, int c) { return min(a,min(b,c)); }
212 }// class
213 /*
214 a : fizic
215 b : matematic
216
217 1 f m 1 m
218 2 i m 2 a
219 3 z m 3 t
220 3 i 4 e
221 3 i 5 m
222 3 i 6 a
223 3 i 7 t
224 4 i 8 i
225 5 c 9 c
226
227 COST transformare = 7
228
229 Process completed.
230 */
3 #include <string>
4
5 using namespace std;
6
7 const int NMAX = 2e1;
8
9 std::string a, b; // a --> b (OBS: a1=a.char(0), ...)
10
11 char
12 inserez=’i’, // inserez inaintea pozitiei
13 sterg=’s’,
14 modific=’m’,
15 nimic=’ ’; // caracter !!!
16
17 char
18 sus=’|’,
19 stanga=’-’,
20 diags=’x’;
21
22 int na,nb;
23 int c[NMAX+1][NMAX+1]; // c[i][j] = cost a1..ai --> b1..bj
24
25 char op[NMAX+1][NMAX+1]; // op = operatia efectuata
26 char dir[NMAX+1][NMAX+1]; // dir = directii pentru minim !!!
27
28 int min(int a, int b) { return a<b ? a:b; }
29
30 int min(int a, int b, int c) { return min(a,min(b,c)); }
31
32 void read()
33 {
34 std::ifstream cin("distedit5.in");
35
36 std::getline(cin, a);
37 na=a.length(); // a[0], a[1], ..., a[na-1]
38 cout<<"a = "<<a<<" --> na = "<<na<<"\n";
39
40 std::getline(cin, b);
41 nb=b.length(); // b[0], b[1], ..., b[nb-1]
42 cout<<"b = "<<b<<" --> nb = "<<nb<<"\n\n";
43
44 cin.close();
45 }
46
47 void afissol(int i,int j)
48 {
49 if(i==0&&j==0) return;
50
51 if(dir[i][j]==diags) afissol(i-1,j-1);
52 else
53 if(dir[i][j]==stanga) afissol(i,j-1);
54 else
55 if(dir[i][j]==sus) afissol(i-1,j);
56
57 if((i>0)&&(j>0))
58 {
59 if(op[i][j]==sterg)
60 cout<<i<<" "<<a[i-1]<<" "<<op[i][j]<<"\n";
61 else
62 if(op[i][j]==inserez)
63 cout<<i<<" "<<" "<<op[i][j]<<" "<<j<<" "<<b[j-1]<<"\n";
64 else
65 cout<<i<<" "<<a[i-1]<<" "<<op[i][j]<<" "<<j<<" "<<b[j-1]<<"\n";
66 }
67 else
68 if(i==0)
69 //cout<<i<<" "<<a[i]<<" "<<op[i][j]<<" "<<j<<" "<<b[j-1]<<"\n";
70 cout<<i<<" "<<op[i][j]<<" "<<j<<" "<<b[j-1]<<"\n";
71 else
72 if(j==0)
73 //cout<<i<<" "<<a[i-1]<<" "<<op[i][j]<<" "<<j<<" "<<b[j]<<"\n";
74 cout<<i<<" "<<a[i-1]<<" "<<op[i][j]<<"\n";
75 }//afissolEcran(...)
76
77 int main()
78 {
CAPITOLUL 15. PROGRAMARE DINAMIC 260
79 int i,j,cmin=0;
80
81 read();
82
83 for(i=1;i<=na;i++)
84 {
85 c[i][0]=i; // stergeri din a; b=vid !!!
86 op[i][0]=sterg; // a_i
87 dir[i][0]=sus;
88 }
89
90 for(j=1;j<=nb;j++)
91 {
92 c[0][j]=j; // inserari in a; a=vid !!!
93 op[0][j]=inserez; //b_j
94 dir[0][j]=stanga;
95 }
96
97 op[0][0]=nimic;
98 dir[0][0]=nimic;
99 for(i=1;i<=na;i++)
100 {
101 for(j=1;j<=nb;j++)
102 {
103 if(a[i-1]==b[j-1])
104 {
105 c[i][j]=c[i-1][j-1];
106 op[i][j]=nimic;
107 dir[i][j]=diags;
108 }
109 else
110 {
111 cmin=min(c[i-1][j-1],c[i-1][j],c[i][j-1]);
112 c[i][j]=1+cmin;
113 if(cmin==c[i][j-1]) // inserez b_j
114 {
115 op[i][j]=inserez;
116 dir[i][j]=stanga;
117 }
118 else
119 if(cmin==c[i-1][j]) // sterg a_i
120 {
121 op[i][j]=sterg;
122 dir[i][j]=sus;
123 }
124 else
125 if(cmin==c[i-1][j-1]) //inlocuire a_i <-- b_j
126 {
127 op[i][j]=modific;
128 dir[i][j]=diags;
129 }
130 }// else
131 }// for j
132 }// for i
133
134 cout<<a<<" --> "<<b<<"\n\n";
135
136 afissol(na,nb);
137
138 // in fisier
139 //std::ofstream cout("distedit5.out");
140 //cout <<c[na][nb]<< ’\n’;
141
142 // pe ecran
143 cout << "\ncost = "<<c[na][nb]<< ’\n’;
144
145 return 0;
146 }
147 /*
148 a = fizic --> na = 5
149 b = matematic --> nb = 9
150
151 fizic --> matematic
152
153 1 f m 1 m
154 2 i m 2 a
CAPITOLUL 15. PROGRAMARE DINAMIC 261
155 3 z m 3 t
156 3 i 4 e
157 3 i 5 m
158 3 i 6 a
159 3 i 7 t
160 4 i 8 i
161 5 c 9 c
162
163 cost = 7
164
165 Process returned 0 (0x0) execution time : 0.094 s
166 Press any key to continue.
167 */
https://www.guru99.com/knapsack-problem-dynamic-programming.html
With the weight limit j, the optimal selections among packages 1, 2, ..., i â 1, i to have the
largest value will have two possibilities:
If package i is not selected, B[i][j] is the maximum possible value by selecting among packages
1, 2, ..., i â 1 with weight limit of j. You have:
B[i][j] = B[i â 1][j]
If package i is selected (of course only consider this case when W[i] ⤠j) then B[i][j] is equal to
the value V[i] of package i plus the maximum value can be obtained by selecting among packages
1, 2, ..., i â 1 with weight limit (j â W[i]). That is, in terms of the value you have:
B[i][j] = V[i] + B[i â 1][j â W[i]]
Due to the creation of B[i][j], which is the maximum possible value, B[i][j] will be the max of
the above 2 values. Basis of Dynamic Programming
So, you have to consider if it is better to choose package i or not. From there you have the
recursive formula as follows:
B[i][j]= max(B[i â 1][j], V[i]+B[i â 1][j â W[i]]
It is easy to see B[0][j] = maximum value possible by selecting from 0 package = 0. Calculate
the Table of Options
You build a table of options based on the above recursive formula. To check if the results are
correct (if not exactly, you rebuild the objective function B[i][j]). Through the creation of the
objective function B[i][j] and the table of options, you will orient the tracing.
Table of options B includes n + 1 lines, M + 1 columns,
Firstly, lled with the basis of dynamic programming: Line 0 includes all zeros. Using recursive
formulas, use line 0 to calculate line 1, use line 1 to calculate line 2, etc. ... until all lines are
calculated.
Trace
When calculating the table of options, you are interested in B[n][M] which is the maximum
value obtained when selecting in all n packages with the weight limit M.
If B[n][M] = B[n â 1][M] then package n is not selected, you trace B[n â 1][M]. If B[n][M]
â B[n â 1][M], you notice that the optimal selection has the package n and trace B[n â
1][M â W[n]].
Continue to trace until reaching row 0 of the table of options. Algorithm to Look Up the Table
of Options to Find the Selected Packages
Note: If B[i][j] = B[i â 1][j], the package i is not selected. B[n][W] is the optimal total value
of package put into the knapsack.
Steps for tracing:
Step 1: Starting from i = n, j = M. Step 2: Look in column j, up from bottom, you nd the
line i such that B[i][j] > B[i â 1][j]. Mark selected package i: Select [i] = true;
Daca B[i][j] == B[i â 1][j] înseamn c obiectul i nu a fost folosit (pus în rucsac).
Step 3: j = B[i][j] â W[i]. If j > 0, go to step 2, otherwise go to step 4 Step 4: Based on
the table of options to print the selected packages.
Explanation of Knapsack code:
Create table B[][]. Set default value for each cell is 0. Build table B[][] in bottom-up manner.
Calculate the table of options with the retrieval formula. Calculate B[i][j]. If you do not select
package i. Then evaluate: if you select package i, it will be more benecial then reset B[i][j]. Trace
CAPITOLUL 15. PROGRAMARE DINAMIC 262
the table from row n to row 0. If you choose package n. Once select package n, can only add
weight M - W[n - 1].
https://www.geeksforgeeks.org/0-1-knapsack-problem-dp-10/?ref=leftb
ar-rightbar
Method 1: Recursion. Approach: A simple solution is to consider all subsets of items and
calculate the total weight and value of all subsets. Consider the only subsets whose total weight is
smaller than W. From all such subsets, pick the maximum value subset. Optimal Sub-structure:
To consider all subsets of items, there can be two cases for every item.
***
Case 1: The item is included in the optimal subset.
Case 2: The item is not included in the optimal set.
***
Therefore, the maximum value that can be obtained from ânâ items is the max of the
following two values.
Maximum value obtained by n-1 items and W weight (excluding nth item). Value of nth item
plus maximum value obtained by n-1 items and W minus the weight of the nth item (including
nth item).
If the weight of ânthâ item is greater than âWâ, then the nth item cannot be
included and Case 1 is the only possibility.
...
import java.io.*;
class Rucsac01
{
public static void main(String[] args) throws IOException
{
int n,m;
int i,j;
int[] g,v;
int[][] c;
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
g=new int[n+1];
v=new int[n+1];
c=new int[n+1][m+1];
out.println(c[n][m]);
out.close();
}// main(...)
{
if(a>b) return a; else return b;
}// max(...)
}
import java.io.*;
class Schimb
{
public static void main(String[] args) throws IOException
{
int v,n;
int i,j;
int[] b, ns;
st.nextToken(); v=(int)st.nval;
st.nextToken(); n=(int)st.nval;
b=new int[n+1];
ns=new int[v+1];
ns[0]=1;
for(i=1; i<=n; i++)
for(j=b[i]; j<=v; j++)
ns[j]+=ns[j-b[i]];
out.println(ns[v]);
out.close();
}// main
}// class
Traversarea unei matrice de la Vest la Est; sunt permise deplas ri spre vecinii unei poziµii
(chiar ³i deplas ri prin "exteriorul" matricei).
import java.io.*;
class Traversare
{
public static void main(String[] args) throws IOException
{
int m,n;
int i,j,imin;
int[][] a,c,t;
int[] d;
int cmin,minc;
st.nextToken(); m=(int)st.nval;
st.nextToken(); n=(int)st.nval;
a=new int[m][n];
c=new int[m][n];
t=new int[m][n];
d=new int[n];
for(i=0;i<m;i++)
for(j=0;j<n;j++)
{
st.nextToken();
a[i][j]=(int)st.nval;
}
for(i=0;i<m;i++) c[i][0]=a[i][0];
for(j=1; j<n; j++)
for(i=0; i<m; i++)
{
minc=min(c[(i+m-1)%m][j-1], c[i][j-1], c[(i+1)%m][j-1]);
c[i][j]=a[i][j]+minc;
if(minc==c[(i+m-1)%m][j-1]) t[i][j]=((i+m-1)%m)*n+(j-1);
else
if(minc==c[i][j-1]) t[i][j]=i*n+(j-1);
else t[i][j]=((i+1)%m)*n+(j-1);
}
imin=0;
cmin=c[0][n-1];
for(i=1;i<m;i++)
if(c[i][n-1]<cmin)
{
cmin=c[i][n-1];
imin=i;
}
out.println(cmin);
d[n-1]=imin*n+(n-1);
j=n-1;
i=imin;
while(j>0)
{
i=t[i][j]/n;
j--;
d[j]=i*n+j;
}
for(j=0;j<n;j++) out.println((1+d[j]/n)+" "+(1+d[j]%n));
out.close();
}// main(...)
}// class
/*
traversare.in traversare.out
3 4 6
2 1 3 2 2 1
1 3 5 4 1 2
3 4 2 7 3 3
1 4
*/
Scopul algoritmului este de a realiza n t ieturi, dea lungul unei vergele în locuri pre-specicate,
cu efort minim (sau cost). Costul ec rei operaµii de t iere este proporµional cu lungimea vergelei
care trebuie t iat . În viaµa real , ne putem imagina costul ca ind efortul depus pentru a plasa
vergeaua (sau un bu³tean!) în ma³ina de t iat.
Consider m ³irul de numere naturale 0 $ x1 $ x2 $ ... $ xn $ xn1 în care xn1 reprezint
lungimea vergelei iar x1 , x2 , ..., xn reprezint abscisele punctelor în care se vor realiza t ieturile
(distanµele faµ de cap tul "din stânga" al vergelei).
Not m prin cij (i $ j ) costul minim necesar realiz rii tuturor t ieturilor segmentului de
vergea xi ..xj .
Evident cii 1 0 pentru c nu este necesar nici o t ietur .
Pentru j % i s presupunem c realiz m prima t ietur în xk (i $ k $ j ). Din vergeaua
xi ...xj obµinem dou bucaµi mai mici: xi ...xk ³i xk ...xj . Costul pentru t ierea vergelei
xi ...xj este format din costul transportului acesteia la ma³ina de t iat (xj xi ) + costul t ierii
vergelei xi ...xk (adic cik ) ³i + costul t ierii vergelei xk ...xj (adic ck j ).
Dar, ce valoare are k ? Evident, k trebuie s aib acea valoare care s minimizeze expresia
cik ck j . Obµinem:
import java.io.*;
class Vergea
{
static int n,nt=0;
static int x[];
static int c[][];
static int t[][];
st.nextToken(); n=(int)st.nval;
x=new int[n+2];
c=new int[n+2][n+2];
t=new int[n+2][n+2];
System.out.println("n="+n);
System.out.print("x: ");
for(i=1;i<=n+1;i++) System.out.print(x[i]+" ");
CAPITOLUL 15. PROGRAMARE DINAMIC 266
System.out.println();
for(i=0;i<=n;i++) c[i][i+1]=0;
j=-1;
for(h=2;h<=n+1;h++) // lungimea vargelei
{
for(i=0;i<=n+1-h;i++) // inceputul vargelei
{
j=i+h; // sfarsitul vargelei
c[i][j]=x[j]-x[i];
min=Integer.MAX_VALUE;
kmin=-1;
for(k=i+1;k<=j-1;k++)
if(c[i][k]+c[k][j]<min)
{
min=c[i][k]+c[k][j];
kmin=k;
}
c[i][j]+=min;
t[i][j]=kmin;
}//for i
}// for h
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
0 0 4 8 16 27 38
0 0 0 3 9 19 29
CAPITOLUL 15. PROGRAMARE DINAMIC 267
0 0 0 0 4 12 22
0 0 0 0 0 7 17
0 0 0 0 0 0 7
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 1 1 2 3 4
0 0 0 2 3 4 4
0 0 0 0 3 4 4
0 0 0 0 0 4 4
0 0 0 0 0 0 5
0 0 0 0 0 0 0
0 0 0 0 0 0 0
Ordinea taieturilor:
1 : 0..6 --> 4
2 : 0..4 --> 2
3 : 0..2 --> 1
4 : 2..4 --> 3
5 : 4..6 --> 5
*/
1 9 1 9 9
1 11 9 9
8
2 8 2 8 8 8
2
7 7 7
7
3 3 3
6 5 6 6 6
4 5 4 5 5 4 555
Evident, orice latur a poligonului face parte dintr-un triunghi al triangulaµiei. Consider m
la început latura 1, 9. S presupunem c într-o anumit triangulaµie optim latura 1, 9 face
parte din triunghiul 1, 5, 9. Diagonalele triangulaµiei optime vor genera o triangulaµie optim a
poligoanelor convexe 1, 2, 3, 4, 5 ³i 5, 6, 7, 8, 9. Au ap rut astfel dou subprobleme ale problemei
iniµiale.
S not m prin p i, k, j perimetrul triunghiului i, k, j (i $ k $ j ) i³i prin cij costul minim
al triagulaµiei poligonului convex i, i 1, ..., j (unde i $ j ). Atunci:
³i
cij min rp i, k, j cik ck j x, dac i $ j &n
i&k$j
import java.io.*;
class RoyFloydWarshall // O(n^3)
{
static final int oo=0x7fffffff;
static int n,m;
static int[][] d;
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
d=new int[n+1][n+1];
for(k=1;k<=n;k++)
for(i=1;i<=n;i++) // drumuri intre i si j care trec
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 3 2 3 1 * * *
1 3 1 3 4 2 * * *
2 3 2 1 2 2 * * *
4 5 1 * * * 2 1 2
5 6 3 * * * 1 2 3
4 6 2 * * * 2 3 4
*/
Capitolul 16
Potrivirea ³irurilor
Consider m un text (un ³ir de caractere) t t1 , t2 , ..., tn ³i un ³ablon (tot un ³ir de caractere,
numit pattern în englez ) p p1 , p2 , ..., pm . Consider m m & n ³i dorim s determin m dac
textul t conµine ³ablonul p, adic , dac exist 0 & d & n m astfel încât tdi pi pentru orice
1 & i & m. Problema potrivirii ³irurilor const în determinarea tuturor valorilor d (considerate
deplasamente) cu proprietatea menµionat .
import java.io.*;
class PotrivireSir
{
static char[] t,p;
static int n,m;
s=br.readLine();
n=s.length();
t=new char[n+1];
for(i=0;i<n;i++) t[i+1]=s.charAt(i);
System.out.print("t : ");
afisv(t,1,n);
s=br.readLine();
m=s.length();
p=new char[m+1];
for(i=0;i<m;i++) p[i+1]=s.charAt(i);
System.out.print("p : ");
afisv(p,1,m);
System.out.println();
for(i=1;i<=n-m+1;i++)
269
CAPITOLUL 16. POTRIVIREA IRURILOR 270
{
for(j=1;j<=m;j++) if(p[j]!=t[i+j-1]) break;
j--; // ultima pozi\c tie potrivita
abababaababaababa
abaabab
abababaababaababa
abaabab
*/
1 2 3 4 5 6 7
³i exist dou potriviri:
p: a b a a b a b
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
t: a b a b a b a a b a b a a b a b a
p: a b a a b a b
1 2 3 4 5 6 7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
t: a b a b a b a a b a b a a b a b a
p: a b a a b a b
1 2 3 4 5 6 7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
t: a b a b a b a a b a b a a b a b a
p: a b a a b a b
p: a b a a b a b
p: a b a a b a b
p: a b a a b a b
p: a b a a b a b *
p: a b a a b a b
p: a b a a b a b
p: a b a a b a b
p: a b a a b a b
p: a b a a b a b *
p: a b a a b a b
1 2 3 4 5 6 7
Prima nepotrivire din ecare încercare este evidenµiat prin caracter boldat iar soluµiile sunt
marcate cu *.
Dorim s avans m cu mai mult de un pas la o nou încercare, f r s risc m s pierdem vreo
soluµie!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
t: a b a b a b a a b a b a a b a b a
p: a b a a b a b
p: a b a a b a b
p: a b a a b a b *
p: a b a a b a b *
p: a b a
Not m prin ti..j secvenµa de elemente consecutive ti , ..., tj (cuprins între poziµiile i ³i j )
din ³irul t t1 , t2 , ..., tn .
S presupunem c suntem la un pas al veric rii potrivirii cu un deplasament d ³i prima
nepotrivire a ap rut pe poziµia i din text ³i poziµia j 1 din ³ablon, deci ti j..i 1 p1..j
³i ti j pj 1.
¬
Care este cel mai bun deplasament d pe care trebuie s -l încercam?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
j j+1 m
d p: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1 ... j+1-k ...
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
...
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
t: 1 ... i-j ... ...
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
i-k i-1
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx i ... ... ... n
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
1 k k+1 m
d' p:
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
...
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
... ...
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Folosind Figura ??, dorim s determin m cel mai mare indice k $ j astfel încât p1..k
pj 1 k..j . Cu alte cuvinte, dorim s determin m cel mai lung sux al secvenµei p1..j
¬
iar noul deplasament d trebuie ales astfel încât s realizeze acest lucru. Este astfel realizat ³i
potrivirea textului t cu ³ablonul p, ti k..i 1 p1..k .
R mâne s veric m apoi dac ti pk 1.
Observ m c noul deplasament depinde numai de ³ablonul p ³i nu are nici o leg tur cu textul
t.
Algoritmul KMP utilizeaz pentru determinarea celor mai lungi suxe o funcµie numit next.
Dat ind ³irul de caractere p1..m, funcµia
Initializ m next1 0.
Presupunem c au fost determinate valorile next1, next2, ..., nextj .
Cum determin m nextj 1?
1 k+1-k' k
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
p: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
... ...
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
k+1 ... ...
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
... ...
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
p: 1 ... j+1-k ... j+1-k' ...... j j+1 ... ...
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
.. .. ..
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx k'+1 ... ...
p: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1 k'
Ne ajut m de Figura ?? pentru a urm ri mai u³or raµionamentul! În aceast gur nextj k
¬
³i nextk k .
Dac pj 1 pk 1 (folosind notaµiile din gur ) atunci nextj 1 k 1.
Obµinem:
Dac nici acum nu avem egalitate de caractere, vom continua acela³i raµionament pân cand
g sim o egalitate de caractere sau lungimea prexului c utat este 0. Evident, acest algoritm se
termin într-un num r nit de pa³i pentru c j % k % k % ... ' 0. Dac ajungem la 0, atunci vom
¬
avea next j 1 0.
Ordinul de complexitate al algoritmului KMP este O n m.
import java.io.*;
class KMP
{
static int na=0; // nr aparitii
static char[] t,p; // t[1..n]=text, p[1..m]=pattern
static int[] next;
s=br.readLine();
sc=s.toCharArray();
m=sc.length;
CAPITOLUL 16. POTRIVIREA IRURILOR 273
p=new char[m+1];
for(i=1;i<=m;i++) p[i]=sc[i-1];
}//readData()
}//class
/*
pattern apare cu deplasarea 5 :
12312123412123412
1234
pattern apare cu deplasarea 11 :
12312123412123412
1234
*/
Restricµii ³i preciz ri
a 1 & n & 20000
Exemple
circular.in circular.out
10 7
ABCBAABBAB
BABABCBAAB
Timp maxim de execuµie/test: 0.1 secunde
Rezolvare (indicaµia autorului): O variant cu dou "for"-uri e foarte u³or de scris, dar nu
se încadreaz în timp pentru n mare.
Folosim algoritmului KMP de c utare a unui sub³ir.
Concaten m primul ³ir cu el însu³i ³i c ut m prima apariµie a celui de-al doilea ³ir în ³irul nou
format. În realitate nu e nevoie de concatenarea efectiv a ³irului, doar µinem cont c indicii care
se refer la ³irul "mai lung" trebuie luaµi modulo n.
import java.io.*;
class Circular
{
static int n,d=-1; // pozitia de potrivire
static char[] x,y; // x[1..n]=text, y[1..m]=pattern
static int[] next;
n=Integer.parseInt(br.readLine()); // System.out.println("n="+n);
x=new char[n+1];
y=new char[n+1];
for(i=1;i<=n;i++) y[i]=sc[i-1];
}//readData()
12341212341234112312
*/
Copiii solarieni se joac adesea trimiµându-³i mesaje codicate. Pentru codicare ei folosesc
un cifru bazat pe o permutare p a literelor alfabetului solarian ³i un num r natural d.
Alfabetul solarian conµine m litere foarte complicate, a³a c noi le vom reprezenta prin numere
de la 1 la m.
Dat ind un mesaj în limbaj solarian, reprezentat de noi ca o succesiune de n numere cuprinse
între 1 ³i m, c1 c2 ...cn , codicarea mesajului se realizeaz astfel: se înlocuie³te ecare liter ci cu
p ci , apoi ³irul obµinut p c1 p c2 ...p cn se rote³te spre dreapta, f când o permutare circular
cu d poziµii rezultând ³irul p cnd1 ...p cn1 p cn p c1 p c2 ...p cnd .
De exemplu, pentru mesajul 213321, permutarea p 312 (adic p 1 3, p 2 1, p 3 2)
³i d 2. Aplicând permutarea p vom obµine ³irul 132213, apoi rotind spre dreapta ³irul cu dou
poziµii obµinem codicarea 131322.
Cerinµ :
Date ind un mesaj necodicat ³i codicarea sa, determinaµi cifrul folosit (permutarea p ³i
num rul d).
Date de intrare:
Fi³ierul de intrare cifru.in conµine pe prima linie numele naturale n ³i m, separate prin spaµiu,
reprezentând lungimea mesajului ³i respectiv num rul de litere din alfabetul solarian. Pe cea de a
doua linie este scris mesajul necodicat ca o succesiune de n numere cuprinse între 1 ³i m separate
prin câte un spaµiu. Pe cea de a treia linie este scris mesajul codicat ca o succesiune de n numere
cuprinse între 1 ³i m separate prin câte un spaµiu.
Date de ie³ire:
Fi³ierul de ie³ire cifru.out va conµine pe prima linie num rul natural d, reprezentând num rul
de poziµii cu care s-a realizat permutarea circular spre dreapta. Dac pentru d exist mai multe
posibilit µii se va alege valoarea minim . Pe urm toarea linie este descris permutarea p. Mai
exact se vor scrie valorile p 1, p 2, ..., p m separate prin câte un spaµiu.
Restricµii:
n & 100000
m & 9999
Mesajul conµine ecare num r natural din intervalul 1, m cel puµin o dat .
Pentru teste cu m & 5 se acord 40 de puncte din care 20 pentru teste ³i cu n & 2000.
Exemplu:
cifru.in cifru.out
63 2
213321 312
131322
Timp maxim de execuµie/test: 0.2 secunde
Indicatii de rezolvare:
Soluµia comisiei
Fiecare apariµie a unui simbol din alfabet într-un ³ir se înlocuie³te cu distanµa faµ de prece-
denta apariµie a aceluia³i simbol (considerând ³irul circular, deci pentru prima apariµie a simbolului
se ia distanµa faµ de ultima apariµie a aceluia³i simbol).
F când aceast recodicare pentru cele dou ³iruri reducem problema la determinarea permu-
t rii circulare care duce primul ³ir în al doilea, care poate rezolvat cu un algoritm de pattern
matching, dac concaten m primul ³ir cu el însu³i rezultând o complexitate O n.
Pentru m mic se pot genera toate permut rile mulµimii r1, 2, ..., mx f când pentru ecare
permutare o c utare (cu KMP de exemplu), iar pentru n mic se poate c uta permutarea pentru
ecare d 0, 1, ..., n.
Codul surs
import java.io.*;
class kmp
{
static int[] t0; // text mesaj necodificat --> spatiu ... de eliberat !
static int[] t1; // text mesaj codificat --> spatiu ... de eliberat !
CAPITOLUL 16. POTRIVIREA IRURILOR 277
static int[] t; // text in KMP ... (d0,d0) ... d0 dublat ... spatiu !!!
static int[] s; // sablon in KMP ... (d1)
static int[] p; // prefix in KMP ... 1,2,...n
static int[] ua; // pozitia ultimei aparitii ... 1,2,...,m ... ==> d[] mai rapid
static int[] perm;// permutarea
static int n,m; // ... n=100.000, m=9.999 ... maxim !!! ==> 200K
int i,j,j0,j1,k,deplasarea=-1;
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
ua=new int[m+1];
t0=new int[n+1];
t1=new int[n+1];
d0=new int[n+1];
d1=new int[n+1];
p=new int[n+1];
s=d0;
prefix(s,p,n);
//afisv(s,1,n); afisv(p,1,n); System.out.println();
t=new int[2*n+1]; // ocupa spatiu prea mult; aici standard dar ...
for(i=1;i<=n;i++) t[i]=t[n+i]=d1[i];
//afisv(t,1,2*n);
// permutarea ...
perm=ua; // economie de spatiu ...
for(i=1;i<=m;i++) perm[i]=0;
k=0; // nr elemente plasate deja in permutare ...
j1=0;
for(i=1;i<=n;i++)
{
j1++;
CAPITOLUL 16. POTRIVIREA IRURILOR 278
j0=n-deplasarea+i;
if(j0>n) j0=j0-n;
//System.out.println(i+" : "+j0+" "+j1);
if(perm[t0[j0]]==0)
{
perm[t0[j0]]=t1[j1];
k++;
}
if(k==m) break;
}
//afisv(perm,1,m);
for(i=1;i<=n;i++)
{
if(ua[t[i]]!=0) // stiu pozitia spre stanga a lui t[i] ...
{
if(ua[t[i]]<i)
d[i]=i-ua[t[i]]; // e mai la stanga ...
else
d[i]=i-ua[t[i]]+n; // e mai la dreapta ...
d[i]=k;
ua[t[i]]=i;
}// for i
}// distanta(...)
Geometrie computaµional
P3
P3
P3
P2 P2
P2
P1 a) P1 b) P1 c)
o P1 x1 , y1 , P2 x2 , y2 , P3 x3 , y3 y2 y1 x3 x2 y3 y2 x 2 x1
astfel
~
$0 sens trigonometric
o P1 x1 , y1 , P2 x2 , y2 , P3
x3 , y3
0 coliniare
% 0
sensul acelor de ceas
280
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 281
P7 P6 P2 P3
P5 P4
P1 P1
P2 P3 P4 P7 P6 P5
a) b)
fi x, y y yi xi1 xi x xi yi1 yi
Dreapta Pi Pi1 împarte planul în dou semiplane. Funcµia fi x, y are valori de acela³i
semn pentru toate punctele din acela³i semiplan, valori cu semn contrar pentru toate punctele din
cel lalt semiplan ³i valoarea 0 pentru doate punctele situate pe dreapt .
Pentru a siguri c punctul P0 x0 , y0 se a în interiorul poligonului (acesta ind convex)
trebuie s veric m dac toate vârfurile poligonului împreun cu punctul P0 x0 , y0 sunt de aceea³i
parte a dreptei Pi Pi1 , adic toate valorile fi xj , yj (1 & j & n, j j i ³i j j i 1) au acela³i semn
cu fi x0 , y0 (sau sunt nule dac accept m prezenµa punctului P0 x0 , y0 pe frontiera poligonului).
Aceasta este o condiµie necesar dar nu ³i sucient . Vom verica dac pentru orice latur Pi Pi1
(1 & i & n) a poligonului toate celelalte vârfuri sunt în acela³i semiplan cu P0 x0 , y0 (din cele
dou determinate de dreapta suport a laturii respective) iar dac se întâmpl acest lucru atunci
putem trage concluzia c punctul P0 x0 , y0 se a în interiorul poligonului convex.
O alt modalitate de vericare dac punctul P0 x0 , y0 este în interiorul sau pe frontiera
poligonului convex P1 x1 , y1 P2 x2 , y2 ...Pn xn yn este vericarea urm toarei relaµii:
= arie
n
ariepoligon P1 P2 ...Pn triunghi P0 Pk Pk1
k 1
P7 P6 P7 P6
P5 P5
P1 P1
P3 P3
P2 P4 P2 P4
a) b)
5 5
4 4
3 3
2 2
1 1
1 2 3 4 5 6 7 1 2 3 4 5 6 7
a) b)
int i0,i,i1,i2;
i0=1;
for(i=2;i<=n;i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i;
System.out.println("Stanga_Jos ==> P"+i0+" : "+x[i0]+" "+y[i0]);
i1=i0;
npic++;
System.out.println(npic+" --> "+i1); //br.readLine();
do
{
i2=i1+1; if(i2>n) i2-=n;
for(i=1;i<=n;i++)
{
//System.out.println("orient("+i1+","+i+","+i2+")="+orient(i1,i,i2));
//br.readLine();
if(orient(i1,i,i2)>0) i2=i; else
if(orient(i1,i,i2)==0) // coliniare
if( // i intre i1 i2 ==> cel mai apropiat
((x[i]-x[i1])*(x[i]-x[i2])<0)||
((y[i]-y[i1])*(y[i]-y[i2])<0)
)
i2=i;
}
u[i1]=i2;
p[i2]=i1;
i1=i2;
npic++;
System.out.println(npic+" --> "+i1); //br.readLine();
} while(i2!=i0);
npic--; // apare de doua ori primul punct !
System.out.print("u : "); afisv(u,1,n);
System.out.print("p : "); afisv(p,1,n);
}// infasurareJarvis()
for(k=1;k<=n;k++)
{
st.nextToken(); x[k]=(int)st.nval;
st.nextToken(); y[k]=(int)st.nval;
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 284
}
infasurareJarvis();
}//main
}//class
F r punctele coliniare de pe înf ³ur toarea convex (cazul b în gur ):
import java.io.*; // infasuratoare convexa
class Jarvis2 // pe frontiera coliniare ==> iau numai capetele ... !!!
{
static int n,npic=0; // npic=nr puncte pe infasuratoarea convexa
static int [] x;
static int [] y;
static int [] p; // precedent
static int [] u; // urmator
int i0,i,i1,i2;
i0=1;
for(i=2;i<=n;i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i;
System.out.println("Stanga_Jos ==> P"+i0+" : "+x[i0]+" "+y[i0]);
i1=i0;
npic++;
System.out.println(npic+" --> "+i1); //br.readLine();
do
{
i2=i1+1; if(i2>n) i2-=n;
for(i=1;i<=n;i++)
{
//System.out.println("orient("+i1+","+i+","+i2+")="+orient(i1,i,i2));
//br.readLine();
if(orient(i1,i,i2)>0) i2=i; else
if(orient(i1,i,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;
}
u[i1]=i2;
p[i2]=i1;
i1=i2;
npic++;
System.out.println(npic+" --> "+i1); //br.readLine();
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 285
} while(i2!=i0);
npic--; // apare de doua ori primul punct !
System.out.print("u : "); afisv(u,1,n);
System.out.print("p : "); afisv(p,1,n);
}// infasurareJarvis()
5 5
4 4
3 3
2 2
1 1
1 2 3 4 5 6 7 1 2 3 4 5 6 7
a) b)
5 5
4 4
3 3
2 2
1 1
1 2 3 4 5 6 7 1 2 3 4 5 6 7
a) b)
int i,j,aux;
i=p;j=u;
while(i<j)
{
while( (i<j)&&
( (orient(1,i,k)<0)||
((orient(1,i,k)==0)&&
(((x[i]-x[1])*(x[i]-x[k])<0)||((y[i]-y[1])*(y[i]-y[k])<0)))
)
)
{
i++;
}
while( (i<j)&&
( (orient(1,j,k)>0)||
((orient(1,j,k)==0)&&
(((x[j]-x[1])*(x[j]-x[k])>0)||((y[j]-y[1])*(y[j]-y[k])>0)))
)
)
{
j--;
}
if(i<j)
{
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 287
if(k==i) k=j; else if(k==j) k=i;// k=fix dar se poate schimba pozitia
aux=x[i]; x[i]=x[j]; x[j]=aux;
aux=y[i]; y[i]=y[j]; y[j]=aux;
aux=o[i]; o[i]=o[j]; o[j]=aux;
}
}// while
if(p<i-1) qsort(p,i-1);
if(j+1<u) qsort(j+1,u);
}// qSort(...)
i0=1;
for(i=2;i<=n;i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i;
System.out.println("Stanga_Jos ==> P"+i0+" : "+x[i0]+" "+y[i0]+"\n");
qsort(2,n);
System.out.println();
System.out.print("x : "); afisv(x,1,n); System.out.println();
System.out.print("y : "); afisv(y,1,n); System.out.println();
System.out.print("o : "); afisv(o,1,n); System.out.println();
System.out.println();
}// scanareGraham()
for(k=1;k<=n;k++)
{
st.nextToken(); x[k]=(int)st.nval;
st.nextToken(); y[k]=(int)st.nval;
o[k]=k;
}
scanareGraham();
i0=1;
for(i=2;i<=n;i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i;
qsort(2,n);
i1=1; p[1]=i1;
i2=2; p[2]=i2;
np=2;
i3=3;
while(i3<=n)
{
while(orient(i1,i2,i3)>0)
{
i2=p[np-1];
i1=p[np-2];
np--;
}
np++;
p[np]=i3;
i2=p[np];
i1=p[np-1];
i3++;
}// while
// afisez rezultatele
System.out.print("punctele initiale: ");
for(i=1;i<=np;i++) System.out.print(o[p[i]]+" ");
System.out.println();
System.out.print("infasuratoare x: ");
for(i=1;i<=np;i++) System.out.print(x[p[i]]+" ");
System.out.println();
System.out.print("infasuratoare y: ");
for(i=1;i<=np;i++) System.out.print(y[p[i]]+" ");
System.out.println();
}// scanareGraham()
for(k=1;k<=n;k++)
{
st.nextToken(); x[k]=(int)st.nval;
st.nextToken(); y[k]=(int)st.nval;
o[k]=k;
}
scanareGraham();
}//main
}//class
Versiune f r puncte coliniare pe înf ³ur toare:
import java.io.*; // aici ... infasuratoarea nu contine puncte coliniare ...
class Graham2 // este o eliminare din rezultatul final dar ...
{ // se pot elimina puncte la sortare si/sau scanare ...
static int n;
static int np; // np=nr puncte pe infasuratoarea convexa
static int[] x;
static int[] y;
static int[] o; // pozitia inainte de sortare
static int[] p; // poligonul infasuratoare convexa
static void qsort(int p, int u)// elimin si punctele coliniare (din interior)
{
int i,j,k,aux;
// aleg un punct fix k
k=(p+u)/2;
i=p;
j=u;
while(i<j)
{
while( (i<j)&&
( (orient(1,i,k)<0)||
((orient(1,i,k)==0)&&
(((x[i]-x[1])*(x[i]-x[k])<0)||((y[i]-y[1])*(y[i]-y[k])<0)))
)
) i++;
while( (i<j)&&
( (orient(1,j,k)>0)||
((orient(1,j,k)==0)&&
(((x[j]-x[1])*(x[j]-x[k])>0)||((y[j]-y[1])*(y[j]-y[k])>0)))
)
) j--;
if(i<j)
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 291
{
if(k==i) k=j; else if(k==j) k=i;// k=fix dar se poate schimba pozitia
aux=x[i]; x[i]=x[j]; x[j]=aux;
aux=y[i]; y[i]=y[j]; y[j]=aux;
aux=o[i]; o[i]=o[j]; o[j]=aux;
}
}
if(p<i-1) qsort(p,i-1);
if(j+1<u) qsort(j+1,u);
}// qSort(...)
i0=1;
for(i=2;i<=n;i++) if((x[i]<x[i0])||((x[i]==x[i0])&&(y[i]<y[i0]))) i0=i;
qsort(2,n);
i1=1; p[1]=i1;
i2=2; p[2]=i2;
np=2;
i3=3;
while(i3<=n)
{
while(orient(i1,i2,i3)>0) // elimin i2
{
i2=p[np-1];
i1=p[np-2];
np--;
}
np++;
p[np]=i3;
i2=p[np];
i1=p[np-1];
i3++;
}// while
// afisez rezultatele
System.out.print("punctele initiale: ");
for(i=1;i<=np;i++) if(o[p[i]]!=0) System.out.print(o[p[i]]+" ");
System.out.println();
System.out.print("infasuratoare x: ");
for(i=1;i<=np;i++) if(o[p[i]]!=0) System.out.print(x[p[i]]+" ");
System.out.println();
System.out.print("infasuratoare y: ");
for(i=1;i<=np;i++) if(o[p[i]]!=0) System.out.print(y[p[i]]+" ");
System.out.println();
}// scanareGraham()
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 292
for(k=1;k<=n;k++)
{
st.nextToken(); x[k]=(int)st.nval;
st.nextToken(); y[k]=(int)st.nval;
o[k]=k;
}
scanareGraham();
}//main
}//class
D
M
ymax
P7 R
P6
A P8 P9 P5
P12
P1 P10
P11 P4 C
P2
P3 Q
N ymin
B
xmin xmax
Ck = C k-1 P
k+1
Ck C
P k+1
k Ck
a) b)
acestea urmeaz celelalte puncte într-o ordine oarecare). Presupunem c punctele, dup ordonare,
sunt: P1 x1 , y1 , P2 x2 , y2 , P3 x3 , y3 , ..., Pn xn , yn .
Not m cu Ci ai , bi ; ri cercul de centru ai , bi ³i raz minim ri care acoper punctele
P1 , P2 , ..., Pn .
Ô Consider m cercul C2 a2 , b2 ; r2 unde a2 x1 x2 ©2, b2 y1 y2 ©2 ³i r2
1
2
x 2 x 1 2
y2 y 1 2
, adic cercul de diametru P P
1 2 .
S presupunem c am determinat, pas cu pas, cercurile C2 , C3 , ..., Ci ³i trebuie s determin m
cercul Ci1 .
Dac punctul Pi1 se a în interiorul cercului Ci atunci cercul Ci1 este identic cu Ci .
Dac punctul Pi1 nu se a în interiorul cercului Ci atunci cercul Ci1 se determin reluînd
algoritmul pentru ³irul de puncte P1 , P2 , ...,Pi , Pi1 dar impunând condiµia ca acest cerc s treac
în mod obligatoriu prin punctul Pi1 xi1 , yi1 . Putem plasa acest punct pe prima poziµie în
³irul punctelor ³i astfel vom impune la ecare pas ca punctul P1 s e pe cercul care trebuie
determinat!
a Nu exist trei puncte coliniare, indiferent dac sunt gr dini sau fântâni
a Orice linie din ³ierele de intrare ³i ie³ire se termin prin marcajul de sfâr³it de linie.
Exemplu
seceta.in seceta.out Explicaµie
3 624 Costul minim este [6.24264 * 100]=624
1 4 prin legarea perechilor:
3 3 Gradini Fantani
4 7 14 23
2 3 33 31
2 5 47 25
3 1
Timp maxim de execuµie/test: 1 sec sub Windows ³i 0.5 sec sub Linux.
Indicaµii de rezolvare *
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++)
{
st.nextToken(); xf[k]=(int)st.nval;
st.nextToken(); yf[k]=(int)st.nval;
}
}
int aux;
if(a>b) {aux=a; a=b; b=aux;}
if((a<=c)&&(c<=b)) return true; else return false;
}
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++)
{
st.nextToken(); xf[k]=(int)st.nval;
st.nextToken(); yf[k]=(int)st.nval;
}
}
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 298
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]+") ");
System.out.println(" ("+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();
}
}
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++)
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 300
{
st.nextToken(); xf[k]=(int)st.nval;
st.nextToken(); yf[k]=(int)st.nval;
}
}
import java.io.*; // gresit (!) dar ... obtine 100p ... !!!
class Seceta4 // test 9 : 2.18 sec --> 0.04 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 boolean[] epus=new boolean[13];
citire();
rezolvare();
afisare();
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1)+" ms");
}// main(...)
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++)
{
st.nextToken(); xf[k]=(int)st.nval;
st.nextToken(); yf[k]=(int)st.nval;
}
}// citire(...)
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 302
if(seIntersecteaza) continue;
a[k]=i;
epus[i]=true;
if(k<n) f(k+1); else verificCostul();
epus[i]=false;
}// for i
}// f(...)
În Delta Dun rii exist o zon s lbatic , rupt de bucuriile ³i necazurile civilizaµiei moderne.
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 303
În aceast zon exist doar n case, poziµiile acestora ind specicate prin coordonatele carte-
ziene de pe hart .
Postul de radio al ONI 2005 dore³te s emit pentru toµi locuitorii din zon ³i, prin urmare,
va trebui s instaleze o anten de emisie special pentru aceasta.
O anten emite unde radio într-o zon circular . Centrul zonei coincide cu punctul în care
este poziµionat antena. Raza zonei este denumit puterea antenei. Cu cât puterea antenei este
mai mare, cu atât antena este mai scump .
Prin urmare trebuie selectat o poziµie optim de amplasare a antenei, astfel încât ecare cas
s se ae în interiorul sau pe frontiera zonei circulare în care emite antena, iar puterea antenei s
e minim .
Cerinµ
Scrieµi un program care s determine o poziµie optim de amplasare a antenei, precum ³i
puterea minim a acesteia.
Datele de intrare
Fi³ierul de intrare antena.in conµine pe prima linie un num r natural n, reprezentând num rul
de case din zon . Pe urm toarele n linii se a poziµiile caselor. Mai exact, pe linia i 1 se a
dou numere întregi separate printr-un spaµiu x y , ce reprezint abscisa ³i respectiv ordonata casei
i. Nu exist dou case în aceea³i locaµie.
Datele de ie³ire
Fi³ierul de ie³ire antena.out conµine pe prima linie dou numere reale separate printr-un
spaµiu x y reprezentând abscisa ³i ordonata poziµiei optime de amplasare a antenei.
Pe cea de a doua linie se va scrie un num r real reprezentând puterea antenei.
Restricµii ³i preciz ri
a 2$N $ 15001
a 15000 $ x, y $ 15001
a Numerele reale din ³ierul de ie³ire trebuie scrise cu trei zecimale cu rotunjire.
a La evaluare, se veric dac diferenµa dintre soluµia a³at ³i cea corect (în valoare absolut )
este $ 0.01.
Exemplu
antena.in antena.out Explicaµie
7 3.250 2.875 Antena va plasat în punctul
5 0 3.366 de coordonate 3.250, 2.825 iar
2 6 puterea antenei este 3.366
4 5
2 2
0 2
3 6
5 2
Timp maxim de execuµie/test: 0.3 secunde pentru Windows ³i 0.1 secunde pentru Linux.
import java.io.*; // practic, trebuie sa determinam cele trei puncte
class Antena // prin care trece cercul care le acopera pe toate!!!
{
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 304
static int n;
static int[] x,y;
static double x0, y0, r0;
st.nextToken(); n=(int)st.nval;
x=new int[n+1];
y=new int[n+1];
for(k=1;k<=n;k++)
{
st.nextToken(); x[k]=(int)st.nval;
st.nextToken(); y[k]=(int)st.nval;
}
if(n>3)
{
puncteExtreme();
cercDeDiametru(x[1],y[1],x[2],y[2]);
for(k=3;k<=n;k++)
if(!esteInCerc(k))
cercPrin(x[k],y[k],k-1); // trece prin Pk si acopera 1,2,...,k-1
}
else cercCircumscris(x[1],y[1],x[2],y[2],x[3],y[3]);
for(j=2;j<=k;j++)
if(!esteInCerc(j))
cercPrin(xx,yy,x[j],y[j],j-1); // ... acopera 1,2,...,j-1
}// cercPrin(...)
if(!esteInCerc(i))
cercCircumscris(xx,yy,xxx,yyy,x[i],y[i]);
}// cercPrin(...)
// caut cel mai din stanga punct (si mai jos) si-l pun pe pozitia 1
// (caut incepand cu pozitia 1)
kmin=1; min=x[1];
for(k=2;k<=n;k++)
if((x[k]<min)||(x[k]==min)&&(y[k]<y[kmin])) {min=x[k]; kmin=k;}
if(kmin!=1) swap(1,kmin);
// caut cel mai din dreapta (si mai sus) punct si-l pun pe pozitia 2
// (caut incepand cu pozitia 2)
kmax=2; max=x[2];
for(k=3;k<=n;k++)
if((x[k]>max)||(x[k]==max)&&(y[k]>y[kmax])) {max=x[k]; kmax=k;}
if(kmax!=2) swap(2,kmax);
// caut cel mai de jos (si mai la dreapta) punct si-l pun pe pozitia 3
// (caut incepand cu pozitia 3)
kmin=3; min=y[3];
for(k=4;k<=n;k++)
if((y[k]<min)||(y[k]==min)&&(x[k]>x[kmin])) {min=y[k]; kmin=k;}
if(kmin!=3) swap(3,kmin);
// caut cel mai de sus (si mai la stanga) punct si-l pun pe pozitia 4
// (caut incepand cu pozitia 4)
kmax=4; max=y[4];
for(k=5;k<=n;k++)
if((y[k]>max)||(y[k]==max)&&(x[k]<x[kmax])) {max=y[k]; kmax=k;}
if(kmax!=4) swap(4,kmax);
double a12, a13, b12, b13, c12, c13; // int ==> eroare !!!
if(a12*b13-a13*b12!=0)
{
x0=(c12*b13-c13*b12)/(a12*b13-a13*b12);
y0=(a12*c13-a13*c12)/(a12*b13-a13*b12);
r0=Math.sqrt((x1-x0)*(x1-x0)+(y1-y0)*(y1-y0));
}
else // consider cercul de diametru [(minx,maxx),(miny,maxy)]
{ // punctele sunt coliniare !
x0=(max(x1,x2,x3)+min(x1,x2,x3))/2;
y0=(max(y1,y2,y3)+min(y1,y2,y3))/2;
r0=d(x0,y0,x1,y1)/2;
}
}// cercCircumscris(...)
static double d(int x1, int y1, int x2, int y2)
{
double dx,dy;
dx=x2-x1;
dy=y2-y1;
return Math.sqrt(dx*dx+dy*dy);
}
static double d(double x1, double y1, double x2, double y2)
{
double dx,dy;
dx=x2-x1;
dy=y2-y1;
return Math.sqrt(dx*dx+dy*dy);
}
//interschimb punctele i si j
static void swap(int i, int j)
{
int aux;
aux=x[i]; x[i]=x[j]; x[j]=aux;
aux=y[i]; y[i]=y[j]; y[j]=aux;
}// swap(...)
}// class
P cal a primit, a³a cum era învoiala, un petec de teren de pe mo³ia boierului. Terenul este
împrejmuit complet cu segmente drepte de gard ce se sprijin la ambele capete de câte un par
zdrav n. La o nou prinsoare, P cal iese iar în câ³tig ³i prime³te dreptul s str mute ni³te pari,
unul câte unul, cum i-o voia, astfel încât s -³i extind suprafaµa de teren. Dar învoiala prevede
c ecare par poate mutat în orice direcµie, dar nu pe o distanµ mai mare decât o valoare dat
(scris pe ecare par) ³i ecare segment de gard, ind cam ³uubred, poate rotit ³i prelungit de
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 307
Ionic a primit de ziua lui de la tat l s u un joc format din piese de form triunghiular de
dimensiuni diferite ³i o suprafaµu a magnetic pe care acestea pot a³ezate.
Pe suprafaµa magnetic este desenat un triunghi dreptunghic cu lungimile catetelor a, respectiv
b ³i un sistem de coordonate xOy cu originea în unghiul drept al triunghiului, semiaxa Ox pe
cateta de lungime a, respectiv semiaxa Oy pe cateta de lungime b.
La un moment dat Ionic a³eaz pe tabla magnetic n piese, pentru care se cunosc coordo-
natele vârfurilor lor. Tat l lui Ionic vrea s verice dac pe tabl piesele realizeaz o partiµie a
triunghiului dreptunghic desenat, adic dac sunt îndeplinite condiµiile:
a nu exist piese suprapuse;
a piesele acoper toat porµiunea desenat (în form de triunghi dreptunghic);
a nu exist porµiuni din piese în afara triunghiului desenat.
Cerinµ
Se cere s se verice dac piesele plasate pe tabla magnetic formeaz o partiµie a triunghiului
desenat pe tabla magnetic .
Date de intrare
Fi³ierul de intrare part.in conµine pe prima linie un num r natural k , reprezentând num rul
de seturi de date din ³ier. Urmeaz k grupe de linii, câte o grup pentru ecare set de date.
Grupa de linii corespunz toare unui set este format dintr-o linie cu numerele a, b, n separate
între ele prin câte un spaµiu ³i n linii cu câte ³ase numere întregi separate prin spaµii reprezentând
coordonatele vârfurilor (abscis ordonat ) celor n piese, câte o pies pe o linie.
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 308
Date de ie³ire
În ³ierul part.out se vor scrie k linii, câte o linie pentru ecare set de date. Pe linia i
(i 1, 2, ..., k ) se va scrie 1 dac triunghiurile din setul de date i formeaz o partiµie a triunghiului
desenat pe tabla magnetic sau 0 în caz contrar.
Restricµii ³i preciz ri
a 1 & n & 150
a 1 & k & 10
a a, b sunt numere întregi din intervalul 0, 31000
a Coordonatele vârfurilor pieselor sunt numere întregi din intervalul [0, 31000].
Exemplu
part.in part.out
2 1
20 10 4 0
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
y y
10 10
T1
5 5 T1
T2 T2
T3 T4
x x
10 20 10 20
a) b)
static int N, A, B;
static int[][] X=new int[N_MAX][3];
static int[][] Y=new int[N_MAX][3];
static int point_sign (int x1, int y1, int x2, int y2, int _x, int _y)
{
int a, b, c;
a=y2-y1;
b=x1-x2;
c=y1*x2-x1*y2;
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 309
return sgn(a*_x+b*_y+c);
}
area+=Math.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(area!=A*B) return 0;
for(i=0;i<N;i++)
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 310
for(j=i+1;j<N;j++)
if(triangle_intersect(i,j)) return 0;
return 1;
}
st.nextToken(); tests=(int)st.nval;
for(; tests-->0;)
{
st.nextToken(); A=(int)st.nval;
st.nextToken(); B=(int)st.nval;
st.nextToken(); N=(int)st.nval;
for(i=0;i<N;i++)
for(j=0;j<3;j++)
{
st.nextToken(); X[i][j]=(int)st.nval;
st.nextToken(); Y[i][j]=(int)st.nval;
}
X[N][0]=0; Y[N][0]=0;
X[N][1]=A; Y[N][1]=0;
X[N][2]=0; Y[N][2]=B;
out.println(solve());
}
out.close();
}// main(...)
}// class
În comuna Triunghi din România sunt n µ rani codicaµi prin numerele 1, 2, ..., n. Dup
anul 1990 a început retrocedarea suprafeµelor de p mânt deµinute înainte de colectivizare. Fiecare
µ ran are un document prin care dovede³te c este proprietar pe o singur suprafaµ de teren de
form triunghiular . Din p cate, documentele dau b taie de cap primarului (care se ocup de
retrocedarea suprafeµelor de p mânt), pentru c sunt porµiuni din suprafeµele de p mânt care se
reg sesc pe mai multe documente.
În aceast comun exist o fântân cu ap , ind posibil ca ea s e revendicat de mai mulµi
µ rani. O suprafaµ de p mânt este dat prin coordonatele celor trei colµuri, iar fântâna este
considerat punctiform ³i dat prin coordonatele punctului.
Cerinµ
S se scrie un program care s determine:
a Codurile µ ranilor care au documente cu suprafeµe de p mânt ce conµin în interior sau pe
frontier fântâna.
b Codul µ ranului ce deµine un document cu suprafaµa de teren, care include toate celelalte
suprafeµe.
Date de intrare
Fi³ierul de intrare triunghi.in are pe prima linie num rul n de µ rani, pe urm toarele n linii
câte 6 valori numere întregi separate prin câte un spaµiu, în formatul: x1 y1 x2 y2 x3 y3, ce
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 311
reprezint coordonatele celor trei colµuri ale suprafeµei triunghiulare deµinute de un µ ran (x1, x2,
x3 abscise, iar y1, y2, y3 ordonate). Pe linia i 1 se a coordonatele colµurilor suprafeµei de
teren triunghiulare deµinute de µ ranul i, i 1, 2, ..., n. Ultima linie a ³ierului (linia n 2) va
conµine coordonatele fântânii în formatul x y , cu un spaµiu între ele (x abscis , iar y ordonat ).
Date de ie³ire
Fi³ierul de ie³ire triunghi.out va conµine pe prima linie r spunsul de la punctul a, adic :
num rul de µ rani care îndeplinesc condiµia din cerinµ ³i apoi codurile lor (în ordine cresc toare),
cu un spaµiu între ele. Dac nu exist µ rani cu condiµia din cerinµ , pe prima linie se va scrie cifra
0. Pe linia a doua se va scrie r spunsul de la punctul b, adic : codul µ ranului cu proprietatea
cerut , sau cifra 0, dac nu exist un astfel de µ ran.
Restricµii ³i preciz ri
a 2 & n & 65
a coordonatele colµurilor suprafeµelor de p mânt ³i ale fântânii sunt numere întregi din inter-
valul 3000, 3000
a cele trei colµuri ale ec rei suprafeµe de p mânt sunt distincte ³i necoliniare
a nu exist doi µ rani care s deµin aceea³i suprafaµ de p mânt
a nu se acord punctaje parµiale.
Exemplu
secv.in secv.out
3 212
10 0 0 10 10 10 2
0 100 100 0 -100 0
0 0 10 0 0 10
10 5
Explicaµie:
La punctul a, sunt doi µ rani care deµin suprafeµe de p mânt ce au în interior sau pe frontier
fântâna, cu codurile 1 ³i 2.
La punctul b, µ ranul cu codul 2 deµine o suprafaµ de teren care include, suprafeµele de
p mânt deµinute de ceilalµi µ rani (cu codurile 1 ³i 3).
Timp maxim de execuµie/test: 0.1 secunde
Codul surs
import java.io.*;
class Pereti
{
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 312
//System.out.println("imax = "+imax);
ok=true;
for(i=1;i<=n;i++)
{
if(i==imax) continue;
s1=aria(x1[imax],y1[imax],x2[imax],y2[imax],x1[i],y1[i]);
s2=aria(x2[imax],y2[imax],x3[imax],y3[imax],x1[i],y1[i]);
s3=aria(x1[imax],y1[imax],x3[imax],y3[imax],x1[i],y1[i]);
if(smax!=s1+s2+s3) { ok=false; break; }
CAPITOLUL 17. GEOMETRIE COMPUTAIONAL 313
s1=aria(x1[imax],y1[imax],x2[imax],y2[imax],x2[i],y2[i]);
s2=aria(x2[imax],y2[imax],x3[imax],y3[imax],x2[i],y2[i]);
s3=aria(x1[imax],y1[imax],x3[imax],y3[imax],x2[i],y2[i]);
if(smax!=s1+s2+s3) { ok=false; break; }
s1=aria(x1[imax],y1[imax],x2[imax],y2[imax],x3[i],y3[i]);
s2=aria(x2[imax],y2[imax],x3[imax],y3[imax],x3[i],y3[i]);
s3=aria(x1[imax],y1[imax],x3[imax],y3[imax],x3[i],y3[i]);
if(smax!=s1+s2+s3) { ok=false; break; }
}
if(ok) out.println(imax); else out.println(0);
out.close();
}// main(...)
static int aria(int x1, int y1, int x2, int y2, int x3, int y3) // dubla ...
{
int s=x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1;
if(s<0) s=-s;
return s;
}
}// class
Capitolul 18
Teoria jocurilor
18.1.2 Exemple
314
Capitolul 19
Alµi algoritmi
19.1.2 Exemple
315
CAPITOLUL 19. ALI ALGORITMI 316
import java.io.*;
class BellmanFord
{
static final int oo=0x7fffffff; // infinit
static int n,m; // varfuri, muchii
st.nextToken(); n=(int)st.nval;
st.nextToken(); m=(int)st.nval;
w=new int[n+1][n+1];
d=new int[n+1];
p=new int[n+1];
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
w[i][j]=oo;// initializare !
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost=(int)st.nval;
w[i][j]=cost;
w[j][i]=cost; // numai pentru graf neorientat
}
init(nods);
boolean cicluNegativ=false;
for(u=1;u<n;u++)
for(v=u+1;v<=n;v++)
if(w[u][v]<oo) // (u,v)=muchie
if(d[u]<oo) // atentie !!! oo+ceva=???
if(d[v]>d[u]+w[u][v])
{
cicluNegativ=true;
break;
}
if(!cicluNegativ)
for(k=1;k<=n;k++)
{
System.out.print(nods+"-->"+k+" dist="+d[k]+" drum: ");
drum(k);
System.out.println();
}
}//main
/*
6 7
1 2 -3 1-->1 dist=0 drum: 1
1 3 1 1-->2 dist=-3 drum: 1 2
2 3 2 1-->3 dist=-1 drum: 1 2 3
CAPITOLUL 19. ALI ALGORITMI 318
import java.io.*;
class BellmanFord
{
static final int oo=0x7fffffff;
static int n,m; // varfuri, muchii
static int[][] w; // matricea costurilor
static int[] d; // d[u]=dist(nods,u)
static int[] p; // p[u]=predecesorul nodului u
w=new int[n+1][n+1];
d=new int[n+1];
p=new int[n+1];
for(i=1;i<=n;i++) for(j=1;j<=n;j++) w[i][j]=oo; // initializare !
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost=(int)st.nval;
w[i][j]=cost;
}
init(nods);
for(k=1;k<=n-1;k++) // de n-1 ori !!!
for(u=1;u<=n;u++) // vectorii arcelor erau mai buni !
for(v=1;v<=n;v++) // lista de adiacenta era mai buna !
if(w[u][v]<oo) // (u,v)=arc
relax(u,v);
boolean cicluNegativ=false;
for(u=1;u<=n;u++)
for(v=1;v<=n;v++)
CAPITOLUL 19. ALI ALGORITMI 319
if(w[u][v]<oo) // (u,v)=arc
if(d[u]<oo) // atentie !!! oo+ceva=???
if(d[v]>d[u]+w[u][v])
{
cicluNegativ=true;
break;
}
System.out.println(cicluNegativ);
if(!cicluNegativ)
for(k=1;k<=n;k++)
{
System.out.print(nods+"-->"+k+" dist="+d[k]+" drum: ");
if(d[k]<oo) drum(k); else System.out.print("Nu exista drum!");
System.out.println();
}
}//main
/*
6 8 false
1 2 -3 1-->1 dist=0 drum: 1
1 3 1 1-->2 dist=-4 drum: 1 3 2
2 3 6 1-->3 dist=1 drum: 1 3
3 4 1 1-->4 dist=2 drum: 1 3 4
5 4 1 1-->5 dist=2147483647 drum: Nu exista drum!
5 6 -3 1-->6 dist=4 drum: 1 3 4 6
4 6 2
3 2 -5
*/
CAPITOLUL 19. ALI ALGORITMI 320
import java.io.*;
class BellmanFordDAG
{
static final int oo=0x7fffffff;
static final int WHITE=0, BLACK=1; // color[u]=BLACK ==> u in lista
static int n,m,t,pozl; // varfuri, muchii, time, pozitie in lista
static int[] color; // culoare
static int[] lista; // lista
static int[] gi; // grad interior
static int[][] w; // matricea costurilor
static int[] d; // d[u]=dist(nods,u)
static int[] p; // p[u]=predecesorul nodului u
w=new int[n+1][n+1];
color=new int[n+1];
lista=new int[n+1];
gi=new int[n+1];
d=new int[n+1];
p=new int[n+1];
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) w[i][j]=oo; // initializare !
for(k=1;k<=m;k++)
{
st.nextToken(); i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
st.nextToken(); cost=(int)st.nval;
w[i][j]=cost;
gi[j]++;
}
topoSort();
System.out.print("Lista : "); afisv(lista);
init(nods);
for(i=1;i<=n;i++)
{
u=lista[i];
for(v=1;v<=n;v++)
CAPITOLUL 19. ALI ALGORITMI 321
System.out.print("Distante : ");
afisv(d);
for(k=1;k<=n;k++)
{
if(d[k]<oo) System.out.print(k+" : "+d[k]+" ... ");
else System.out.print(k+": oo ... ");
drum(k);
System.out.println();
}
}//main
return nod;
}// nodgi0()
/*
6 7 Lista : 1 3 2 5 4 6
1 2 -3 Distante : 0 -4 1 2 oo 4
1 3 1 1 : 0 ... 1
3 4 1 2 : -4 ... 1 3 2
5 4 1 3 : 1 ... 1 3
5 6 -3 4 : 2 ... 1 3 4
4 6 2 5: oo ...
3 2 -5 6 : 4 ... 1 3 4 6
*/
323
Anexa A
Un pic de matematic !
A.1 ...
...
A.1.2 Exemple
...
324
Anexa B
Un pic de programare!
B.1 ...
...
B.1.2 Exemple
...
325
Glosar
326
Bibliograe
[1] Aho, A., Hopcroft, J., Ullman, J.D.; Data strutures and algorithms, Addison Wesley, 1983
[2] Andreica M.I.; Elemente de algoritmic - probleme ³i soluµii, Cibernetica MC, 2011
[3] Andonie R., Gârbacea I.; Algoritmi fundamentali, o perspectiv C++, Ed. Libris, 1995
[5] Bell D., Perr M.; Java for Students, Second Edition, Prentice Hall, 1999
[7] Cerchez, E., erban, M.; Informatic - manual pentru clasa a X-a., Ed. Polirom, 2000
[8] Cerchez, E.; Informatic - Culegere de probleme pentru liceu, Ed. Polirom, 2002
[9] Cerchez, E., erban, M.; Programarea în limbajul C/C++ pentru liceu, Ed. Polirom, 2005
[10] Cori, R.; Lévy, J.J.; Algorithmes et Programmation, Polycopié, version 1.6;
http://w3.edu.polytechnique.fr/informatique/
[11] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Introducere în Algoritmi, Ed Agora, 2000
[12] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Pseudo-Code Language, 1994
[13] Cristea, V.; Giumale, C.; Kalisz, E.; Paunoiu, Al.; Limbajul C standard, Ed. Teora, Bucure³ti,
1992
[16] Giumale C., Negreanu L., C linoiu S.; Proiectarea ³i analiza algoritmilor. Algoritmi de sortare,
Ed. All, 1997
[18] Knuth, D.E.; Arta program rii calculatoarelor, vol. 1: Algoritmi fundamentali, Ed. Teora,
1999.
[19] Knuth, D.E.; Arta programarii calculatoarelor, vol. 2: Algoritmi seminumerici, Ed. Teora,
2000.
[20] Knuth, D.E.; Arta programarii calculatoarelor, vol. 3: Sortare ³i c utare, Ed. Teora, 2001.
[21] Knuth, D.E.; The art of computer programming, vol. 4A: Combinatorial algorithms, Part 1,
Addison Wesley, 2011.
[22] Lambert,K. A., Osborne,M.; Java. A Framework for Programming and Problem Solving,
PWS Publishing, 1999
[24] Livovschi, L.; Georgescu H.; Analiza ³i sinteza algoritmilor. Ed. Enciclopedic , Bucure³ti,
1986.
327
BIBLIOGRAFIE 328
[26] Od gescu, I., Smeureanu, I., tef nescu, I.; Programarea avansat a calculatoarelor personale,
Ed. Militar , Bucure³ti 1993
[27] Od gescu, I.; Metode ³i tehnici de programare, Ed. Computer Lobris Agora, Cluj, 1998
[28] Popescu Anastasiu, D.; Puncte de articulaµie ³i punµi în grafuri, Gazeta de Informatic nr.
5/1993
[35] Tomescu, I.; Probleme de combinatoric ³i teoria grafurilor, Editura Didactic ³i Pedagogic ,
Bucure³ti, 1981
[36] Tomescu, I.; Leu, A.; Matematic aplicat în tehnica de calcul, Editura Didactic ³i Pedago-
gic , Bucure³ti, 1982
[37] Tudor, S.; Informatic - prolul real intensiv, varianta C++; Editura L&S, Bucure³ti, 2004
[38] Tudor, S.; Hutanu, V,; Informatic intensiv; Editura L&S, Bucure³ti, 2006
[40] Vi³inescu, R.; Vi³inescu, V.; Programare dinamic - teorie ³i aplicaµii; GInfo nr. 15/4 2005
[41] Vlada, M.; Conceptul de algoritm - abordare modern , GInfo, 13/2,3 2003
[43] Vlada, M.; Gândirea Algoritmic - O Filosoe Modern a Matematicii ³i Informaticii, CNIV-
2003, Editura Universit µii din Bucure³ti, 2003
[44] Weis, M.A.; Data structures and Algorithm Analysis, Ed. The Benjamin/Cummings Pu-
blishing Company. Inc., Redwoods City, California, 1995.
[46] Wirth N.; Algorithms + Data Structures = Programs, Prentice Hall, Inc 1976
Adi, 1
330