P. 1
Algoritmi si structuri de date

Algoritmi si structuri de date

5.0

|Views: 12,161|Likes:
Published by liceulneagoe808

More info:

Published by: liceulneagoe808 on Feb 14, 2011
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

11/07/2014

pdf

text

original

Sections

  • Not¸iuni fundamentale
  • 1.1 Programe ciudate
  • 1.1.1 Un program ciudat ˆın Pascal
  • 1.1.2 Un program ciudat ˆın C++
  • 1.1.3 Un program ciudat ˆın Java
  • 1.1.4 Structura unui program Java
  • 1.2 Conversii ale datelor numerice
  • 1.2.1 Conversia din baza 10 ˆın baza 2
  • 1.2.2 Conversia din baza 2 ˆın baza 10
  • 1.2.3 Conversii ˆıntre bazele 2 ¸si 2r
  • 2.1 Date ¸si structuri de date
  • 2.1.1 Date
  • 2.1.2 Structuri de date
  • 2.2 Structuri ¸si tipuri de date abstracte
  • 2.2.1 Structuri de date abstracte
  • 2.2.2 Tipuri de date abstracte
  • 2.3 Structuri de date elementare
  • 2.3.1 Liste
  • 2.3.2 Stive ¸si cozi
  • 2.3.3 Grafuri
  • 2.3.4 Arbori binari
  • 2.3.5 Heap-uri
  • 2.3.6 Structuri de mult¸imi disjuncte
  • 3.1 Etape ˆın rezolvarea problemelor
  • 3.2 Algoritmi
  • 3.2.1 Ce este un algoritm?
  • 3.2.2 Propriet˘at¸ile algoritmilor
  • 3.2.3 Tipuri de prelucr˘ari
  • 3.3 Descrierea algoritmilor
  • 3.3.1 Limbaj natural
  • 3.3.2 Scheme logice
  • 3.3.3 Pseudocod
  • 3.4 Limbaj algoritmic
  • 3.4.1 Declararea datelor
  • 3.4.2 Operat¸ii de intrare/ie¸sire
  • 3.4.3 Prelucr˘ari liniare
  • 3.4.4 Prelucr˘ari alternative
  • 3.4.5 Prelucr˘ari repetitive
  • 3.4.7 Probleme rezolvate
  • 3.4.8 Probleme propuse
  • 3.5 Instruct¸iuni corespondente limbajului algorit-
  • 3.5.1 Declararea datelor
  • 3.5.2 Operat¸ii de intrare/ie¸sire
  • 3.5.3 Prelucr˘ari liniare
  • 3.5.4 Prelucr˘ari alternative
  • 3.5.5 Prelucr˘ari repetitive
  • 3.5.7 Probleme rezolvate
  • 3.5.8 Probleme propuse
  • 4.1 Scopul analizei complexit˘at¸ii
  • 4.1.1 Complexitatea spat¸iu
  • 4.1.2 Complexitatea timp
  • 4.2 Notat¸ia asimptotic˘a
  • 4.2.1 Definire ¸si propriet˘at¸i
  • 4.2.2 Clase de complexitate
  • 4.2.3 Cazul mediu ¸si cazul cel mai defavorabil
  • 4.2.4 Analiza asimptotic˘a a structurilor fundamentale
  • 4.3 Exemple
  • 4.3.1 Calcularea maximului
  • 4.3.2 Sortarea prin select¸ia maximului
  • 4.3.3 Sortarea prin insert¸ie
  • 4.3.4 Sortarea rapid˘a (quicksort)
  • 4.3.5 Problema celebrit˘at¸ii
  • 4.4 Probleme
  • 4.4.1 Probleme rezolvate
  • 4.4.2 Probleme propuse
  • Recursivitate
  • 5.1 Funct¸ii recursive
  • 5.1.1 Funct¸ii numerice
  • 5.1.2 Funct¸ia lui Ackerman
  • 5.1.3 Recursii imbricate
  • 5.2 Proceduri recursive
  • 6.1 Relat¸ii de recurent¸˘a
  • 6.1.1 Ecuat¸ia caracteristic˘a
  • 6.1.2 Solut¸ia general˘a
  • 6.2 Ecuat¸ii recurente neomogene
  • 6.2.1 O form˘a simpl˘a
  • 6.2.2 O form˘a mai general˘a
  • 6.2.3 Teorema master
  • 6.2.4 Transformarea recurent¸elor
  • 6.3 Probleme rezolvate
  • Algoritmi elementari
  • 7.1 Operat¸ii cu numere
  • 7.1.1 Minim ¸si maxim
  • 7.1.2 Divizori
  • 7.1.3 Numere prime
  • 7.2 Algoritmul lui Euclid
  • 7.2.1 Algoritmul clasic
  • 7.2.2 Algoritmul lui Euclid extins
  • 7.3 Operat¸ii cu polinoame
  • 7.3.1 Adunarea a dou˘a polinoame
  • 7.3.2 ˆInmult¸irea a dou˘a polinoame
  • 7.3.3 Calculul valorii unui polinom
  • 7.3.4 Calculul derivatelor unui polinom
  • 7.4 Operat¸ii cu mult¸imi
  • 7.4.1 Apartenent¸a la mult¸ime
  • 7.4.2 Diferent¸a a dou˘a mult¸imi
  • 7.4.3 Reuniunea ¸si intersect¸ia a dou˘a mult¸imi
  • 7.4.4 Produsul cartezian a dou˘a mult¸imi
  • 7.4.5 Generarea submult¸imilor unei mult¸imi
  • 7.5 Operat¸ii cu numere ˆıntregi mari
  • 7.5.1 Adunarea ¸si sc˘aderea
  • 7.5.2 Inmult¸irea ¸si ˆımp˘artirea
  • 7.5.3 Puterea
  • 7.6 Operat¸ii cu matrice
  • 7.6.1 ˆInmult¸irea
  • 7.6.2 Inversa unei matrice
  • Algoritmi combinatoriali
  • 8.1 Principiul includerii ¸si al excluderii ¸si aplicat¸ii
  • 8.1.1 Principiul includerii ¸si al excluderii
  • 8.1.2 Num˘arul funct¸iilor surjective
  • 8.1.3 Num˘arul permut˘arilor f˘ar˘a puncte fixe
  • 8.2 Principiul cutiei lui Dirichlet ¸si aplicat¸ii
  • 8.2.1 Problema subsecvent¸ei
  • 8.2.2 Problema sub¸sirurilor strict monotone
  • 8.3 Numere remarcabile
  • 8.3.1 Numerele lui Fibonacci
  • 8.3.2 Numerele lui Catalan
  • 8.4 Descompunerea ˆın factori primi
  • 8.4.1 Funct¸ia lui Euler
  • 8.4.2 Num˘arul divizorilor
  • 8.4.3 Suma divizorilor
  • 8.5 Partit¸ia numerelor
  • 8.5.1 Partit¸ia lui n ˆın exact k termeni
  • 8.5.2 Partit¸ia lui n ˆın cel mult k termeni
  • 8.5.3 Partit¸ii multiplicative
  • 8.6 Partit¸ia mult¸imilor
  • 8.7 Probleme rezolvate
  • Algoritmi de c˘autare
  • 9.1 Problema c˘aut˘arii
  • 9.2 C˘autarea secvent¸ial˘a
  • 9.3 C˘autare binar˘a
  • 9.4 Inserare ˆın tabel˘a
  • 9.5 Dispersia
  • 10.1 Introducere
  • 10.2 Sortare prin select¸ie
  • 10.3 Sortare prin insert¸ie
  • 10.3.1 Insert¸ie direct˘a
  • 10.3.2 Insert¸ie binar˘a
  • 10.4 Sortare prin interschimbare
  • 10.5 Sortare prin mic¸sorarea incrementului - shell
  • 11.1 Liste liniare
  • 11.2 Cozi
  • 11.3 Stive
  • 11.4. EVALUAREA EXPRESIILOR ARITMETICE PREFIXATE 157
  • 11.4 Evaluarea expresiilor aritmetice prefixate
  • 11.5 Operat¸ii asupra listelor
  • Algoritmi divide et impera
  • 12.1 Tehnica divide et impera
  • 12.2 Ordinul de complexitate
  • 12.3 Exemple
  • 12.3.1 Sortare prin partitionare - quicksort
  • 12.3.2 Sortare prin interclasare - MergeSort
  • 12.3.3 Placa cu g˘auri
  • 12.3.4 Turnurile din Hanoi
  • 12.3.5 ˆInjum˘at˘at¸ire repetat˘a
  • Algoritmi BFS-Lee
  • 13.1 Prezentare general˘a
  • 13.2 Probleme rezolvate
  • 13.2.1 Romeo ¸si Julieta - OJI2004 clasa a X-a
  • 13.2.2 Sudest - OJI2006 clasa a X-a
  • 13.2.3 Muzeu - ONI2003 clasa a X-a
  • 13.2.4 P˘aianjen ONI2005 clasa a X-a
  • 13.2.5 Algoritmul Edmonds-Karp
  • 13.2.6 Cuplaj maxim
  • 14.1 Metoda greedy
  • 14.2 Algoritmi greedy
  • 14.3 Exemple
  • 14.3.1 Problema continu˘a a rucsacului
  • 14.3.2 Problema plas˘arii textelor pe o band˘a
  • 14.3.3 Problema plas˘arii textelor pe m benzi
  • 14.3.4 Maximizarea unei sume de produse
  • 14.3.5 Problema stat¸iilor
  • 14.3.6 Problema cutiilor
  • 14.3.7 Problema sub¸sirurilor
  • 14.3.8 Problema intervalelor disjuncte
  • 14.3.9 Problema alegerii taxelor
  • 14.3.10 Problema acoperirii intervalelor
  • 14.3.11 Algoritmul lui Prim
  • 14.3.12 Algoritmul lui Kruskal
  • 14.3.13 Algoritmul lui Dijkstra
  • 14.3.14 Urgent¸a - OJI2002 cls 11
  • 14.3.16 Pal - ONI2005 cls 9
  • 14.3.17 S¸ant¸ - ONI2006 cls 9
  • Metoda backtracking
  • 15.1 Generarea produsului cartezian
  • 15.1.1 Generarea iterativ˘a a produsului cartezian
  • 15.1.2 Generarea recursiv˘a a produsului cartezian
  • 15.2 Metoda bactracking
  • 15.2.1 Bactracking iterativ
  • 15.2.2 Backtracking recursiv
  • 15.3 Probleme rezolvate
  • 15.3.1 Generarea aranjamentelor
  • 15.3.2 Generarea combin˘arilor
  • 15.3.3 Problema reginelor pe tabla de ¸sah
  • 15.3.4 Turneul calului pe tabla de ¸sah
  • 15.3.5 Problema color˘arii h˘art¸ilor
  • 15.3.6 Problema vecinilor
  • 15.3.7 Problema labirintului
  • 15.3.8 Generarea partit¸iilor unui num˘ar natural
  • 15.3.9 Problema parantezelor
  • 15.3.10 Algoritmul DFS de parcurgere a grafurilor
  • 15.3.11 Determinarea componentelor conexe
  • 15.3.12 Determinarea componentelor tare conexe
  • 15.3.13 Sortare topologic˘a
  • 15.3.14 Determinarea nodurilor de separare
  • 15.3.15 Determinarea muchiilor de rupere
  • 15.3.16 Determinarea componentelor biconexe
  • 15.3.17 Triangulat¸ii - OJI2002 clasa a X-a
  • 15.3.18 Partit¸ie - ONI2003 clasa a X-a
  • 15.3.19 Scufit¸a - ONI2003 clasa a X-a
  • Programare dinamic˘a
  • 16.1 Prezentare general˘a
  • 16.2 Probleme rezolvate
  • 16.2.1 Inmult¸irea optimal˘a a matricelor
  • 16.2.2 Sub¸sir cresc˘ator maximal
  • 16.2.3 Sum˘a maxim˘a ˆın triunghi de numere
  • 16.2.4 Sub¸sir comun maximal
  • 16.2.5 Distant¸a minim˘a de editare
  • 16.2.6 Problema rucsacului (0−1)
  • 16.2.7 Problema schimbului monetar
  • 16.2.8 Problema travers˘arii matricei
  • 16.2.9 Problema segment˘arii vergelei
  • 16.2.10 Triangularizarea poligoanelor convexe
  • 16.2.11 Algoritmul Roy-Floyd-Warshall
  • 16.2.12 Oracolul decide - ONI2001 cls 10
  • 16.2.13 Pav˘ari - ONI2001 clasa a X-a
  • 16.2.14 Balant¸a ONI2002 clasa a X-a
  • 16.2.15 Aliniere ONI2002 clasa a X-a
  • 16.2.17 L˘acusta - OJI2005 clasa a X-a
  • 16.2.18 Avere ONI2005 cls 10
  • 16.2.19 Suma - ONI2005 cls 10
  • Potrivirea ¸sirurilor
  • 17.1 Un algoritm ineficient
  • 17.2 Un algoritm eficient - KMP
  • 17.3 Probleme rezolvate
  • 17.3.1 Circular - Campion 2003-2004 Runda 6
  • 17.3.2 Cifru - ONI2006 baraj
  • Geometrie computat¸ional˘a
  • 18.1 Determinarea orient˘arii
  • 18.2 Testarea convexit˘at¸ii poligoanelor
  • 18.3 Aria poligoanelor convexe
  • 18.4 Pozit¸ia unui punct fat¸˘a de un poligon convex
  • 18.5 Pozit¸ia unui punct fat¸˘a de un poligon concav
  • 18.6 ˆInf˘a¸sur˘atoarea convex˘a
  • 18.6.1 ˆImpachetarea Jarvis
  • 18.6.2 Scanarea Craham
  • 18.7 Dreptunghi minim de acoperire a punctelor
  • 18.8. CERC MINIM DE ACOPERIRE A PUNCTELOR 463
  • 18.8 Cerc minim de acoperire a punctelor
  • 18.9 Probleme rezolvate
  • 18.9.1 Seceta - ONI2005 clasa a IX-a
  • 18.9.2 Antena - ONI2005 clasa a X-a
  • 18.9.3 Mo¸sia lui P˘acal˘a - OJI2004 clasa a XI-a
  • 18.9.4 Partit¸ie - ONI2006 baraj
  • 18.9.5 Triunghi - ONI2007 cls 9
  • Teoria jocurilor
  • 19.1 Jocul NIM
  • 19.1.1 Prezentare general˘a
  • Alt¸i algoritmi
  • 20.1 Secvent¸˘a de sum˘a maxim˘a
  • 20.1.1 Prezentare general˘a
  • 20.1.2 Exemple
  • 20.2 Algoritmul Belmann-Ford
  • 20.2.1 Algoritmul Belmann-Ford pentru grafuri neorientate
  • 20.2.2 Alg Belmann-Ford pentru grafuri orientate

ALGORITMI SI STRUCTURI DE DATE ¸ Note de curs

(uz intern - draft v2.3)

Prefat˘ ¸a
Cˆnd dorim s˘ reprezent˘m obiectele din lumea real˘ ˆ a a a a ıntr-un program pe calculator, trebuie s˘ avem ˆ vedere: a ın • modelarea obiectelor din lumea real˘ sub forma unor entit˘¸i matematice a at abstracte ¸i tipuri de date, s • operatiile pentru ˆ ¸ ınregistrarea, accesul ¸i utilizarea acestor entit˘¸i, s at • reprezentarea acestor entit˘¸i ˆ memoria calculatorului, ¸i at ın s • algoritmii pentru efectuarea acestor operatii. ¸ Primele dou˘ elemente sunt ˆ esent˘ de natur˘ matematic˘ ¸i se refer˘ la a ın ¸a a a s a ”ce” structuri de date ¸i operatii trebuie s˘ folosim, iar ultimile dou˘ elemente s ¸ a a implic˘ faza de implementare ¸i se refer˘ la ”cum” s˘ realiz˘m structurile de date a s a a a ¸i operatiile. Algoritmica ¸i structurile de date nu pot fi separate. De¸i algoritmica s ¸ s s ¸i programarea pot fi separate, noi nu vom face acest lucru, ci vom implementa s algoritmii ˆ ıntr-un limbaj de programare (Pascal, C/C++, Java). Din aceast˘ cauz˘ a a acest curs este ¸i o initiere ˆ algoritmic˘ ¸i programare. s ¸ ın as Scopul cursului este subordonat scopului specializ˘rii (informatic˘, ˆ cazul a a ın nostru) care este s˘ preg˘teasc˘ speciali¸ti competenti, cu ˆ a a a s ¸ ınalt˘ calificare ˆ a ın domeniul informaticii, cadre didactice competente ˆ acest domeniu (profesor de ın informatic˘ ˆ gimnaziu ¸i liceu), informaticieni ˆ diverse domenii cu profil tehnic, a ın s ın economic, etc. ce pot ˆ ıncepe lucrul imediat dup˘ absolvirea facult˘¸ii.Dezideratul a at final este deci competenta. Competenta ˆ ¸ ¸ ıntr-un domeniu de activitate implic˘ a experient˘ ˆ rezolvarea problemelor din acel domeniu de activitate. Atˆt ¸a ın a competenta cˆt ¸i experienta ˆ rezolvarea problemelor se pot obtine numai dac˘ ¸ a s ¸ ın ¸ a permanent se ˆ ıntreprind eforturi pentru ˆ sirea de noi cuno¸tinte. De exemplu, ınsu¸ s ¸ orice informatician (programator sau profesor) care elaboreaz˘ programe pentru a rezolvarea unor probleme diverse, trebuie s˘ aib˘ competente conform schemei1 : a a ¸
PROBLEMA (model fizic)

ALGORITMICA (model virtual)

PROGRAMARE

Gandire algoritmica

Experienta (rezolvarea de probleme)

Cursul de Algoritmi ¸i structuri de date este util (¸i chiar necesar) pentru s s formarea competentelor ¸i abilit˘¸ilor unui bun programator sau profesor de ¸ s at informatic˘. Pentru a vedea care sunt aceste competente ¸i abilit˘¸i putem, de a ¸ s at
1 M. Vlada; E-Learning ¸i Software educational; Conferinta National˘ de ˆ a¸amˆnt Virtual, s ¸ ¸ ¸ a Inv˘t˘ a Bucure¸ti, 2003 s

exemplu, s˘ citim Programa pentru informatic˘ - Concursul national unic pentru a a ¸ ocuparea posturilor didactice declarate vacante ˆ ˆ a¸amˆntul preuniversitar.2 ın ınv˘t˘ a ˆ Intr-un fel, primul semestru al cursului Algoritmi ¸i structuri de date este s echivalent cu ceea ce se pred˘ la informatic˘ ˆ clasa a IX-a iar al doilea semestru cu a a ın clasa a X-a (specializarea: matematic˘-informatic˘, intensiv informatic˘). Diferenta a a a ¸ este dat˘ ˆ primul rˆnd de dificultatea problemelor abordate de c˘tre noi ˆ cadrul a ın a a ın acestui curs. Din aceast˘ cauz˘ vom avea ˆ vedere ¸i ce prevede Pograma ¸olar˘ a a ın s s a pentru clasa a IX-a, Profil real, Specializarea: Matematic˘-informatic˘, intensiv a a informatic˘. De asemenea, merit˘ s˘ vedem ce p˘reri au cei care au terminat de a a a a curˆnd o facultate cu un profil de informatic˘ ¸i care au un ˆ a a s ınceput de carier˘ a reu¸it. Vom ˆ ¸elege de ce acest curs este orientat pe rezolvarea de probleme. s ınt Alegerea limbajului Java pentru prezentarea implement˘rilor algoritmilor a a fost f˘cut˘ din cˆteva considerente. Java verific˘ validitatea indicilor tablourilor a a a a (programele nu se pot termina printr-o violare de memorie sau eroare de sistem). Java realizeaz˘ gestiunea automat˘ a memoriei (recupereaz˘ automat memoria a a a care nu mai este necesar˘ programului) ceea ce simplific˘ scrierea programelor a a ¸i permite programatorului s˘ se concentreze asupra esentei algoritmului. Exist˘ s a ¸ a documentatie pe internet. Compilatorul de Java este gratuit. Un program scris ˆ ¸ ın Java poate fi executat pe orice calculator (indiferent de arhitectur˘ sau sistem de a operare). Studentii nu sunt obligati s˘ realizeze implement˘rile algoritmilor ˆ Java; ¸ ¸ a a ın ei pot folosi Pascal sau C/C++. Algoritmii prezentati ˆ curs sunt descri¸i ˆ limbaj ¸ ın s ın natural sau ˆ limbaj algoritmic iar implement˘rile sunt ˆ limbajul de programare ın a ın Java. Java este un limbaj orientat-obiect, dar noi vom utiliza foarte putin aceast˘ ¸ a particularitate. Sunt prezentate toate elementele limbajului de programare Java necesare pentru acest curs dar ecesta nu este un curs de programare ˆ Java. ın Cuno¸tintele minimale acceptate la sfˆr¸itul cursului rezult˘ din Legea nr. s ¸ as a 288 din 24 iunie 2004 privind organizarea studiilor universitare ¸i, de exemplu, s din Ghidul calit˘¸ii ˆ ˆ a¸amˆntul superior3 . Aici se precizeaz˘ faptul c˘ diploma at ın ınv˘t˘ a a a de licent˘ se acord˘ unui absolvent al programului de studii care: demonstreaz˘ ¸a a a acumulare de cuno¸tinte ¸i capacitatea de a ˆ ¸elege aspecte din domeniul s ¸ s ınt de studii ˆ care s-a format, poate folosi atˆt cuno¸tintele acumulate precum ın a s ¸ ¸i capacitatea lui de ˆ ¸elegere a fenomenelor printr-o abordare profesional˘ s ınt a ˆ domeniul de activitate, a acumulat competente necesare demonstr˘rii, ın ¸ a argument˘rii ¸i rezolv˘rii problemelor din domeniul de studii considerat, ¸i-a a s a s dezvoltat deprinderi de ˆ a¸are necesare procesului de educatie continu˘. ınv˘t ¸ a

prin O.M:Ed.C. nr.5287/15.11.2004 Universit˘¸ii din Bucure¸ti, 2004; Capitolul 4, Calitatea programelor de studii uniat s versitare, Prof.univ.dr. Gabriela M. Atanasiu - Universitatea Tehnic˘ ”Gh.Asachi” din Ia¸i a s
3 Editura

2 Aprobat˘ a

Cuprins
1 Notiuni fundamentale ¸ 1.1 Programe ciudate . . . . . . . . . . . . . 1.1.1 Un program ciudat ˆ Pascal . . ın 1.1.2 Un program ciudat ˆ C++ . . . ın 1.1.3 Un program ciudat ˆ Java . . . ın 1.1.4 Structura unui program Java . . 1.2 Conversii ale datelor numerice . . . . . 1.2.1 Conversia din baza 10 ˆ baza 2 . ın 1.2.2 Conversia din baza 2 ˆ baza 10 . ın 1.2.3 Conversii ˆ ıntre bazele 2 ¸i 2r . . s 2 Structuri de date 2.1 Date ¸i structuri de date . . . . . . . . s 2.1.1 Date . . . . . . . . . . . . . . . 2.1.2 Structuri de date . . . . . . . . 2.2 Structuri ¸i tipuri de date abstracte . . s 2.2.1 Structuri de date abstracte . . 2.2.2 Tipuri de date abstracte . . . . 2.3 Structuri de date elementare . . . . . 2.3.1 Liste . . . . . . . . . . . . . . . 2.3.2 Stive ¸i cozi . . . . . . . . . . . s 2.3.3 Grafuri . . . . . . . . . . . . . 2.3.4 Arbori binari . . . . . . . . . . 2.3.5 Heap-uri . . . . . . . . . . . . . 2.3.6 Structuri de multimi disjuncte ¸ 3 Algoritmi 3.1 Etape ˆ rezolvarea problemelor . ın 3.2 Algoritmi . . . . . . . . . . . . . 3.2.1 Ce este un algoritm? . . . 3.2.2 Propriet˘¸ile algoritmilor at 3.2.3 Tipuri de prelucr˘ri . . . a v . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 1 2 3 4 5 5 6 6 7 7 7 9 10 10 10 11 11 12 13 14 15 16 17 17 18 18 20 20

3.3

3.4

3.5

Descrierea algoritmilor . . . . . . . . . . . . . . . 3.3.1 Limbaj natural . . . . . . . . . . . . . . . 3.3.2 Scheme logice . . . . . . . . . . . . . . . . 3.3.3 Pseudocod . . . . . . . . . . . . . . . . . Limbaj algoritmic . . . . . . . . . . . . . . . . . . 3.4.1 Declararea datelor . . . . . . . . . . . . . 3.4.2 Operatii de intrare/ie¸ire . . . . . . . . . ¸ s 3.4.3 Prelucr˘ri liniare . . . . . . . . . . . . . . a 3.4.4 Prelucr˘ri alternative . . . . . . . . . . . a 3.4.5 Prelucr˘ri repetitive . . . . . . . . . . . . a 3.4.6 Subalgoritm . . . . . . . . . . . . . . . . . 3.4.7 Probleme rezolvate . . . . . . . . . . . . . 3.4.8 Probleme propuse . . . . . . . . . . . . . Instructiuni corespondente limbajului algoritmic ¸ 3.5.1 Declararea datelor . . . . . . . . . . . . . 3.5.2 Operatii de intrare/ie¸ire . . . . . . . . . ¸ s 3.5.3 Prelucr˘ri liniare . . . . . . . . . . . . . . a 3.5.4 Prelucr˘ri alternative . . . . . . . . . . . a 3.5.5 Prelucr˘ri repetitive . . . . . . . . . . . . a 3.5.6 Subprograme . . . . . . . . . . . . . . . . 3.5.7 Probleme rezolvate . . . . . . . . . . . . . 3.5.8 Probleme propuse . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

20 21 22 22 23 23 23 24 24 25 26 27 30 32 32 34 35 35 35 36 37 52 55 55 57 57 58 58 60 61 62 62 62 62 63 64 66 67 67 69

4 Analiza complexit˘¸ii algoritmilor at 4.1 Scopul analizei complexit˘¸ii . . . . . . . . . . . . . . . . at 4.1.1 Complexitatea spatiu . . . . . . . . . . . . . . . ¸ 4.1.2 Complexitatea timp . . . . . . . . . . . . . . . . 4.2 Notatia asimptotic˘ . . . . . . . . . . . . . . . . . . . . ¸ a 4.2.1 Definire ¸i propriet˘¸i . . . . . . . . . . . . . . . s at 4.2.2 Clase de complexitate . . . . . . . . . . . . . . . 4.2.3 Cazul mediu ¸i cazul cel mai defavorabil . . . . . s 4.2.4 Analiza asimptotic˘ a structurilor fundamentale a 4.3 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Calcularea maximului . . . . . . . . . . . . . . . 4.3.2 Sortarea prin selectia maximului . . . . . . . . . ¸ 4.3.3 Sortarea prin insertie . . . . . . . . . . . . . . . . ¸ 4.3.4 Sortarea rapid˘ (quicksort) . . . . . . . . . . . . a 4.3.5 Problema celebrit˘¸ii . . . . . . . . . . . . . . . . at 4.4 Probleme . . . . . . . . . . . . . . . . . . . . . . . . . . 4.4.1 Probleme rezolvate . . . . . . . . . . . . . . . . . 4.4.2 Probleme propuse . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

5 Recursivitate 5.1 Functii recursive . . . . . . . ¸ 5.1.1 Functii numerice . . . ¸ 5.1.2 Functia lui Ackerman ¸ 5.1.3 Recursii imbricate . . 5.2 Proceduri recursive . . . . . .

. . . . .

. . . . .

. . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71 71 71 74 74 75 77 77 78 78 80 80 81 82 84 87 93 93 93 94 95 95 95 96 97 97 98 98 98 100 100 100 101 101 102 104 104 105 106 107 107 107

6 Analiza algoritmilor recursivi 6.1 Relatii de recurent˘ . . . . . . . . ¸ ¸a 6.1.1 Ecuatia caracteristic˘ . . . ¸ a 6.1.2 Solutia general˘ . . . . . . ¸ a 6.2 Ecuatii recurente neomogene . . . ¸ 6.2.1 O form˘ simpl˘ . . . . . . . a a 6.2.2 O form˘ mai general˘ . . . a a 6.2.3 Teorema master . . . . . . 6.2.4 Transformarea recurentelor ¸ 6.3 Probleme rezolvate . . . . . . . . .

7 Algoritmi elementari 7.1 Operatii cu numere . . . . . . . . . . . . . . . . ¸ 7.1.1 Minim ¸i maxim . . . . . . . . . . . . . s 7.1.2 Divizori . . . . . . . . . . . . . . . . . . 7.1.3 Numere prime . . . . . . . . . . . . . . 7.2 Algoritmul lui Euclid . . . . . . . . . . . . . . . 7.2.1 Algoritmul clasic . . . . . . . . . . . . . 7.2.2 Algoritmul lui Euclid extins . . . . . . . 7.3 Operatii cu polinoame . . . . . . . . . . . . . . ¸ 7.3.1 Adunarea a dou˘ polinoame . . . . . . . a 7.3.2 ˆ Inmultirea a dou˘ polinoame . . . . . . ¸ a 7.3.3 Calculul valorii unui polinom . . . . . . 7.3.4 Calculul derivatelor unui polinom . . . . 7.4 Operatii cu multimi . . . . . . . . . . . . . . . ¸ ¸ 7.4.1 Apartenenta la multime . . . . . . . . . ¸ ¸ 7.4.2 Diferenta a dou˘ multimi . . . . . . . . ¸ a ¸ 7.4.3 Reuniunea ¸i intersectia a dou˘ multimi s ¸ a ¸ 7.4.4 Produsul cartezian a dou˘ multimi . . . a ¸ 7.4.5 Generarea submultimilor unei multimi . ¸ ¸ 7.5 Operatii cu numere ˆ ¸ ıntregi mari . . . . . . . . . 7.5.1 Adunarea ¸i sc˘derea . . . . . . . . . . . s a 7.5.2 Inmultirea ¸i ˆ artirea . . . . . . . . . ¸ s ımp˘ 7.5.3 Puterea . . . . . . . . . . . . . . . . . . 7.6 Operatii cu matrice . . . . . . . . . . . . . . . . ¸ 7.6.1 ˆ Inmultirea . . . . . . . . . . . . . . . . . ¸ 7.6.2 Inversa unei matrice . . . . . . . . . . .

8 Algoritmi combinatoriali 8.1 Principiul includerii ¸i al excluderii ¸i aplicatii s s ¸ 8.1.1 Principiul includerii ¸i al excluderii . . s 8.1.2 Num˘rul functiilor surjective . . . . . a ¸ 8.1.3 Num˘rul permut˘rilor f˘r˘ puncte fixe a a aa 8.2 Principiul cutiei lui Dirichlet ¸i aplicatii . . . s ¸ 8.2.1 Problema subsecventei . . . . . . . . . ¸ 8.2.2 Problema sub¸irurilor strict monotone s 8.3 Numere remarcabile . . . . . . . . . . . . . . 8.3.1 Numerele lui Fibonacci . . . . . . . . 8.3.2 Numerele lui Catalan . . . . . . . . . 8.4 Descompunerea ˆ factori primi . . . . . . . . ın 8.4.1 Functia lui Euler . . . . . . . . . . . . ¸ 8.4.2 Num˘rul divizorilor . . . . . . . . . . a 8.4.3 Suma divizorilor . . . . . . . . . . . . 8.5 Partitia numerelor . . . . . . . . . . . . . . . ¸ 8.5.1 Partitia lui n ˆ exact k termeni . . . ¸ ın 8.5.2 Partitia lui n ˆ cel mult k termeni . . ¸ ın 8.5.3 Partitii multiplicative . . . . . . . . . ¸ 8.6 Partitia multimilor . . . . . . . . . . . . . . . ¸ ¸ 8.7 Probleme rezolvate . . . . . . . . . . . . . . . 9 Algoritmi de c˘utare a 9.1 Problema c˘ut˘rii . . a a 9.2 C˘utarea secvential˘ a ¸ a 9.3 C˘utare binar˘ . . . a a 9.4 Inserare ˆ tabel˘ . . ın a 9.5 Dispersia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

109 109 109 110 112 113 113 114 114 115 116 119 119 121 121 122 122 123 123 123 124 127 127 127 129 130 131 133 133 134 139 139 141 142 143 145 145 151 155 157 159

10 Algoritmi elementari de sortare 10.1 Introducere . . . . . . . . . . . . . . . . . . . 10.2 Sortare prin selectie . . . . . . . . . . . . . . ¸ 10.3 Sortare prin insertie . . . . . . . . . . . . . . ¸ 10.3.1 Insertie direct˘ . . . . . . . . . . . . . ¸ a 10.3.2 Insertie binar˘ . . . . . . . . . . . . . ¸ a 10.4 Sortare prin interschimbare . . . . . . . . . . 10.5 Sortare prin mic¸orarea incrementului - shell s 11 Liste 11.1 Liste liniare . . . . . . . . . . . 11.2 Cozi . . . . . . . . . . . . . . . 11.3 Stive . . . . . . . . . . . . . . . 11.4 Evaluarea expresiilor aritmetice 11.5 Operatii asupra listelor . . . . . ¸ . . . . . . . . . . . . . . . . . . prefixate . . . . . . . . . . . . . . . .

12 Algoritmi divide et impera 12.1 Tehnica divide et impera . . . . . . . . . . . 12.2 Ordinul de complexitate . . . . . . . . . . . 12.3 Exemple . . . . . . . . . . . . . . . . . . . . 12.3.1 Sortare prin partitionare - quicksort 12.3.2 Sortare prin interclasare - MergeSort 12.3.3 Placa cu g˘uri . . . . . . . . . . . . a 12.3.4 Turnurile din Hanoi . . . . . . . . . 12.3.5 ˆ Injum˘t˘¸ire repetat˘ . . . . . . . . a at a

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

163 163 164 165 165 166 168 169 173 177 177 180 180 185 191 197 204 208 213 213 214 215 215 216 217 217 217 218 219 219 220 220 220 228 230 241 246 250 255 260

13 Algoritmi BFS-Lee 13.1 Prezentare general˘ . . . . . . . . . . . . . . . . a 13.2 Probleme rezolvate . . . . . . . . . . . . . . . . 13.2.1 Romeo ¸i Julieta - OJI2004 clasa a X-a s 13.2.2 Sudest - OJI2006 clasa a X-a . . . . . . 13.2.3 Muzeu - ONI2003 clasa a X-a . . . . . . 13.2.4 P˘ianjen ONI2005 clasa a X-a . . . . . a 13.2.5 Algoritmul Edmonds-Karp . . . . . . . 13.2.6 Cuplaj maxim . . . . . . . . . . . . . . 14 Metoda optimului local - greedy 14.1 Metoda greedy . . . . . . . . . . . . . . . . . 14.2 Algoritmi greedy . . . . . . . . . . . . . . . . 14.3 Exemple . . . . . . . . . . . . . . . . . . . . . 14.3.1 Problema continu˘ a rucsacului . . . . a 14.3.2 Problema plas˘rii textelor pe o band˘ a a 14.3.3 Problema plas˘rii textelor pe m benzi a 14.3.4 Maximizarea unei sume de produse . . 14.3.5 Problema statiilor . . . . . . . . . . . ¸ 14.3.6 Problema cutiilor . . . . . . . . . . . . 14.3.7 Problema sub¸irurilor . . . . . . . . . s 14.3.8 Problema intervalelor disjuncte . . . . 14.3.9 Problema alegerii taxelor . . . . . . . 14.3.10 Problema acoperirii intervalelor . . . . 14.3.11 Algoritmul lui Prim . . . . . . . . . . 14.3.12 Algoritmul lui Kruskal . . . . . . . . . 14.3.13 Algoritmul lui Dijkstra . . . . . . . . . 14.3.14 Urgenta - OJI2002 cls 11 . . . . . . . ¸ 14.3.15 Reactivi - OJI2004 cls 9 . . . . . . . . 14.3.16 Pal - ONI2005 cls 9 . . . . . . . . . . 14.3.17 Sant - ONI2006 cls 9 . . . . . . . . . . ¸ ¸ 14.3.18 Cezar - OJI2007 cls 11 . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

15 Metoda backtracking 15.1 Generarea produsului cartezian . . . . . . . . . . . 15.1.1 Generarea iterativ˘ a produsului cartezian . a 15.1.2 Generarea recursiv˘ a produsului cartezian a 15.2 Metoda bactracking . . . . . . . . . . . . . . . . . 15.2.1 Bactracking iterativ . . . . . . . . . . . . . 15.2.2 Backtracking recursiv . . . . . . . . . . . . 15.3 Probleme rezolvate . . . . . . . . . . . . . . . . . . 15.3.1 Generarea aranjamentelor . . . . . . . . . . 15.3.2 Generarea combin˘rilor . . . . . . . . . . . a 15.3.3 Problema reginelor pe tabla de ¸ah . . . . . s 15.3.4 Turneul calului pe tabla de ¸ah . . . . . . . s 15.3.5 Problema color˘rii h˘rtilor . . . . . . . . . a a¸ 15.3.6 Problema vecinilor . . . . . . . . . . . . . . 15.3.7 Problema labirintului . . . . . . . . . . . . 15.3.8 Generarea partitiilor unui num˘r natural . ¸ a 15.3.9 Problema parantezelor . . . . . . . . . . . . 15.3.10 Algoritmul DFS de parcurgere a grafurilor . 15.3.11 Determinarea componentelor conexe . . . . 15.3.12 Determinarea componentelor tare conexe . 15.3.13 Sortare topologic˘ . . . . . . . . . . . . . . a 15.3.14 Determinarea nodurilor de separare . . . . 15.3.15 Determinarea muchiilor de rupere . . . . . 15.3.16 Determinarea componentelor biconexe . . . 15.3.17 Triangulatii - OJI2002 clasa a X-a . . . . . ¸ 15.3.18 Partitie - ONI2003 clasa a X-a . . . . . . . ¸ 15.3.19 Scufita - ONI2003 clasa a X-a . . . . . . . . ¸ 16 Programare dinamic˘ a 16.1 Prezentare general˘ . . . . . . . . . . . . . . . a 16.2 Probleme rezolvate . . . . . . . . . . . . . . . 16.2.1 Inmultirea optimal˘ a matricelor . . . ¸ a 16.2.2 Sub¸ir cresc˘tor maximal . . . . . . . s a 16.2.3 Sum˘ maxim˘ ˆ triunghi de numere . a a ın 16.2.4 Sub¸ir comun maximal . . . . . . . . . s 16.2.5 Distanta minim˘ de editare . . . . . . ¸ a 16.2.6 Problema rucsacului (0 − 1) . . . . . . 16.2.7 Problema schimbului monetar . . . . . 16.2.8 Problema travers˘rii matricei . . . . . a 16.2.9 Problema segment˘rii vergelei . . . . . a 16.2.10 Triangularizarea poligoanelor convexe 16.2.11 Algoritmul Roy-Floyd-Warshall . . . . 16.2.12 Oracolul decide - ONI2001 cls 10 . . . 16.2.13 Pav˘ri - ONI2001 clasa a X-a . . . . . a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

269 269 269 274 277 279 279 280 280 284 294 296 298 301 303 306 310 311 313 314 316 320 321 323 326 330 336 343 343 345 345 348 352 353 360 366 367 368 370 373 374 375 381

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

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

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

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

1.1. PROGRAME CIUDATE

3

1.1.3

Un program ciudat ˆ Java ın

class Ciudat { public static void main(String args[]) { int x,y,z; x=200000; y=300000; z=x*y; System.out.println(x+"*"+y+"="+z); } }

Iat˘ un program ˆ C++ ˆ care dorim s˘ calcul˘m suma 200.000 ∗ 300.000. a ın ın a a

De¸i ne a¸teptam s˘ apar˘ ca rezultat 60.000.000.000, surpriza este c˘ pe ecran s s a a a apare 200000*300000=-129542144

Figura 1.3: Un program ciudat ˆ Java ın Calculul cu numerele ˆ ıntregi este relativ simplu. Calculele sunt f˘cute ˆ a ıntr-o aritmetic˘ modulo N = 2n unde n este num˘rul de biti ai cuvˆntului ma¸in˘. a a ¸ a s a Exist˘ ma¸ini pe 16, 32 ¸i 64 biti pentru care N este aproximativ egal cu 6 × 104 , a s s ¸ 4 × 109 ¸i respectiv 2 × 1019 . s Se pot reprezenta ¸i numerele ˆ s ıntregi negative. Modul curent de reprezentare este ˆ complement fat˘ de 2. ˆ notatie binar˘, bitul cel mai semnificativ este ın ¸a In ¸ a bitul de semn. Numerele negative sunt cuprinse ˆ ıntre −2n−1 ¸i 2n−1 − 1. s Atunci cˆnd valorile obtinute din calcule dep˘¸esc marginile permise de tipul a ¸ as variabilelor implicate ˆ respectivele calcule, se pot obtine rezultate eronate. ın ¸

4

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.1.4

Structura unui program Java

Un program simplu ˆ Java are urm˘toarea structur˘: ın a a class numeClasa { public static void main(String args[]) { // declar˘ri de variabile a // instructiuni ¸ } } Programul prezentat ˆ sectiunea anterioar˘ se poate scrie sub forma: ın ¸ a class Ciudat { public static void main(String args[]) { // declar˘ri de variabile a int x,y,z; // instructiuni ¸ x=200000; y=300000; z=x*y; System.out.println(x+”*”+y+”=”+z); } } Clasa este elementul de baz˘ ˆ Java. Cel mai simplu program ˆ Java este a ın ın format dintr-o clas˘ (numele clasei este la latitudinea programatorului; singura a recomandare este s˘ ˆ a ınceap˘ cu liter˘ mare) ¸i functia main. a a s ¸ ˆ exemplul de mai sus sunt declarate trei variabile (x, y ¸i z) de tip int In s (adic˘ de tip ˆ a ıntreg cu semn). Spatiul alocat variabilelor de tip int este de 4 octeti ¸ ¸ (32 biti). Aceasta ˆ ¸ ınseamn˘ c˘ o astfel de variabil˘ poate avea valori ˆ a a a ıntre −263 ¸i s 263 − 1. Valoarea maxim˘ este de aproximativ 2 miliarde. a ˆ programul anterior x are valoarea 200.000 iar y are valoarea 300.000, deci In produsul are valoarea 60 miliarde care dep˘¸e¸te cu mult valoarea maxim˘ de 2 as s a miliarde. ˆ binar, 60 miliarde se scrie (folosint 36 biti) sub forma In ¸ 110111111000010001110101100000000000 dar sunt retinuti numai 32 biti din partea dreapt˘, adic˘ ¸ ¸ ¸ a a 11111000010001110101100000000000 Primul bit reprezint˘ bitul de semn (1 reprezint˘ semnul - iar 0 reprezint˘ a a a semnul +). Aceast˘ reprezentare trebuie gˆndit˘ ca fiind o reprezentare ˆ cod a a a ın

1.2. CONVERSII ALE DATELOR NUMERICE

5

complementar (ea este ˆ memoria calculatorului ¸i toate numerele ˆ ın s ıntregi cu semn sunt reprezentate ˆ acest cod). ın Reprezentarea ˆ cod direct se obtine din reprezentarea ˆ cod complementar ın ¸ ın (mai precis, trecˆnd prin reprezentarea ˆ cod invers ¸i adunˆnd, ˆ binar, 1): a ın s a ın 11111000010001110101100000000000 (cod complementar) 10000111101110001010011111111111 (cod invers) 10000111101110001010100000000000 (cod direct) Din codul direct se obtine -129542144 ˆ baza 10. Aceasta este explicatia acelui ¸ ın ¸ rezultat ciudat!

1.2

Conversii ale datelor numerice

1.2.1

Conversia din baza 10 ˆ baza 2 ın

Fie x = an ...a0 num˘rul scris ˆ baza 10. Conversia ˆ baza 2 a num˘rului x a ın ın a se efectueaz˘ dup˘ urm˘toarele reguli: a a a • Se ˆ ımparte num˘rul x la 2 iar restul va reprezenta cifra de ordin 0 a a num˘rului scris ˆ noua baz˘ (b0 ). a ın a • Cˆtul obtinut la ˆ artirea anterioar˘ se ˆ a ¸ ımp˘ ¸ a ımparte la 2 ¸i se obtine s ¸ cifra de ordin imediat superior a num˘rului scris ˆ noua baz˘. Secventa a ın a ¸ de ˆ artiri se repet˘ pˆn˘ cˆnd se ajunge la cˆtul 0. ımp˘ ¸ a a a a a • Restul de la a k-a ˆ artire va reprezenta cifra bk−1 . Restul de ımp˘ ¸ la ultima ˆ artire reprezint˘ cifra de ordin maxim ˆ reprezentarea ımp˘ ¸ a ın num˘rului ˆ baza 2. a ın Metoda conduce la obtinerea rezultatului dup˘ un num˘r finit de ˆ artiri, ¸ a a ımp˘ ˆ ıntrucˆt ˆ mod inevitabil se ajunge la un cˆt nul. ˆ plus, toate resturile obtinute a ın a In ¸ apartin multimii {0, 1}. ¸ ¸ Exemplu. Fie x = 13 num˘rul ˆ baza 10. Secventa de ˆ artiri este: a ın ¸ ımp˘ ¸ (1) se ˆ ımparte 13 la 2 ¸i se obtine cˆtul 6 ¸i restul 1 (deci b0 = 1) s ¸ a s (2) se ˆ ımparte 6 la 2 ¸i se obtine cˆtul 3 ¸i restul 0 (deci b1 = 0) s ¸ a s (3) se ˆ ımparte 3 la 2 ¸i se obtine cˆtul 1 ¸i restul 1 (deci b2 = 1) s ¸ a s (4) se ˆ ımparte 1 la 2 ¸i se obtine cˆtul 0 ¸i restul 1 (deci b3 = 1). s ¸ a s Prin urmare (13)10 = (1101)2 .

6

CAPITOLUL 1. NOTIUNI FUNDAMENTALE ¸

1.2.2

Conversia din baza 2 ˆ baza 10 ın

Dac˘ y = bn ...b1 b0 este un num˘r ˆ baza 2 , atunci reprezentarea ˆ baza 10 a a ın ın se obtine efectuˆnd calculul (ˆ baza 10): ¸ a ın x = bn 2n + ... + b1 2 + b0 . Exemplu. Fie y = 1100. Atunci reprezentarea ˆ baza 10 va fi ın x = 1 · 23 + 1 · 22 + 0 · 21 + 0 · 20 = 12.

1.2.3

Conversii ˆ ıntre bazele 2 ¸i 2r s

Pentru conversia unui num˘r din baza p ˆ baza q se poate converti num˘rul a ın a din baza p ˆ baza 10, iar acesta se converte¸te ˆ baza q. ın s ın ˆ cazul conversiei unui num˘r din baza p = 2 ˆ baza q = 2r se poate evita In a ın trecerea prin baza 10 procedˆndu-se ˆ modul urm˘tor: se formeaz˘ grupuri de a ın a a cˆte r cifre pornind de la ultima cifr˘ din dreapta, ˆ a a ınspre stˆnga. Fiecare grup de a r cifre va fi convertit ˆ ıntr-o cifr˘ a bazei q. a Fie, spre exemplu: p = 2, q = 16 = p4 ¸i x = (1011010)2 . s Se obtin urm˘toarele grupuri de cˆte 4 cifre binare: ¸ a a (1010)2 = A16 ¸i (0101)2 = 516 . s Deci scrierea num˘rului x ˆ baza 16 este: (5A)16 . a ın Se observ˘ c˘ a fost completat˘ cu 0, spre stˆnga, cea mai din stˆnga grup˘, a a a a a a pˆn˘ la formarea grupei complete de 4 cifre binare. a a ˆ cazul conversiei unui num˘r din baza p = 2r ˆ baza q = 2 se poate de In a ın asemenea evita trecerea prin baza 10 procedˆndu-se ˆ modul urm˘tor: fiecare cifr˘ a ın a a din reprezentarea ˆ baza p = 2r se ˆ ın ınlocuie¸te cu r cifre binare care reprezint˘ s a scrierea respectivei cifre ˆ baza 2. ın Fie, spre exemplu: p = 16 = 24 , q = 2 ¸i x = (3A)16 . s Se fac urm˘toarele ˆ a ınlocuiri de cifre: 3 → 0011, A → 1010. Deci scrierea num˘rului x ˆ baza 2 este: (111010)2 . a ın Se observ˘ c˘ nu apar cifrele 0 din stˆnga scrierii brute (00111010)2 obtinute a a a ¸ prin ˆ ınlocuiri.

Capitolul 2

Structuri de date
ˆ Inainte de a elabora un algoritm, trebuie s˘ ne gˆndim la modul ˆ care a a ın reprezent˘m datele. a

2.1

Date ¸i structuri de date s

2.1.1

Date

Datele sunt entit˘¸i purt˘toare de informatie. ˆ informatic˘, o dat˘ este un at a ¸ In a a model de reprezentare a informatiei, accesibil unui anumit procesor (om, unitate ¸ central˘, program), model cu care se poate opera pentru a obtine noi informatii a ¸ ¸ despre fenomenele, procesele ¸i obiectele lumii reale. ˆ functie de modul lor de s In organizare, datele pot fi: elementare (simple) sau structurate. Datele elementare au caracter atomic, ˆ sensul c˘ nu pot fi descompuse ˆ ın a ın alte date mai simple. Astfel de date sunt cele care iau ca valori numere sau ¸iruri s de caractere. O dat˘ elementar˘ apare ca o entitate indivizibil˘ atˆt din punct de a a a a vedere al informatiei pe care o reprezint˘ cˆt ¸i din punct de vedere al procesorului ¸ a a s care o prelucreaz˘. a O dat˘ elementar˘ poate fi privit˘ la nivel logic (la nivelul procesorului uman) a a a sau la nivel fizic (la nivelul calculatorului). Din punct de vedere logic, o dat˘ poate fi definit˘ ca un triplet de forma a a (identif icator, atribute, valori). Din punct de vedere fizic, o dat˘ poate fi definit˘ ca o zon˘ de memorie de o a a a anumit˘ lungime, situat˘ la o anumit˘ adres˘ absolut˘, ˆ care sunt memorate ˆ a a a a a ın ın timp ¸i ˆ s ıntr-o form˘ specific˘ valorile datei. a a 7

8

CAPITOLUL 2. STRUCTURI DE DATE

Identificatorul este un simbol asociat datei pentru a o distinge de alte date ¸i s pentru a o putea referi ˆ cadrul programului. ın Atributele sunt propriet˘¸ii ale datei ¸i precizeaz˘ modul ˆ care aceasta va fi at s a ın tratat˘ ˆ cadrul procesului de prelucrare. Dintre atribute, cel mai important este a ın atributul de tip care define¸tete apartenenta datei la o anumit˘ clas˘ de date. s ¸ a a O clas˘ de date este definit˘ de natura ¸i domeniul valorilor datelor care fac a a s parte din clasa respectiv˘, de operatiile specifice care se pot efectua asupra datelor a ¸ ¸i de modelul de reprezentare intern˘ a datelor. Astfel, exist˘ date de tip ˆ s a a ıntreg, de tip real, de tip logic, de tip ¸ir de caractere, etc. s O multime de date care au acelea¸i caracteristici se nume¸te tip de date. ¸ s s Evident, un tip de date este o clas˘ de date cu acela¸i mod de interpretare logic˘ a s a ¸i reprezentare fizic˘ ¸i se caracterizeaz˘ prin valorile pe care le pot lua datele ¸i s as a s prin operatiile care pot fi efectuate cu datele de tipul respectiv. ¸ De exemplu, tipul ˆ ıntreg se caracterizeaz˘ prin faptul c˘ datele care ˆ apartin a a ıi ¸ pot lua doar valori ˆ ıntregi, ¸i asupra lor pot fi efectuate operatii aritmetice clasice s ¸ (adunare, sc˘dere, ˆ a ınmultire, ˆ artire ˆ multimea numerelor ˆ ¸ ımp˘ ¸ ın ¸ ıntregi, comparatii). ¸ Se poate considera c˘ datele organizate sub forma tablourilor unidimensionale a formeaz˘ tipul vector iar datele organizate sub forma tablourilor bidimensionale a formeaz˘ tipul matrice. a ˆ functie de natura elementelor care o compun, o structur˘ de date poate fi: In ¸ a • omogen˘, atunci cˆnd toate elementele au acela¸i tip; a a s • neomogen˘, atunci cˆnd elementele componente au tipuri diferite. a a • static˘, atunci cˆnd num˘rul de componente este fixat; a a a

ˆ functie de num˘rul datelor care o compun, o structur˘ de date poate fi: In a a • dinamic˘, atunci cˆnd num˘rul de componente este variabil. a a a

Din punct de vedere al modului ˆ care sunt utilizate datele pot fi: ın • Constante. Valoarea lor nu este ¸i nu poate fi modificat˘ ˆ cadrul s a ın algoritmului, fiind fixat˘ de la ˆ a ınceputul acestuia. O constant˘ este a o dat˘ care p˘streaz˘ aceea¸i valoare pe tot parcursul procesului de a a a s prelucrare. Pentru constantele care nu au nume, ˆ a¸i valoarea lor ıns˘s este cea prin care se identific˘. Constante care au nume (identificator) a sunt initializate cu o valoare ˆ momentul declar˘rii. ¸ ın a • Variabile. Valoarea lor poate fi modificat˘ ˆ cadrul algoritmului. a ın ˆ momentrul declar˘rii lor, variabilele pot fi initializate (li se atribuie In a ¸ o valoare) sau pot fi neinitializate (nu li se atribuie nici o valoare). ¸ O variabil˘ este o dat˘ care nu p˘streaz˘ neap˘rat aceea¸i valoare pe a a a a a s parcursul procesului de prelucrare. Tipul unei date trebuie s˘ fie precizat, ˆ cadrul programului de prelucrare, a ın printr-o declaratie de tip ce precede utilizarea respectivei constante sau variabile. ¸ Valorile datei pot fi numere, sau valori de adev˘r, sau ¸iruri de caractere, etc. a s

2.1. DATE SI STRUCTURI DE DATE ¸

9

2.1.2

Structuri de date

Datele apar frecvent sub forma unor colectii de date de diferite tipuri, menite ¸ s˘ faciliteze prelucrarea ˆ cadrul rezolv˘rii unei anumite probleme concrete. a ın a Datele structurate, numite uneori ¸i structuri de date, sunt constituite din mai s multe date elementare (uneori de acela¸i tip, alteori de tipuri diferite), grupate cu s un anumit scop ¸i dup˘ anumite reguli. s a Exemple. 1. Un ¸ir finit de numere reale a1 , a2 , ..., an poate fi reprezentat ca o dat˘ s a structurat˘ (tablou unidimensional sau vector). a 2. O matrice  a1,1  a2,1    ··· am,1 a1,2 a2,2 ··· am,1 ··· ··· .. . ···  a1,n a2,n    ···  am,n

poate fi reprezentat˘ ca o dat˘ structurat˘ (tablou bidimensional) specificˆ fiecare a a a ınd element prin doi indici (de linie ¸i de coloan˘). s a O structur˘ de date este deci o colectie de date, eventual de tipuri diferite, a ¸ pe care s-a definit o anumit˘ organizare ¸i c˘reia ˆ este specific un anumit mod de a s a ıi identificare a elementelor componente. Componetele unei structuri de date pot fi identificate prin nume sau prin ordinea pe care o ocup˘ ˆ cadrul structurii. a ın Dac˘ accesul la o anumit˘ component˘ a structurii de date se poate face f˘r˘ a a a aa s˘ ¸inem seama de celelalte componente, vom spune c˘ structura de date este cu at a acces direct. ˆ schimb, dac˘ accesul la o component˘ a structurii de date se poate In a a face numai ¸inˆnd cont de alte cˆmpuri ale structurii (ˆ conformitate cu ordinea t a a ın structurii, printr-un proces de traversare) atunci vom spune c˘ structura este cu a acces secvential. ¸ Structurile de date pot fi create pentru a fi depozitate ˆ memoria intern˘ ın a (aceste structuri de date se numesc structuri interne) sau ˆ memoria extern˘ (se ın a numesc structuri externe, sau fi¸iere). Structurile interne au un caracter de date s temporare (ele dispar odat˘ cu ˆ a ıncetarea activit˘¸ii de prelucrare) iar cele externe at au un caracter de date permanente (mai bine spus, de lung˘ durat˘). a a Dac˘ pe lˆng˘ componentele structurii se ˆ a a a ınregistreaz˘ pe suport ¸i alte date a s suplimentare care s˘ materializeze relatia de ordonare, atunci structura de date a ¸ respectiv˘ este explicit˘, ˆ caz contrar este implicit˘. De exemplu, structura de a a ın a date de tip tablou este o structur˘ implicit˘ de date iar structura de date de tip a a list˘ liniar˘ este o structur˘ explicit˘ de date. a a a a Asupra structurilor de date se pot efectua operatii care se refer˘ structura ¸ a respectiv˘ sau la valorile datelor componente. Cele mai importante operatii sunt: a ¸ − operatia de creare, care const˘ ˆ memorarea pe suportul de memorie a ¸ a ın structurii de date ˆ forma sa initial˘, ın ¸ a

10

CAPITOLUL 2. STRUCTURI DE DATE

− operatia de consultare, care const˘ ˆ accesul la elementele structurii ˆ ¸ a ın ın vederea prelucr˘rii valorilor acestora, ¸i a s − operatia de actualizare, care const˘ ˆ ad˘ugarea de noi elemente, sau ¸ a ın a eliminarea elementelor care nu mai sunt necesare, sau modificarea valorilor unor componente ale structurii. Toate structurile de date la fel organizate ¸i pe care s-au definit acelea¸i s s operatii, poart˘ numele de tip de structur˘ de date. Dac˘ analiz˘m ˆ a operatiile ¸ a a a a ıns˘ ¸ care se efectueaz˘ asupra unei structuri de date, vom putea vedea c˘ toate acestea a a se reduc la executarea, eventual repetat˘, a unui grup de operatii specifice numite a ¸ operatii de baz˘. ¸ a

2.2

Structuri ¸i tipuri de date abstracte s

2.2.1

Structuri de date abstracte

Abstractizarea datelor reprezint˘ de fapt concentrarea asupra esentialului, a ¸ ignorˆnd detaliile (sau altfel spus, conteaz˘ ”ce” nu ”cum”). a a St˘pˆnirea aplicatiilor complexe se obtine prin descompunerea ˆ module. a a ¸ ¸ ın Un modul trebuie s˘ fie simplu, cu complexitatea ascuns˘ ˆ interiorul lui, ¸i s˘ a a ın s a aib˘ o interfat˘ simpl˘ care s˘ permit˘ folosirea lui f˘r˘ a cunoa¸te implementarea. a ¸a a a a aa s O structur˘ de date abstract˘ este un modul constˆnd din date ¸i operatii. a a a s ¸ Datele sunt ascunse ˆ interiorul modulului ¸i pot fi accesate prin intermediul ın s operatiilor. Structura de date este abstract˘ deoarece este cunoscut˘ numai interfata ¸ a a ¸ structurii, nu ¸i implementarea (operatiile sunt date explicit, valorile sunt definite s ¸ implicit, prin intermediul operatiilor). ¸

2.2.2

Tipuri de date abstracte

Procesul de abstractizare se refer˘ la dou˘ aspecte: a a • abstractizarea procedural˘, care separ˘ propriet˘¸ile logice ale unei actiuni de a a at ¸ detaliile implement˘rii acesteia a • abstractizarea datelor, care separ˘ propriet˘¸ile logice ale datelor de detaliile a at reprezent˘rii lor a O structur˘ de date abstracte are un singur exemplar (o singur˘ instanta). a a ¸˘ Pentru a crea mai multe exemplare ale structurii de date abstracte se define¸te un s tip de date abstract. ˆ Java, de exemplu, clasa asigur˘ un mod direct de definire In a a oric˘rui tip de date abstract. a

2.3. STRUCTURI DE DATE ELEMENTARE

11

2.3

Structuri de date elementare

2.3.1

Liste

O list˘ este o colectie de elemente de informatie (noduri) aranjate ˆ a ¸ ¸ ıntr-o anumit˘ ordine. Lungimea unei liste este num˘rul de noduri din list˘. Structura a a a corespunzatoare de date trebuie s˘ ne permit˘ s˘ determin˘m eficient care este a a a a primul/ultimul nod ˆ structur˘ ¸i care este predecesorul/succesorul unui nod dat ın as (dac˘ exist˘). Iat˘ cum arat˘ cea mai simpl˘ list˘, lista liniar˘: a a a a a a a
capul listei coada listei

Figura 2.1: List˘ liniar˘ a a O list˘ circular˘ este o list˘ ˆ care, dup˘ ultimul nod, urmeaz˘ primul nod, a a a ın a a deci fiecare nod are succesor ¸i predecesor. s Cˆteva dintre operatiile care se efectueaz˘ asupra listelor sunt: inserarea a ¸ a (ad˘ugarea) unui nod, extragerea (¸tergerea) unui nod, concatenarea unor liste, a s num˘rarea elementelor unei liste etc. a Implementarea unei liste se realizeaz˘ ˆ dou˘ moduri: secvential ¸i ˆ antuit. a ın a ¸ s ın˘ ¸ Implementarea secvential˘ se caracterizeaz˘ prin plasarea nodurilor ˆ locatii a a ın ¸ succesive de memorie, ˆ conformitate cu ordinea lor ˆ list˘. Avantajele acestui ın ın a mod de implementare sunt accesul rapid la predecesorul/succesorul unui nod ¸i s g˘sirea rapid˘ a primului/ultimului nod. Dezavantajele sunt modalit˘¸ile relativ a a at complicate de inserarea/¸tergere a unui nod ¸i faptul c˘, ˆ general, nu se folose¸te s s a ın s ˆ ıntreaga memorie alocat˘ listei. a Implementarea ˆ antuit˘ se caracterizeaz˘ prin faptul c˘ fiecare nod contine ınl˘ ¸ a a a ¸ dou˘ p˘rti: informatia propriu-zis˘ ¸i adresa nodului succesor. Alocarea memoriei a a¸ ¸ as pentru fiecare nod se poate face ˆ mod dinamic, ˆ timpul rul˘rii programului. ın ın a Accesul la un nod necesit˘ parcurgerea tuturor predecesorilor s˘i, ceea ce conduce a a la un consum mai mare de timp pentru aceast˘ operatie. ˆ schimb, operatiile a ¸ In ¸ de inserare/¸tergere sunt foarte rapide. Se consum˘ exact atˆt spatiu de memorie s a a ¸ cˆt este necesar dar, evident, apare un consum suplimentar de memorie pentru a ˆ ınregistrarea leg˘turii c˘tre nodul succesor. Se pot folosi dou˘ adrese ˆ loc de a a a ın una, astfel ˆ at un nod s˘ contin˘ pe lang˘ adresa nodului succesor ¸i adresa ıncˆ a ¸ a a s nodului predecesor. Obtinem astfel o list˘ dublu inl˘ntuit˘, care poate fi traversat˘ ¸ a a ¸ a a ˆ ambele directii. ın ¸ Listele ˆ antuite pot fi reprezentate prin tablouri. ˆ acest caz, adresele ınl˘ ¸ In nodurilor sunt de fapt indici ai tabloului.

12

CAPITOLUL 2. STRUCTURI DE DATE

O alternativ˘ este s˘ folosim dou˘ tablouri val ¸i next astfel: s˘ memor˘m a a a s a a informatia fiecarui nod i ˆ locatia val[i], iar adresa nodului s˘u succesor ˆ locatia ¸ ın ¸ a ın ¸ next[i]. Indicele locatiei primului nod este memorat ˆ variabila p. Vom conveni ca, ¸ ın pentru cazul listei vide, s˘ avem p = 0 ¸i next[u] = 0 unde u reprezint˘ ultimul nod a s a din list˘. Atunci, val[p] va contine informatia primului nod al listei, next[p] adresa a ¸ ¸ celui de-al doilea nod, val[next[p]] informatia din al doilea nod, next[next[p]] ¸ adresa celui de-al treilea nod, etc. Acest mod de reprezentare este simplu dar apare problema gestion˘rii locatiilor libere. O solutie este s˘ reprezent˘m locatiile a ¸ ¸ a a ¸ libere tot sub forma unei liste ˆ ınlantuite. Atunci, ¸tergerea unui nod din lista ¸ s initial˘ implic˘ inserarea sa ˆ lista cu locatii libere, iar inserarea unui nod ˆ lista ¸ a a ın ¸ ın initial˘ implic˘ ¸tergerea sa din lista cu locatii libere. Pentru implementarea listei ¸ a as ¸ de locatii libere, putem folosi acelea¸i tablouri dar avem nevoie de o alt˘ variabil˘, ¸ s a a f reehead, care s˘ contin˘ indicele primei locatii libere din val ¸i next. Folosim a ¸ a ¸ s acelea¸i conventii: dac˘ f reehead = 0 ˆ s ¸ a ınseamn˘ c˘ nu mai avem locatii libere, iar a a ¸ next[ul] = 0 unde ul reprezint˘ ultima locatie liber˘. a ¸ a Vom descrie in continuare dou˘ tipuri de liste particulare foarte des folosite. a

2.3.2

Stive ¸i cozi s

O stiv˘ este o list˘ liniar˘ cu proprietatea c˘ operatiile de inserare/extragere a a a a ¸ a nodurilor se fac ˆ ın/din coada listei. Dac˘ nodurile A, B, C sunt inserate ˆ a ıntr-o stiv˘ ˆ aceast˘ ordine, atunci primul nod care poate fi ¸ters/extras este C. ˆ mod a ın a s In echivalent, spunem c˘ ultimul nod inserat este singurul care poate fi ¸ters/extras. a s Din acest motiv, stivele se mai numesc ¸i liste LIFO (Last In First Out). s Cel mai natural mod de reprezentare pentru o stiv˘ este implementarea a secvential˘ ˆ ¸ a ıntr-un tablou S[1..n], unde n este num˘rul maxim de noduri. Primul a nod va fi memorat ˆ S[1], al doilea ˆ S[2], iar ultimul ˆ S[top], unde top este ın ın ın o variabil˘ care contine adresa (indicele) ultimului nod inserat. Initial, cˆnd stiva a ¸ ¸ a este vid˘, avem (prin conventie) top = 0. a ¸ O coad˘ este o list˘ liniar˘ ˆ care inser˘rile se fac doar ˆ capul listei, iar a a a ın a ın ¸tergerile/extragerile se fac doar din coada listei. Din acest motiv, cozile se mai s numesc ¸i liste FIFO (First In First Out). s O reprezentare secvential˘ pentru o coad˘ se obtine prin utilizarea unui ¸ a a ¸ tablou C[0..n − 1], pe care ˆ trat˘m ca ¸i cum ar fi circular: dup˘ locatia C[n − 1] ıl a s a ¸ urmeaz˘ locatia C[0]. Fie tail variabila care contine indicele locatiei predecesoare a ¸ ¸ ¸ primei locatii ocupate ¸i fie head variabila care contine indicele locatiei ocupate ¸ s ¸ ¸ ultima oar˘. Variabilele head ¸i tail au aceea¸i valoare atunci ¸i numai atunci cˆnd a s s s a coada este vid˘. Initial, avem head = tail = 0. a ¸ Trebuie s˘ observ˘m faptul c˘ testul de coad˘ vid˘ este acela¸i cu testul de a a a a a s coad˘ plin˘. Dac˘ am folosi toate cele n locatii la un moment dat, atunci nu am a a a ¸ putea distinge ˆ ıntre situatia de ”coad˘ plin˘” ¸i cea de ”coad˘ vid˘”, deoarece ˆ ¸ a a s a a ın ambele situatii am avea head = tail. ˆ consecint˘, vom folosi efectiv, ˆ orice ¸ In ¸a ın moment, cel mult n − 1 locatii din cele n ale tabloului C. ¸

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

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

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

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

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

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

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

ALGORITMI 3. nu numai pentru o problem˘ particular˘. Un algoritm trebuie s˘ prevad˘. Din a a a aceast˘ cauz˘. o metod˘ de rezolvare a unei ecuatii particulare nu poate a a a ¸ fi considerat˘ algoritm. o metod˘ a a a s a a a care nu asigur˘ obtinerea rezultatului dup˘ un num˘r finit de pa¸i nu a ¸ a a s poate fi considerat˘ algoritm. Sunt prelucr˘ri caracterizate prin faptul c˘ a a aceea¸i prelucrare (simpl˘ sau structurat˘) este repetat˘ cˆt s a a a a timp este ˆ ındeplinit˘ o anumit˘ conditie. a • Determinism. descrierea ˆ limba romˆn˘ (ca ¸i ˆ limba englez˘ [15]) ˆ a ın a a s ın a ın mod uzual nu este o idee mai bun˘.2. Detaliile sintactice.3 Tipuri de prelucr˘ri a Prelucr˘rile care intervin ˆ a ıntr-un algoritm pot fi simple sau structurate. a • Finitudine.2 Propriet˘¸ile algoritmilor at Principalele propriet˘¸i pe care trebuie s˘ le aib˘ un algoritm sunt: at a a • Generalitate. Dac˘ ˆ cadrul algoritmului nu intervin a ın a ın elemente aleatoare. atunci ori de cˆte ori se aplic˘ algoritmul aceluia¸i a a s set de date de intrare trebuie s˘ se obtin˘ acela¸i rezultat. ¸a ın Pe de alt˘ parte.20 CAPITOLUL 3. a − Repetitive. Algoritmii au o serie de structuri . Orice algoritm trebuie s˘ permit˘ obtinerea rezultatului a a ¸ dup˘ un num˘r finit de prelucr˘ri (pa¸i). • Prelucr˘rile simple sunt atribuiri de valori variabilelor. de exemplu din Pascal. eventual prin a evaluarea unor expresii. • Prelucr˘rile structurate pot fi de unul dintre tipurile: a − Liniare. modul de solutionare a tuturor situatiilor care pot s˘ aa at ¸ ¸ a apar˘ ˆ rezolvarea problemei. C/C++ sau Java.2. Din aceast˘ cauz˘. a ¸ a s 3. ın ın − Alternative.ˆ special a ın . f˘r˘ ambiguit˘¸i ¸i a a aa at s f˘r˘ neclarit˘¸i. Sunt secvente de prelucr˘ri simple sau structurate ¸ a care sunt efectuate ˆ ordinea ˆ care sunt specificate.3 Descrierea algoritmilor Algoritmii nu sunt programe. Sunt prelucr˘ri caracterizate prin faptul c˘ ˆ a a ın functie de realizarea sau nerealizarea unei conditii se alege ¸ ¸ una din dou˘ sau mai multe variante de prelucrare. a a ¸ 3. nu au nici o important˘ ˆ elaborarea/proiectarea algoritmilor. deci ei nu trebuie specificati ˆ ¸ ıntr-un limbaj de programare. Un algoritm trebuie s˘ poat˘ fi utilizat pentru o clas˘ a a a ˆ ıntreag˘ de probleme.

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

. distanta[v] = infinit. a ın a 3. ˆ at ıntre limbajul natural sau cel matematic ¸i un limbaj de programare. predecesor[v]=-1. ALGORITMI Valorile cn−1 . . fiecare a ın programator putˆnd s˘ conceap˘ propriul pseudocod. a ın a dar ¸i evolutia modului de concepere a programelor. numit bloc. fac ca schemele logice s˘ fie s ¸ a din ce ˆ ce mai putin folosite (ˆ favoarea limbajelor algoritmice).. ın Nu exist˘ un anumit standard ˆ elaborarea limbajelor algoritmice. n − 2..3 Pseudocod Un limbaj algoritmic este o notatie care permite exprimarea logicii algorit¸ milor ˆ ıntr-un mod formalizat f˘r˘ a fi necesare reguli de sintax˘ riguroase.. } .3. 2. sunt schemele logice ¸i limbajele s s algoritmice. 0. este dificil˘ ˆ ¸ ın a ıntrucˆt nu a sunt pu¸i ˆ evident˘ foarte clar pa¸ii algoritmului. c0 reprezint˘ coeficientii cˆtului.22 CAPITOLUL 3. c1 . ın ¸ ın 3. s ın ¸a s Modalit˘¸i intermediare de descriere a algoritmilor. Se poate folosi sintaxa lima as a bajului de programare preferat. cn−2 . ˆ care apar enunturi de prelucr˘ri. iar ultima valoare a ¸ a calculat˘ reprezint˘ valoarea restului (valoarea polinomului calculat˘ ˆ b). cu conditia ca acesta s˘ a a a ¸ a permit˘ o descriere clar˘ ¸i neambigu˘ a algoritmilor.3. ca ˆ aa a ın cazul limbajelor de programare... ˆ aceast˘ ordine.. De exemplu: ın ¸ a for fiecare vˆrf v din V a { culoare[v] = alb. 1. Acest dezavantaj.2 Scheme logice Scrierea unui program pornind de la un algoritm descris ˆ ıntr-un limbaj mai mult sau mai putin riguros. Un limbaj algoritmic mai este denumit ¸i pseudocod. ca ˆ exemplele de mai sus. a a a ın Si ˆ acest caz prelucrarea principal˘ este una repetitiv˘ constˆnd ˆ evaluarea ¸ ın a a a ın expresiei bck + ak pentru k luˆnd. Schemele logice au avantajul c˘ sunt sugestive dar ¸i dezavantajul c˘ pot a s a deveni dificil de urm˘rit ˆ cazul unor prelucr˘ri prea complexe. Un algoritm descris s ˆ pseudocod contine atˆt enunturi care descriu operatii ce pot fi traduse direct ın ¸ a ¸ ¸ ˆ ıntr-un limbaj de programare (unui enunt ˆ limbaj algoritmic ˆ corespunde o ¸ ın ıi instructiune ˆ program) cˆt ¸i enunturi ce descriu prelucr˘ri ce urmeaz˘ a fi ¸ ın a s ¸ a a detaliate abia ˆ momentul scrierii programului. valorile n − 1. iar modul de ˆ antuire s a ınl˘ ¸ a blocurilor este specificat prin segmente orientate. Schemele logice sunt descrieri grafice ale algoritmilor ˆ care fiec˘rui ın a pas i se ata¸eaz˘ un simbol grafic.

3.4. LIMBAJ ALGORITMIC

23

3.4

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

3.4.1

Declararea datelor

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

3.4.2

Operatii de intrare/ie¸ire ¸ s

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

24

CAPITOLUL 3. ALGORITMI

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

3.4.3

Prelucr˘ri liniare a

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

3.4.4

Prelucr˘ri alternative a

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

3.4. LIMBAJ ALGORITMIC

25

3.4.5

Prelucr˘ri repetitive a

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

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

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

26

CAPITOLUL 3. ALGORITMI

3.4.6

Subalgoritm

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

3.4. LIMBAJ ALGORITMIC

27

3.4.7

Probleme rezolvate

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

28

CAPITOLUL 3. ALGORITMI

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

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

29

sn =
k=0

1 . k!

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

ci,j =
k=1

ai,k · bk,j ,

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

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

30

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

/* matrice */

3.4.8

Probleme propuse

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

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

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

3.4. LIMBAJ ALGORITMIC

31

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

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

a.

b.

c.

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

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

32

CAPITOLUL 3. ALGORITMI

3.5

Instructiuni corespondente limbajului algorit¸ mic

3.5.1

Declararea datelor

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. Evident R0 = R1 = 1. = u0 v0 1 1 1 0 n × 1 0 static int fibo(int n) { int u. f ibo(n) f ibo(n − 1) = 1 1 1 0 u v × = f ibo(n − 1) f ibo(n − 2) 1 1 1 0 × = . Punˆnd Rn = Rn + 1. } . int i. v0 = v. } return u. v. a ¸ a ′ ′ ′ ′ a s ¸ a ¸i R1 = R0 = 2. int u0. v0. Rezult˘ Rn = 2 · f ibo(n) ¸i de aici obtinem c˘ Rn = 2 · f ibo(n) − 1. S˘ not˘m prin Rn num˘rul apelurilor functiei a a a ¸ f ibo pentru calculul lui f ibo(n).5. for (i = 2. obtinem c˘ Rn = Rn−1 + Rn−2 pentru n > 1. Deci f ibo(4) → → f ibo(3) + f ibo(2) (f ibo(2) + f ibo(1)) + f ibo(2) ((f ibo(1) + f ibo(1)) + f ibo(1)) + f ibo(2) ((1 + f ibo(1)) + f ibo(1)) + f ibo(2) ((1 + 1) + f ibo(1)) + f ibo(2) (2 + f ibo(1)) + f ibo(2) (2 + 1) + f ibo(2) 3 + f ibo(2) 3 + (f ibo(1) + f ibo(1)) 3 + (1 + f ibo(1)) 3 + (1 + 1) 3+2 5 73 → → → → → → → → → → → Exist˘ deci un num˘r semnificativ de apeluri succesive ale functiei f ib (9 a a ¸ apeluri pentru calculul lui f ibo(4)). ¸i Rn = 1 + Rn−1 + Rn−2 s ′ ′ ′ pentru n > 1. v = 1. s Num˘rul de apeluri recursive este foarte mare! Exist˘ o metod˘ iterativ˘ simpl˘ a a a a a care permite calculul lui f ibo(n) mult mai repede. ++i) { u0 = u.. u = 1. i <= n.1. FUNCTII RECURSIVE ¸ argumentului. u = u0 + v0. v = v0.

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

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

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

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

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

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

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

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

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

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

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

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

Obtinem succesiv a ¸ T (2k ) = 3T (2k−1 ) + c2k tk = 3tk−1 + c2k cu ecuatia caracteristic˘ ¸ a tk = c1 3k + c2 2k T (n) = c1 3lg n + c2 n (x − 3)(x − 2) = 0 (x − 4)2 = 0 . tk = c1 4k + c2 2k . ajungem la recurenta a tk = 4tk−1 + 4k cu ecuatia caracteristic˘ ¸ a ¸i solutia general˘ tk = c1 42 + c2 k42 . ANALIZA ALGORITMILOR RECURSIVI ˆ care ˆ ın ınlocuim pe n cu 2k . ˆ s Inlocuim la loc pe k cu log2 n T (n) = c1 n2 + c2 n Rezult˘ a T (n) ∈ O(n2 |n este o putere a lui 2) Un al doilea exemplu ˆ reprezint˘ ecuatia ıl a ¸ T (n) = 4T (n/2) + n2 . n > 1 c fiind o constant˘. s ¸ a Atunci. s˘ consider˘m ¸i exemplul In a s a a s T (n) ∈ O(n2 log n|n este o putere a lui 2) T (n) = 3T (n/2) + cn.86 CAPITOLUL 6. n > 1 Procedˆnd la fel. notam tk = T (2k ) = T (n) ¸i obtinem s ¸ tk = 4tk−1 + 2k Ecuatia caracteristic˘ a acestei recurente liniare este ¸ a ¸ (x − 4)(x − 2) = 0 ¸i deci. T (n) = c1 n2 + c2 n2 lg n ¸i obtinem s ¸ ˆ sfˆr¸it.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ALGORITMI COMBINATORIALI .126 CAPITOLUL 8.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4}.out. } static int pozitiaCautBin(int p.print(a[j] + " "). for(int j=k. j--) a[j]=a[j-1].*. iar ˆ loc s˘ se fac˘ insertia direct˘ ˆ ın a ın a a ¸ a ın aceast˘ secvent˘.length. j++) System.2 Insertie binar˘ ¸ a Metoda anterioar˘ se poate ˆ a ımbunat˘¸i dac˘ ¸inem cont de faptul c˘ secventa at at a ¸ ˆ care se face inserarea este deja ordonat˘. Un program complet este: a import java. else return i.io. for(j=0.1. if (x>a[i]) p=i+1.6.println().9. System. j<a. static void afiseaza() { int j.out. int i) { if (i != k) { int x=a[k]. while (p <= u) { i=(p+u)/2. j>=i+1.3.10. int u. SORTARE PRIN INSERTIE ¸ 141 10. } static void deplasare(int k. } return p. else if (x<a[i]) u=i-1. class SortInsBinApl { static int[] a={3.5. c˘utarea pozitiei pe care se face inserarea se face prin c˘utare a ¸a a ¸ a binar˘.3. a[i]=x.8. } } . int x) { int i=u+1.4.

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

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

ALGORITMI ELEMENTARI DE SORTARE .144 CAPITOLUL 10.

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

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

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

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

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

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

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

vida. a a class Coada { final static int MaxC = 100. se incrementeaz˘ sf arsit ¸i se d˘ acest a s a s a num˘r clientului respectiv. } static boolean esteVida(Coada c) { return c. plina= false. int sfarsit. c. vida. LISTE Fiecare client care se prezint˘ obtine un num˘r ¸i clientii sunt apoi chemati de c˘tre a ¸ a s ¸ ¸ a functionarul de la ghi¸eu ˆ ordinea cresc˘toare a numerelor de ordine primite la ¸ s ın a sosire. c.152 CAPITOLUL 11. gestionarul trebuie s˘ cunoasc˘ dou˘ a a a numere: num˘rul obtinut de c˘tre ultimul client sosit ¸i num˘rul obtinut de c˘tre a ¸ a s a ¸ a ultimul client servit. vida = true. c.plina = false. sfarsit = 0. incrementeaz˘ inceput ¸i cheam˘ posesorul acestui num˘r. s a as a • atunci cˆnd sose¸te un nou client. int inceput.vida = true. info = new int[MaxC]. int continut[]. Coada () { inceput = 0. boolean plina. sau coad˘ circular˘. dac˘ coada a ¸ a nu este vid˘.sfarsit = 0. a • atunci cˆnd functionarul este liber el poate servi un alt client. } static void facVida (Coada c) { c. } static Coada vida(){ return new Coada(). Pentru gestionarea acestui sistem. Not˘m aceste dou˘ numere inceput ¸i sf arsit ¸i gestion˘m a a s s a sistemul ˆ modul urm˘tor: ın a • coada de a¸teptare este vid˘ dac˘ ¸i numai dac˘ inceput = sf arsit. } . Se spune c˘ este un tablou (sau tampon) a circular. a a s a a ˆ cele ce urmeaz˘ sunt prezentate toate operatiile ˆ Java utilizˆnd tehnicile In a ¸ ın a urm˘toare: se reatribuie num˘rul 0 unui nou client atunci cˆnd se dep˘¸e¸te un a a a as s anumit prag pentru valoarea sf arsit.inceput = 0.

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

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

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

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

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

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

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

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

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

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

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

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

se scaneaz˘ tabloul la dreapta lui x pˆn˘ cˆnd se g˘se¸te un element aj < x a a a a a s 4. Avem T (n) = O(am ( ba )m ) = O(bkm ) = O(nk ). Rezult˘ c˘ am = bkm = cnk ¸i de aici T (n) = O(nk m) = O(nk logb n). se interschimb˘ ai cu aj a 5.3 Exemple Dintre problemele clasice care se pot rezolva prin metoda divide et impera mention˘m: ¸ a • c˘utare binar˘ a a • sortare rapid˘ (quickSort) a • problema turnurilor din Hanoi • sortare prin interclasare . a a s 3. la stˆnga In ın a lui x se vor g˘si elemente mai mici ca ¸i x.quicksort Se bazeaz˘ pe metoda interschimb˘rii.3. avˆnd tabloul a[]. EXEMPLE 165 2.3. ˆ a din nou. Dup˘ aceasta. ˆ acel moment. elemente mai mari ca a s ¸i x.2.4. pˆn˘ cˆnd aceste sub¸iruri sunt suficient de mici (se reduc la a a a s un singur element). la dreapta.2.8. 3. se alege la ˆ amplare un element x al tabloului ıntˆ 2.5.3. a < bk .1. se scaneaz˘ tabloul a[] la stˆnga lui x pˆn˘ cˆnd se g˘se¸te un element ai > x a a a a a a s 3. class QuickSort { static int x[]={3. se aplic˘ acela¸i proces sub¸irurilor de la stˆnga ¸i de la s a a s s a s dreapta lui x.Merge sort • dreptunghi de arie maxim˘ ˆ placa cu g˘uri.12. 4 pˆn˘ cˆnd scan˘rile se vor ˆ alni pe undeva la mijlocul a s a a a a ıntˆ tabloului. etc a ın a 12. se aplic˘ urm˘torul algoritm: ¸ a a a 1.4. interschimbarea se face a a ıns˘ pe distante mai mari.6.5. public static void main(String[]args) { . k 12. se repet˘ pa¸ii 2.1 Sortare prin partitionare . Astfel.3}. tabloul a[] va fi partitionat ˆ 2 astfel. a = bk .

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

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

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

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

170 CAPITOLUL 12.*. ’B’. class Hanoi { static int nrMutare=0.print("Introduceti numarul discurilor: "). char tijaSursa. a a a ˆ artim problema astfel: Imp˘ ¸ • se mut˘ primele n − 1 discuri de pe tija surs˘ A pe tija intermediar˘ B a a a • se mut˘ ultimul disc de pe tija surs˘ A pe tija destinatie C a a ¸ • se mut˘ cele n − 1 discuri de pe tija intermediar˘ B pe tija destinatie C a a ¸ pasul 1 A B a) C A B b) pasul 2 C pasul 3 A B d) C A B c) C import java. char tijaDestinatie) { if(nrDiscuri==1) .io. public static void main(String[] args) throws IOException { int nrDiscuri. nrDiscuri=Integer. ALGORITMI DIVIDE ET IMPERA • la un moment dat se va muta un singur disc (cel care se afl˘ deasupra a celorlalte discuri pe o tij˘).parseInt(br.out. char tijaIntermediara. }// main() static void solutiaHanoi(int nrDiscuri. a • un disc poate fi a¸ezat doar peste un disc de diametru mai mare decˆt s a al s˘ sau pe o tij˘ goal˘. System. ’A’.in)). solutiaHanoi(nrDiscuri. BufferedReader br = new BufferedReader(new InputStreamReader(System. ’C’).readLine()).

static int[] a=new int[MAX_NR_DISCURI]. public static void main(String[] args) throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System. .nc.tijaDestinatie.println(++nrMutare + " Disc 1 " + tijaSursa + " ==> "+ tijaDestinatie). discuri pe tija A.tijaIntermediara.tijaDestinatie).. b=new int[MAX_NR_DISCURI]. static int discMutat.nb.3.in)).out. solutiaHanoi(nrDiscuri-1. .out.println(++nrMutare + " Disk " + nrDiscuri + " " + tijaSursa + " --> "+ tijaDestinatie). static int na..12.*. System. EXEMPLE 171 System.io. // na = nr. else { solutiaHanoi(nrDiscuri-1.tijaIntermediara). c=new int[MAX_NR_DISCURI]. } }// solutiaHanoi() }// class Introduceti 1 Disc 1 A 2 Disk 2 A 3 Disc 1 B 4 Disk 3 A 5 Disc 1 C 6 Disk 2 C 7 Disc 1 A 8 Disk 4 A 9 Disc 1 B 10 Disk 2 B 11 Disc 1 C 12 Disk 3 B 13 Disc 1 A 14 Disk 2 A 15 Disc 1 B numarul discurilor: 4 ==> B --> C ==> C --> B ==> A --> B ==> B --> C ==> C --> A ==> A --> C ==> B --> C ==> C import java. etc.tijaSursa.tijaSursa. class HanoiDisc { static int nrMutare=0. static final int MAX_NR_DISCURI=9.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// coada pentru i din pozitia (i. dimensiune mare ¸ multe fire rupte.col) int[] qi=new int[qdim].col) int lm. dreapta=2. t2=System. jos=4. fire putine rupte a ¸ solutie unic˘.cp.t2.j) int[] qj=new int[qdim].*.200 CAPITOLUL 13. // dimensiunile plasei int lp. matriceCosturi(). stanga=8. // costul ajungerii in (i. cu o ”fant˘” a a # 0 1 2 3 4 5 6 7 8 9 m 10 10 2 140 131 100 100 138 140 138 n 8 8 2 140 131 130 7 138 140 138 k 11 46 0 20 2000 12771 23 9381 38365 273 min 16 74 2 278 103 12999 15 9050 555 274 O solutie backtracking simplu obtine maxim 20 de puncte.j) static int[][] d=new int[140][140]. afisSolutia().n. t1=System. // pozitie musca(lin.j) int ic. . // inceput/sfarsit coada public static void main(String[] args) throws IOException { long t1. traseul pe frontier˘ a o ”barier˘” pe mijlocul tablei. static int[][] p=new int[140][140]. // test 3 eroare date lm=144 ??? class Paianjen4 // coada circulara (altfel trebuie dimensiune mare !) { // traseu fara recursivitate (altfel depaseste stiva !) static final int qdim=200. sc. greu de g˘sit cu backtracking a multe fire rupte.cm. Codul surs˘ a import java. // directii pentru intoarcere de la musca! static static static static static static int m.currentTimeMillis(). dimensiune mare traseu ˆ spiral˘ solutie unic˘ ın a a traseu scurt. drum ˆ ”serpentine” ın firele interioare rupte. nici un fir rupt fire putine rupte. ALGORITMI BFS-LEE TESTE Obs dimensiune mic˘. citescDate().currentTimeMillis().io. // plasa static int[][] c=new int[140][140]. // coada pentru j din pozitia (i. // pozitie paianjen(lin. iar ˆ ¸ ¸ ımbun˘t˘¸it a at maxim 30 de puncte. // dimensiune coada circulara static final int sus=1. foarte multe fire rupte ¸ a caz particular.

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

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

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

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

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

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

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

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

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

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

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

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

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

a problemei a a a • o functie de selectie care indic˘ la orice moment care este cel mai promit˘tor ¸ ¸ a ¸a dintre candidatii ˆ a nefolositi ¸ ınc˘ ¸ • o functie obiectiv care d˘ valoarea unei solutii (timpul necesar execut˘rii ¸ a ¸ a tuturor lucr˘rilor ˆ a ıntr-o anumit˘ ordine. ultimul candidat ad˘ugat va r˘mˆne de acum ¸ ¸ a a a a ˆ ıncolo ˆ ea.2 Algoritmi greedy Algoritmii greedy sunt ˆ general simpli ¸i sunt folositi la rezolvarea unor ın s ¸ probleme de optimizare. Dac˘. nu neap˘rat optim˘. ¸ ¸ a a a acesta nu va mai fi niciodat˘ considerat.GREEDY 14. prima solutie g˘sit˘ va fi considerat˘ solutie optim˘ a ¸ a ¸ a a a ¸ a problemei. Dac˘ algoritmul a a ¸ ¸ a a greedy functioneaz˘ corect. dup˘ o astfel de ad˘ugare. etc. conform functiei de selectie. ˆ ıncerc˘m s˘ ad˘ug˘m la aceast˘ multime pe cel mai promitator a a a a a ¸ ¸˘ candidat. ˆ cele mai multe situatii de acest fel avem: In ¸ • o multime de candidati (lucr˘ri de executat. adic˘ dac˘ ¸ a a ¸ ¸ a a a este posibil s˘ complet˘m aceast˘ multime astfel ˆ at s˘ obtinem o solutie a a a ¸ ıncˆ a ¸ ¸ posibil˘. c˘ut˘m o solutie posibil˘ care s˘ a a ¸ a a optimizeze valoarea functiei obiectiv. multimea de a a a a ¸ candidati selectati este fezabil˘.) ¸ ¸ a a • o functie care verific˘ dac˘ o anumit˘ multime de candidati constituie o ¸ a a a ¸ ¸ solutie posibil˘. multimea ¸ ¸ a a a ¸ de candidati selectati nu mai este fezabil˘. nu neap˘rat optim˘. verific˘m ın a a a ¸ ¸ ¸ a dac˘ aceast˘ multime constituie o solutie posibil˘ a problemei. Solutia optim˘ nu este ˆ mod necesar unic˘: se poate ca functia obiectiv s˘ ¸ a ın a ¸ a aib˘ aceea¸i valoare optim˘ pentru mai multe solutii posibile.214 CAPITOLUL 14. dup˘ ad˘ugare. etc) ¸i pe care urm˘rim s˘ o optimiz˘m (minimiz˘m/maximiz˘m) a s a a a a a Pentru a rezolva problema de optimizare. a s a ¸ Descrierea formal˘ a unui algoritm greedy general este: a function greedy(C) // C este multimea candidatilor ¸ ¸ S←∅ // S este multimea ˆ care construim solutia ¸ ın ¸ while not solutie(S) and C = ∅ do x ← un element din C care maximizeaz˘/minimizeaz˘ select(x) a a C ← C − {x} if f ezabil(S ∪ {x}) then S ← S ∪ {x} if solutie(S) then return S else return ”nu exist˘ solutie” a ¸ . vˆrfuri ale grafului. elimin˘m ultimul candidat ad˘ugat. De fiecare dat˘ cˆnd l˘rgim multimea candidatilor selectati. multimea candidatilor selectati este vid˘. lungimea drumului pe care l-am a g˘sit. Dac˘. a problemei ¸ a a a • o functie care verific˘ dac˘ o multime de candidati este fezabil˘. s ¸ Initial. ¸ Un algoritm greedy construie¸te solutia pas cu pas. METODA OPTIMULUI LOCAL . ¸ ¸ ¸ ¸ a La fiecare pas.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

s ¸ a astfel ˆ at suma lor s˘ fie minim˘. ¸ ¸ a Date de intrare Prima linie a fi¸ierului pal.250 } } CAPITOLUL 14. fiind prietenul s˘u ¸ a ın a cel mai priceput ˆ algoritmi. ın Prin noul super-telefon al s˘u. ¸ a Cerint˘ ¸a Avˆnd datele necesare. printul transmite num˘rul de aparitii a fiec˘rei a ¸ a ¸ a cifre de pe u¸a turnului precum ¸i lungimea minim˘ L a primului num˘r.in contine un num˘r ˆ s ¸ a ıntreg L reprezentˆnd luna gimea minim˘ a primului num˘r. Printul ¸tie cum se formeaz˘ aceast˘ combinatie magic˘: trebuie s˘ utilizeze ¸ s a a ¸ a a toate cifrele scrise pe u¸a turnului pentru a obtine dou˘ numere palindroame. s Primul num˘r palindrom trebuie s˘ aib˘ cel putin L cifre. iar s s a a dumneavoastr˘ trebuie s˘-i trimiteti cˆt mai repede numerele cu care poate obtine a a ¸ a ¸ combinatia magic˘. Urmeaz˘ 10 linii: pe linia i + 2 se va afla un a a a num˘r ˆ a ıntreg reprezentˆnd num˘rul de aparitii ale cifrei i. Dac˘ exist˘ mai ¸ a a a multe solutii se va scrie doar una dintre ele. Acum interveniti dumneavoastr˘ ˆ poveste. ¸ Restrictii ¸i preciz˘ri ¸ s a • ˆ total vor fi cel mult 100 de cifre In • 1 ≤ L < 100 ¸i L va fi mai mic decˆt num˘rul total de cifre s a a • Pentru datele de test va exista ˆ ıntotdeauna solutie: se vor putea forma din ¸ cifrele scrise pe u¸a turnului dou˘ numere care ˆ s a ıncep cu o cifr˘ diferit˘ de 0. pentru i cu valori de la a a ¸ 0 la 9. Algorel poate evada dac˘ g˘se¸te combinatia magic˘ cu care poate deschide a a s ¸ a poarta turnului. iar cel de-al doilea a a a ¸ poate avea orice lungime diferit˘ de 0. Date de ie¸ire s Prima linie a fi¸ierului de ie¸ire pal.ONI2005 cls 9 Autor: Silviu G˘nceanu a Printul Algorel este ˆ ˆ ¸ ın ıncurc˘tur˘ din nou: a fost prins de Spˆnul cel a a a Negru ˆ ˆ ın ıncercarea sa de a o salva pe printes˘ ¸i acum este ˆ ¸ as ınchis ˆ Turnul cel ın Mare.out contine primul num˘r palidrom. iar a a primul num˘r s˘ aib˘ cel putin L cifre a a a ¸ . aflati dou˘ numere palindroame cu care se poate a ¸ a obtine combinatia magic˘. Numerele palindroame formate nu pot a ˆ ıncepe cu cifra 0.3. METODA OPTIMULUI LOCAL . iar aceast˘ sum˘ este combinatia magic˘ ce va ıncˆ a a a a ¸ a deschide u¸a.16 Pal .GREEDY 14. iar s s ¸ a cea de-a doua linie contine cel de-al doilea num˘r palindrom.

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

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

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

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

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

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

out 2 1 8 13 12 2 30 60 3 3 1 10 20 1 255 24 3 30 40 3 2 1 30 30 1234 2 27 28 5 Explicatie ¸ sunt necesari 2 paznici: paznicul 1 va p˘zi ˆ a ıntre borna 8 ¸i borna 13. paznicul 3 va p˘zi ˆ s a ıntre borna 30 ¸i s borna 40. iar detinutul p˘zit este 1. iar detinutii p˘ziti sunt 1.5 secunde (Linux) ¸ a Indicatii de rezolvare .3. EXEMPLE . iar detinutul p˘zit este 3 ¸ a 257 5 10 30 30 32 0 30 27 30 27 28 sunt necesari 2 paznici: paznicul 1 va p˘zi la a borna 30.descriere solutie ¸ ¸ Solutia comisiei ¸ Problema cere. a ¸ Pentru a le determina procedez astfel: • ordonez intervalele cresc˘tor dup˘ kilometrul minim ¸i descresc˘tor dup˘ a a s a a kilometrul maxim • pun primul detinut (deci cel cu intervalul de s˘pare cel mai mare) ˆ grija ¸ a ın primului paznic • pentru toate celelalte – caut un paznic care mai p˘ze¸te detinuti ¸i care poate p˘zi ¸i acest a s ¸ ¸ s a s detinut (adic˘ intersectia segmentelor s˘ fie nevid˘) ¸ a ¸ a a – dac˘ g˘sesc a a – ajustez intervalul de s˘pare ca s˘ poat˘ s˘pa ¸i acest detinut a a a a s ¸ – altfel (dac˘ nu g˘sesc) a a – mai am nevoie de un paznic ¸i ˆ dau ˆ grija acestuia s ıl ın .in 3 0 20 8 13 30 60 4 10 203 2 53 30 403 5 7 33 . de fapt. s iar detinutul p˘zit este 5 ¸ a !Solutia nu este unic˘! ¸ a Timp maxim de executie/test: 1 secund˘ (Windows). iar detinutii p˘ziti a ¸ ¸ a ¸ sunt 2 ¸i 4. determinarea num˘rului minim de intersectii ˆ a ¸ ıntre segmentele determinate de kilometrul minim ¸i maxim ˆ s ıntre care sap˘ un detinut. 0.14. iar s detinutul p˘zit este 3 ¸ a sunt necesari 3 paznici: paznicul 1 va p˘zi ˆ a ıntre borna 10 ¸i borna 20. s ¸ ¸ a ¸ s paznicul 2 va p˘zi ˆ a ıntre borna 30 ¸i borna 60. iar detinutii p˘ziti sunt 1 ¸i 2. 2. s ¸ a paznicul 2 va p˘zi la borna 5. 3 ¸i 4. ¸ ¸ a ¸ s paznicul 2 va p˘zi ˆ a ıntre borna 27 ¸i borna 28.

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

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

Edilii a a s a au pavat unele dintre leg˘turile directe dintre dou˘ a¸ez˘ri (numind o astfel de a a s a leg˘tur˘ pavat˘ ”strad˘”). ˆ calculul costului total de transport. a a Date de ie¸ire s Pe prima linie a fi¸ierului cezar. a a Toti senatorii trebuie s˘ participe la ¸edintele Senatului. Cezar a promis c˘ va dota Roma cu o lectic˘ a a gratuit˘ care s˘ circule pe un num˘r de k str˘zi ale Romei astfel ˆ at orice senator a a a a ıncˆ care va circula pe str˘zile respective. a a a a aa a Str˘zile pe care se deplaseaz˘ lectica gratuit˘ trebuie s˘ fie legate ˆ a a a a ıntre ele (zborul. METODA OPTIMULUI LOCAL .GREEDY 14. ei se ¸ a s ¸ In deplaseaz˘ cu lectica. ˆ ıntre oricare dou˘ a¸ez˘ri existˆnd leg˘turi directe sau indirecte. a a La alegerea sa ca prim consul. pentru In toti senatorii.3. Problema este de s a a alege cele k str˘zi ¸i amplasarea sediului s˘lii de ¸edinte a Senatului astfel ˆ at. Cezar a considerat c˘ fiecare senator va c˘l˘tori exact o dat˘ de la ¸ a aa a a¸ezarea sa pˆn˘ la sala de ¸edinte a Senatului. metroul sau teleportarea nefiind posibile la acea vreme). ˆ drumul lor spre sala de ¸edinte. s a a s ¸ Cerint˘ ¸a Scrieti un program care determin˘ costul minim care se poate obtine prin ¸ a ¸ alegerea adecvat˘ a celor k str˘zi pe care va circula lectica gratuit˘ ¸i a locului de a a as amplasare a s˘lii de ¸edint˘ a Senatului.260 CAPITOLUL 14. ın s ¸ s˘ fac˘ economii cˆt mai ˆ a a a ınsemnate. Orice senator care se deplaseaz˘ pe o strad˘ pl˘te¸te 1 ban a a a a s pentru c˘ a fost transportat cu lectica pe acea strad˘. A¸ez˘rile senatoriale sunt numerotate de la 1 s a la n. a a a a ¸ reprezentˆnd numerele de ordine a dou˘ a¸ez˘ri senatoriale ˆ a a s a ıntre care exist˘ strad˘. astfel ˆ at ˆ a a a a ıncˆ ıntre oricare dou˘ a¸ez˘ri senatoriale s˘ a s a a existe o singur˘ succesiune de str˘zi prin care se poate ajunge de la o a¸ezare a a s senatorial˘ la cealalt˘. a s a s ¸ ıncˆ prin folosirea transportului gratuit. s˘ poat˘ folosi lectica gratuit˘ f˘r˘ a pl˘ti. ˆ acest scop. Cezar a promis s˘ stabileasc˘ sediul s˘lii de ¸edinte a Senatului ˆ In a a a s ¸ ıntruna dintre a¸ez˘rile senatoriale aflate pe traseul lecticii gratuite.in contine s ¸ • pe prima linie dou˘ valori n k separate printr-un spatiu reprezentˆnd a ¸ a num˘rul total de senatori ¸i num˘rul de strazi pe care circul˘ lectica gratuit˘ a s a a a • pe urm˘torele n−1 linii se afl˘ cˆte dou˘ valori i j separate printr-un spatiu.18 Cezar . senatorii. a s ¸a Date de intrare Fi¸ierul cezar.out se va scrie costul total minim al transs port˘rii tuturor senatorilor pentru o alegere optim˘ a celor k str˘zi pe care va a a a circula lectica gratuit˘ ¸i a locului unde va fi amplasat˘ sala de ¸edinte a Senatuas a s ¸ lui. cˆte una pentru fiecare In a a s a a dintre cei n senatori ai Republicii. Restrictii ¸i preciz˘ri ¸ s a . ˆ plus.OJI2007 cls 11 ˆ Roma antic˘ exist˘ n a¸ez˘ri senatoriale distincte. O leg˘tur˘ a s a a a a a este direct˘ dac˘ ea nu mai trece prin alte a¸ez˘ri senatoriale intermediare.

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

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

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

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

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

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

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

268 CAPITOLUL 14.GREEDY . METODA OPTIMULUI LOCAL .

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

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

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

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

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

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

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

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

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

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

se trece la nivelul inferior k − 1 prin ie¸irea din a s procedura recursiv˘ f . pozitionare pe prima component˘ ¸ a while (k>0) cˆt timp exist˘ componente de analizat a a if (k==n+1) dac˘ x este solutie a ¸ {afis(x). An . ¸ ¸ ¸ x[k] = i ˆ ınseamn˘ c˘ pe pozitia k din solutia x se afl˘ ai din Ak .k<=n. ın ¸ a s a − cˆnd am g˘sit un succesor. se initializeaz˘ nivelul ¸i se caut˘ un succesor. .k++) x[k]=gol.2.2. verific˘m dac˘ este valid.. –k. ˆ caz contrar urmˆnd a se continua a ın a c˘utarea succesorului. a a ¸ • pornind de la restrictiile problemei se stabilesc conditiile de validitate ale ¸ ¸ solutiilor partiale (conditiile de continuare). A2 . A2 . ˆ caz afirmativ. o afi¸am ¸i revenim pe nivelul anterior.. a a a a In procedura se autoapeleaz˘ pentru k + 1. METODA BACTRACKING 279 ˆ aplicarea metodei pentru o problem˘ concret˘ se parcurg urm˘toarele In a a a etape: • se alege o reprezentare a solutiei sub forma unui vector cu n componente. a a ¸ ¸ a 15. Principiul de functionare al functiei a a a a ¸ f (primul apel este f (1)) corespunz˘tor unui nivel k este urm˘torul: a a − ˆ situatia ˆ care avem o solutie..15.2 Backtracking recursiv Varianta recursiv˘ a algoritmului backtracking poate fi realizat˘ dup˘ o a a a schem˘ asem˘n˘toare cu varianta recursiv˘... altfel golesc ¸i fac pas dreapta s } Vectorul solutie x contine indicii din multimile A1 . . ¸ ¸ s a 15. a − dac˘ nu avem succesor.1 Bactracking iterativ Structura general˘ a algoritmului este: a for(k=1. ın ¸ ın ¸ s˘ s − ˆ caz contrar.2. ¸ • se identific˘ multimile A1 . ¸ ¸ ¸ ˆ aplicatiile concrete solutia nu este obtinut˘ numai ˆ cazul ˆ care k = n In ¸ ¸ ¸ a ın ın ci este posibil s˘ fie satisf˘cut˘ o conditie de g˘sire a solutiei pentru k < n (pentru a a a ¸ a ¸ anumite probleme nu toate solutiile contin acela¸i num˘r de elemente). atunci m˘resc ¸i fac pas dreapta a s else ++x[k]. initiarizarea vectorului solutie ¸ ¸ k=1. a . An ¸i se stabile¸te o ordine ntre elemente a ¸ s s ˆ care s˘ indice modul de parcurgere a fiec˘rei multimi. altfel m˘resc ¸i r˘mˆn pe pozitie a s a a else x[k–]=gol. Mai precis.} atunci: afi¸are solutie ¸i pas stˆnga s ¸ s a else altfel: { if(x[k]<max[k]) dac˘ exist˘ elemente de ˆ a a ıncercat if(posibil(1+x[k])) dac˘ 1+x[k] este valid˘ a a ++x[k++]..

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

METODA BACKTRACKING .342 CAPITOLUL 15.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

372 }// for h

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

out.println(c[0][n+1]); out.close(); afism(c); afism(t); System.out.println("Ordinea taieturilor: \n"); taieturi(0,n+1); System.out.println(); }//main public static void taieturi(int i,int j) throws IOException { if(i>=j-1) return; int k; k=t[i][j]; System.out.println((++nt)+" : "+i+".."+j+" --> "+k); //br.readLine(); // "stop" pentru depanare ! if((i<k)&&(k<j)) { taieturi(i,k); taieturi(k,j); } }//taieturi static void afism(int[][] x) // pentru depanare ! { int i,j; for(i=0;i<=n+1;i++) { for(j=0;j<=n+1;j++) System.out.print(x[i][j]+" "); System.out.println(); } System.out.println(); }// afism(...) }//class /* n=5 x: 2 4 5 8 12 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 8 16 27 38 3 9 19 29 0 4 12 22 0 0 7 17 0 0 0 7 0 0 0 0 0 0 0 0

16.2. PROBLEME REZOLVATE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 2 0 0 0 0 0 2 3 3 0 0 0 0 3 4 4 4 0 0 0 4 4 4 4 5 0 0

373

Ordinea taieturilor: 1 : 2 : 3 : 4 : 5 : */ 0..6 0..4 0..2 2..4 4..6 --> --> --> --> --> 4 2 1 3 5

16.2.10

Triangularizarea poligoanelor convexe

Consider˘m un poligon convex cu n vˆrfuri numerotate cu 1, 2, ..., n (ˆ figur˘ a a ın a n = 9) ¸i dorim s˘ obtinem o triangularizare ˆ care suma lungimilor diagonalelor s a ¸ ın trasate s˘ fie minim˘ (ca ¸i cum am dori s˘ consum˘m cˆt mai putin tu¸ pentru a a s a a a ¸ s trasarea acestora!).
1 2 9 8 7 3 4 6 5 3 4 5 5 5 6 4 555 2 1 9 9 8 7 3 6 6 2 11 9 9

1

8 8

8 7

7

Evident, orice latur˘ a poligonului face parte dintr-un triunghi al triangulatiei. a ¸ Consider˘m la ˆ a ınceput latura [1, 9]. S˘ presupunem c˘ ˆ a a ıntr-o anumit˘ triangulatie a ¸ optim˘ latura [1, 9] face parte din triunghiul [1, 5, 9]. Diagonalele triangulatiei a ¸ optime vor genera o triangulatie optim˘ a poligoanelor convexe [1, 2, 3, 4, 5] ¸i ¸ a s [5, 6, 7, 8, 9]. Au ap˘rut astfel dou˘ subprobleme ale problemei initiale. a a ¸ S˘ not˘m prin p(i, k, j) perimetrul triunghiului [i, k, j] (i < k < j) i¸i prin a a s c[i][j] costul minim al triagulatiei poligonului convex [i, i + 1, ..., j] (unde i < j). ¸ Atunci: c[i][j] = 0, dac˘ j = i + 1 a

374 ¸i s

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

c[i][j] = min {p(i, k, j) + c[i][k] + c[k][j]}, dac˘ i < j ≤ n a
i≤k<j

16.2.11

Algoritmul Roy-Floyd-Warshall

// Lungime drum minim intre oricare doua varfuri in graf orientat ponderat. // OBS: daca avem un drum de lungime minima de la i la j atunci acest drum // va trece numai prin varfuri distincte, iar daca varful k este varf intermediar, // atunci drumul de la i la k si drumul de la k la j sunt si ele minime // (altfel ar exista un drum mai scurt de la i la j); astfel, este indeplinit // "principiul optimalitatii" import java.io.*; class RoyFloydWarshall // O(n^3) { static final int oo=0x7fffffff; static int n,m; static int[][] d; public static void main(String[]args) throws IOException { int i,j,k; StreamTokenizer st=new StreamTokenizer( new BufferedReader(new FileReader("RoyFloydWarshall.in"))); PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("RoyFloydWarshall.out"))); st.nextToken(); n=(int)st.nval; st.nextToken(); m=(int)st.nval; d=new int[n+1][n+1]; for(i=1;i<=n;i++) for(k=1;k<=m;k++) { st.nextToken(); st.nextToken(); st.nextToken(); } for(j=1;j<=n;j++) d[i][j]=oo;

i=(int)st.nval; j=(int)st.nval; d[i][j]=d[j][i]=(int)st.nval;

for(k=1;k<=n;k++) for(i=1;i<=n;i++)

// drumuri intre i si j care trec

16.2. PROBLEME REZOLVATE for(j=1;j<=n;j++) // numai prin nodurile 1, 2, ..., k if((d[i][k]<oo)&&(d[k][j]<oo)) if(d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j]; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) if(d[i][j]<oo) System.out.print(d[i][j]+" "); else System.out.print("*"+" "); System.out.println(); } out.close(); }//main }//class /* 6 6 1 2 1 3 2 3 4 5 5 6 4 6 */

375

3 1 2 1 3 2

2 3 1 * * *

3 4 2 * * *

1 2 2 * * *

* * * 2 1 2

* * * 1 2 3

* * * 2 3 4

16.2.12

Oracolul decide - ONI2001 cls 10

prof. Doru Popescu Anastasiu, Slatina La un concurs particip˘ N concurenti. Fiecare concurent prime¸te o foaie de a ¸ s hˆrtie pe care va scrie un cuvˆnt avˆnd cel mult 100 de caractere (litere mici ale a a a alfabetului englez). Cuvintele vor fi distincte. Pentru departajare, concurentii apeleaz˘ la un oracol. Acesta produce ¸i el ¸ a s un cuvnt. Va cˆ¸tiga concurentul care a scris cuvˆntul ”cel mai apropiat” de al as a oracolului. Gradul de ”apropiere” dintre dou˘ cuvinte este lungimea subcuvˆntului coa a mun de lungime maxim˘. Prin subcuvˆnt al unui cuvˆnt dat se ˆ ¸elege un cuvˆnt a a a ınt a care se poate obtine din cuvˆntul dat, eliminˆnd 0 sau mai multe litere ¸i p˘strˆnd ¸ a a s a a ordinea literelor r˘mase. a Cerint˘ ¸a Se cunosc cuvˆntul c0 produs de oracol ¸i cuvintele ci , i = 1, ..., N scrise de a s concurenti. Pentru a ajuta comisia s˘ desemneze cˆ¸tig˘torul, se cere ca pentru ¸ a as a fiecare i s˘ identificati pozitiile literelor ce trebuie ¸terse din c0 ¸i din ci astfel ˆ at a ¸ ¸ s s ıncˆ prin ¸tergere s˘ se obtin˘ unul dintre subcuvintele comune de lungime maxim˘. s a ¸ a a

376

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

Date de intrare Fi¸ier de intrare: ORACOL.IN s Linia 1: N num˘r natural nenul, reprezentˆnd num˘rul concurentilor; a a a ¸ Linia 2: c0 cuvˆntul produs de oracol; a Liniile 3..N+2: cuvˆnt a pe aceste N linii se afl˘ cuvintele scrise de cei a N concurenti, un cuvˆnt pe o linie; ¸ a Date de ie¸ire s Fi¸ier de ie¸ire: ORACOL.OUT s s Liniile 1..2*N: pozitiile literelor ce trebuie ¸terse ¸ s pe fiecare linie i (i = 1, 3, ..., 2 ∗ N − 1) se vor scrie numere naturale nenule, separate prin cˆte a un spatiu, reprezentˆnd pozitiile de pe care se vor ¸terge litere din cuvˆntul pro¸ a ¸ s a dus de oracol; pe fiecare linie j (j = 2, 4, ..., 2 ∗ N ) se vor scrie numere naturale nenule, separate prin cˆte un spatiu, reprezentˆnd pozitiile de pe care se vor ¸terge a ¸ a ¸ s litere din cuvˆntul concurentului cu num˘rul j/2. a a Restrictii ¸ 2 ≤ N ≤ 100 Dac˘ exist˘ mai multe solutii, ˆ fi¸ier se va scrie una singur˘. a a ¸ ın s a Dac˘ dintr-un cuvˆnt nu se va t˘ia nici o liter˘, linia respectiv˘ din fi¸ierul a a a a a s de intrare va r˘mˆne vid˘. a a a Exemplu ORACOL.IN ORACOL.OUT poate contine solutia: ¸ ¸ 3 3 abc 34 abxd aabxyc 145 acb 3 2 Timp maxim de executare/test: 1 secund˘ a Codul surs˘ a Varianta 1: import java.io.*; // subsir comun maximal - problema clasica ... class Oracol // varianta cu mesaje pentru depanare ... { static final char sus=’|’, stanga=’-’, diag=’*’; static int[][] a; static char[][] d; static String x,y; static boolean[] xx=new boolean[101]; static boolean[] yy=new boolean[101];

16.2. PROBLEME REZOLVATE static char[] z; static int m,n,nc;

377

public static void main(String[] args) throws IOException { int i,j,k; BufferedReader br=new BufferedReader(new FileReader("oracol.in")); PrintWriter out=new PrintWriter( new BufferedWriter(new FileWriter("oracol.out"))); nc=Integer.parseInt(br.readLine()); x=br.readLine().replace(" ",""); // elimina spatiile ... de la sfarsit ! m=x.length(); for(k=1;k<=nc;k++) { y=br.readLine().replaceAll(" ",""); // elimina spatiile ... daca sunt! n=y.length(); matrad(); afism(a); afism(d); System.out.print("O solutie oarecare: "); z=new char[a[m][n]+1]; for(i=1;i<=m;i++) xx[i]=false; for(j=1;j<=n;j++) yy[j]=false; osol(m,n); System.out.println("\n"); for(i=1;i<=m;i++) if(!xx[i]) out.print(i+" "); out.println(); for(j=1;j<=n;j++) if(!yy[j]) out.print(j+" "); out.println(); } out.close(); System.out.println("\n"); }// main(...) static void matrad() { int i,j; a=new int[m+1][n+1];

378

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

d=new char[m+1][n+1]; for(i=1;i<=m;i++) for(j=1;j<=n;j++) if(x.charAt(i-1)==y.charAt(j-1)) { a[i][j]=1+a[i-1][j-1]; d[i][j]=diag; } else { a[i][j]=max(a[i-1][j],a[i][j-1]); if(a[i-1][j]>a[i][j-1]) d[i][j]=sus; else d[i][j]=stanga; } }// matrad() static void osol(int lin, int col) { if((lin==0)||(col==0)) return; if(d[lin][col]==diag) osol(lin-1,col-1); else if(d[lin][col]==sus) osol(lin-1,col); else osol(lin,col-1); if(d[lin][col]==diag) { System.out.print(x.charAt(lin-1)); xx[lin]=yy[col]=true; } }// osol(...) static int max(int a, int b) { if(a>b) return a; else return b; }// max(...) static void afism(int[][] a) { int i,j; System.out.print(" "); for(j=0;j<n;j++) System.out.print(y.charAt(j)+" "); System.out.println();

16.2. PROBLEME REZOLVATE System.out.print(" "); for(j=0;j<=n;j++) System.out.print(a[0][j]+" "); System.out.println(); for(i=1;i<=m;i++) { System.out.print(x.charAt(i-1)+" "); for(j=0;j<=n;j++) System.out.print(a[i][j]+" "); System.out.println(); } System.out.println("\n"); }// afism(int[][]...) static void afism(char[][] d) // difera tipul parametrului { int i,j; System.out.print(" "); for(j=0;j<n;j++) System.out.print(y.charAt(j)+" "); System.out.println(); System.out.print(" "); for(j=0;j<=n;j++) System.out.print(d[0][j]+" "); System.out.println(); for(i=1;i<=m;i++) { System.out.print(x.charAt(i-1)+" "); for(j=0;j<=n;j++) System.out.print(d[i][j]+" "); System.out.println(); } System.out.println("\n"); }// afism(char[][]...) }// class Varianta 2: import java.io.*; // problema reala ... class Oracol { static final char sus=’|’, stanga=’-’, diag=’*’; static int[][] a; static char[][] d; static String x,y; static boolean[] xx=new boolean[101];

379

380

˘ CAPITOLUL 16. PROGRAMARE DINAMICA

static boolean[] yy=new boolean[101]; static int m,n,nc; public static void main(String[] args) throws IOException { int i,j,k; BufferedReader br=new BufferedReader(new FileReader("oracol.in")); PrintWriter out=new PrintWriter( new BufferedWriter( new FileWriter("oracol.out"))); nc=Integer.parseInt(br.readLine()); x=br.readLine().replace(" ",""); // elimina spatiile ... de la sfarsit ! m=x.length(); for(k=1;k<=nc;k++) { y=br.readLine().replaceAll(" ",""); // elimina spatiile ... daca sunt! n=y.length(); matrad(); for(i=1;i<=m;i++) xx[i]=false; for(j=1;j<=n;j++) yy[j]=false; osol(m,n); for(i=1;i<=m;i++) if(!xx[i]) out.print(i+" "); out.println(); for(j=1;j<=n;j++) if(!yy[j]) out.print(j+" "); out.println(); } out.close(); }// main(...) static void matrad() { int i,j; a=new int[m+1][n+1]; d=new char[m+1][n+1]; for(i=1;i<=m;i++) for(j=1;j<=n;j++) if(x.charAt(i-1)==y.charAt(j-1)) { a[i][j]=1+a[i-1][j-1]; d[i][j]=diag;

16.2. PROBLEME REZOLVATE } else { a[i][j]=max(a[i-1][j],a[i][j-1]); if(a[i-1][j]>a[i][j-1]) d[i][j]=sus; else d[i][j]=stanga; } }// matrad() static void osol(int lin, int col) { if((lin==0)||(col==0)) return; if(d[lin][col]==diag) osol(lin-1,col-1); else if(d[lin][col]==sus) osol(lin-1,col); else osol(lin,col-1); if(d[lin][col]==diag) xx[lin]=yy[col]=true; }// osol(...) static int max(int a, int b) { if(a>b) return a; else return b; }// max(...) }// class

381

16.2.13

Pav˘ri - ONI2001 clasa a X-a a

prof. Doru Popescu Anastasiu, Slatina Se d˘ un dreptunghi cu lungimea egal˘ cu 2N centimetri ¸i l˘¸imea egal˘ cu a a s at a 3 centimetri . Cerint˘ ¸a S˘ se determine num˘rul M al pav˘rilor distincte cu dale dreptunghiulare a a a care au lungimea egal˘ cu un centimetru ¸i l˘¸imea egal˘ cu 2 centimetri. a s at a Datele de intrare Fi¸ier de intrare: pavari.in s Linia 1: N - num˘r natural nenul, reprezentˆnnd jum˘tatea lungimii drepa a a tunghiului. Datele de ie¸ire s Fi¸ier de ie¸ire: pavari.out s s Linia 1: M - num˘r natural nenul, reprezentˆnd num˘rul modalit˘¸ilor de a a a a at pava dreptunghiul. Restrictii ¸i preciz˘ri ¸ s a • 1 ≤ N ≤ 100

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

428 ˘ CAPITOLUL 16. PROGRAMARE DINAMICA .

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

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

¸ s Pentru t: 1 a ¸i s 1 a 2 b 3 a 1 a 3 a 4 b 2 b 4 b 5 a 3 a 5 a a 1 5 a 6 b 4 a 6 b b 2 6 b 7 a 5 b 7 a a 3 7 a 8 a 6 a 8 a a 4 8 a 9 b 7 b 9 b b 5 9 b 10 a 11 b 12 a 13 a 14 b 15 a 16 b 17 a p: 2 b exist˘ dou˘ potriviri: a a 10 a a 6 10 a a 1 11 b b 7 11 b b 2 12 a 13 a 14 b 15 a 16 b 17 a t: p: t: p: 1 a 2 b 3 a 4 b 12 a a 3 13 a a 4 14 b b 5 15 a a 6 16 b b 7 17 a Sirul ineficient de ˆ ¸ ıncerc˘ri este: a 1 a a 2 b b a 3 a a b a 4 b a a b a 5 a b a a b a 6 b a b a a b a 7 a b a b a a b a 8 a b a b a a b a 9 b 10 a 11 b 12 a 13 a 14 b 15 a 16 b 17 a t: p: p: p: p: p: p: p: p: p: p: p: b a b a a b a b a b a a b a b a b a a b a 1 * b a b a a b 2 b a b a a 3 b a b a 4 b a b 5 b a 6 * b 7 Prima nepotrivire din fiecare ˆ ıncercare este evidentiat˘ prin caracter boldat ¸ a iar solutiile sunt marcate cu *.2. UN ALGORITM EFICIENT .2 Un algoritm eficient .17. ¸ Dorim s˘ avans˘m cu mai mult de un pas la o nou˘ ˆ a a a ıncercare.KMP Algoritmul KMP (Knuth-Morris-Pratt) determin˘ potrivirea ¸irurilor folosind a s informatii referitoare la potrivirea sub¸irului cu diferite deplasamente ale sale. f˘r˘ s˘ risc˘m aa a a s˘ pierdem vreo solutie! a ¸ .KMP 431 17.

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

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

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

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

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

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

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

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

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

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

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

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

POTRIVIREA SIRURILOR ¸ .444 CAPITOLUL 17.

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

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

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

1 5 4 3 2 1 1 2 3 4 5 6 7 5 4 3 2 1 1 2 3 4 5 6 7 a) b) Toate punctele de pe ˆ a¸ur˘toarea convex˘ (cazul a) ˆ figur˘): ınf˘s a a ın a import java.6 ˆ a¸ur˘toarea convex˘ Inf˘s a a ˆ Impachetarea Jarvis 18. // infasuratoare convexa class Jarvis1 // pe frontiera coliniare ==> le iau pe toate . ¸ ¸ a ın Dac˘ nu se afl˘ ˆ nici un poligon convex obtinut prin partitionarea poligonului a a ın ¸ ¸ concav atunci el nu se afl˘ ˆ interiorul acestuia.6..448 ˘ CAPITOLUL 18.io. !!! { . a ın P7 P6 P5 P1 P2 P3 P4 P1 P2 P3 P4 P7 P6 P5 a) b) 18. Dac˘ punctul este ˆ interiorul unui poligon convex ¸ a ın obtinut prin partitionarea poligonului concav atunci el se afl˘ ˆ interiorul acestuia. GEOMETRIE COMPUTATIONALA ¸ Poligonul concav se descompune ˆ poligoane convexe cu ajutorul diagoın nalelor interne ¸i se folose¸te un algoritm pentru poligoane convexe pentru fiecare s s poligon convex astfel obtinut.*..

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

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

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

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

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

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

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

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

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

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

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

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

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

nextToken().nval.462 ˘ CAPITOLUL 18. Determinarea a a a dreptunghiului de arie minim˘ care contine ˆ interiorul s˘u (inclusiv frontiera) a ¸ ın a toate punctele date se poate face observˆnd c˘ o latur˘ a sa contine o latur˘ a a a a ¸ a poligonului convex. } scanareGraham(). }//main }//class 18. Dintre aceste dreptunghiuri ¸ a se alege cel cu aria minim˘. x[k]=(int)st. Pentru fiecare latur˘ a poligonului convex se determin˘ drepa a tunghiul minim de acoperire care contine acea latur˘.1: Dreptunghi minim de acoperire Putem s˘ presupunem c˘ punctele formeaz˘ un poligon convex. GEOMETRIE COMPUTATIONALA ¸ st.nextToken(). o[k]=k. y[k]=(int)st. st.nval.7 Dreptunghi minim de acoperire a punctelor Se poate determina dreptunghiul minim de acoperire pentru ˆ a¸ur˘toarea ınf˘s a convex˘ (figura 18.1) pentru a prelucra mai putine puncte dar nu este obligatorie a ¸ aceast˘ strategie. a . a M P7 A P1 P2 N xmin D P6 P12 P10 P3 P11 P4 P5 C Q xmax ymin R ymax P8 P9 B Figura 18.

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

464

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸

Gr˘dinile roditoare ale B˘r˘ganului sufer˘ anual pierderi imense din a aa a cauza secetei. C˘ut˘torii de ap˘ au g˘sit n fˆntˆni din care doresc s˘ alimenteze n a a a a a a a gr˘dini. Fie Gi , Fi , i = 1, ..., n puncte ˆ plan reprezentˆnd puncte de alimentare a ın a ale gr˘dinilor ¸i respectiv punctele ˆ care se afl˘ fˆntˆnile. Pentru fiecare punct a s ın a a a se dau coordonatele ˆ ıntregi (x, y) ˆ plan. ın Pentru a economisi materiale, leg˘tura dintre o gr˘din˘ ¸i o fˆntˆn˘ se reaa a as a a a lizeaz˘ printr-o conduct˘ ˆ linie dreapt˘. Fiecare fˆntˆn˘ alimenteaz˘ o singur˘ a a ın a a a a a a gr˘din˘. Consiliul Judetean Galati pl˘te¸te investitia cu conditia ca lungimea toa a ¸ ¸ a s ¸ ¸ tal˘ a conductelor s˘ fie minim˘. a a a Fiecare unitate de conduct˘ cost˘ 100 lei noi (RON). a a Cerint˘ ¸a S˘ se determine m, costul minim total al conductelor ce leag˘ fiecare gr˘din˘ a a a a cu exact o fˆntˆn˘. a a a Date de intrare Fi¸ierul de intrare seceta.in va contine: s ¸ • Pe prima linie se afl˘ num˘rul natural n, reprezentˆnd num˘rul gr˘dinilor a a a a a ¸i al fˆntˆnilor. s a a • Pe urm˘toarele n linii se afl˘ perechi de numere ˆ a a ıntregi Gx Gy , separate printr-un spatiu, reprezentˆnd coordonatele punctelor de alimentare ale gr˘dinilor. ¸ a a • Pe urm˘toarele n linii se afl˘ perechi de numere ˆ a a ıntregi Fx Fy , separate printr-un spatiu, reprezentˆnd coordonatele punctelor fˆntˆnilor. ¸ a a a Date de ie¸ire s Fi¸ierul de ie¸ire seceta.out va contine: s s ¸ m − un num˘r natural reprezentˆnd partea ˆ a a ıntreag˘ a costului minim total a al conductelor. Restrictii ¸i preciz˘ri ¸ s a • 1 < n < 13 • 0 ≤ Gx, Gy, F x, F y ≤ 200 • Nu exist˘ trei puncte coliniare, indiferent dac˘ sunt gr˘dini sau fˆntˆni a a a a a • Orice linie din fi¸ierele de intrare ¸i ie¸ire se termin˘ prin marcajul de sfˆr¸it s s s a as de linie. Exemplu seceta.in 3 14 33 47 23 25 31 seceta.out 624 Explicatie ¸ Costul minim este [6.24264 * 100]=624 prin legarea perechilor: Gradini Fantani 14 23 33 31 47 25

Timp maxim de executie/test: 1 sec sub Windows ¸i 0.5 sec sub Linux. ¸ s

18.9. PROBLEME REZOLVATE Indicatii de rezolvare * ¸

465

Solutia oficial˘, lect. Ovidiu Dom¸a ¸ a s Num˘rul mic al punctelor permite generarea tuturor posibilit˘¸ilor de a conecta a at o gr˘din˘ cu o fˆntˆn˘ neconectat˘ la un moment dat. a a a a a a Pentru fiecare astfel de combinatie g˘sit˘ se calculeaz˘ suma distantelor ¸ a a a ¸ (Gi, F j), ˆ linie dreapta, folosind formula distantei dintre dou˘ puncte ˆ plan, ın ¸ a ın studiat˘ la geometrie. (d(A(x, y), B(z, t) = (x − z)2 + (y − t)2 ). a Acest˘ solutie implementat˘ corect asigur˘ 60 − 70 de puncte. a ¸ a a Pentru a obtine punctajul maxim se tine cont de urm˘toarele aspecte: ¸ a 1. Se construie¸te ˆ prealabil matricea distantelor d(i, j) cu semnificatia s ın ¸ ¸ distantei dintre gr˘dina i ¸i fˆntˆna j. Aceasta va reduce timpul de calcul la ¸ a s a a variantele cu peste 9 perechi. 2. Pentru a elimina cazuri care nu pot constitui solutii optime se folose¸te ¸ s proprietatea patrulaterului c˘ suma a doua laturi opuse (conditie care asigur˘ a ¸ a unicitatea conect˘rii unei singure fˆntˆni la o singur˘ gr˘din˘) este mai mic˘ decˆt a a a a a a a a suma diagonalelor. De aceea nu se vor lua ˆ considerare acele segmente care se ın intersecteaz˘. Conditia de intersectie a dou˘ segmente care au capetele ˆ punctele a ¸ ¸ a ın de coordonate A(a1, a2), B(b1, b2), C(c1, c2), D(d1, d2) este ca luˆnd segmentul a AB, punctele C ¸i D s˘ se afle de aceea¸i parte a segmentului AB ¸i respectiv s a s s pentru segmentul CD, punctele A ¸i B s˘ se afle de aceea¸i parte (se ˆ s a s ınlocuie¸te s ˆ ecuatia dreptei ce trece prin dou˘ puncte, studiat˘ ˆ clasa a 9-a). ın ¸ a a ın Observatie: Pentru cei interesati, problema are solutie ¸i la un nivel superior, ¸ ¸ ¸ s folosind algoritmul de determinare a unui flux maxim de cost minim. Variant˘ cu determinarea intesectiei segmentelor. a ¸ import java.io.*; // cu determinarea intesectiei segmentelor class Seceta1 // Java este "mai incet" decat Pascal si C/C++ { // test 9 ==> 2.23 sec static int nv=0; static int n; static int[] xg, yg, xf, yf, t, c; static int[] a; // permutare: a[i]=fantana asociata gradinii i static double costMin=200*1.42*12*100; static double[][] d; static PrintWriter out; static StreamTokenizer st; public static void main(String[] args) throws IOException { long t1,t2; t1=System.currentTimeMillis(); citire();

466

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ rezolvare(); afisare(); t2=System.currentTimeMillis(); System.out.println("Timp = "+(t2-t1)+" ms");

} static void citire() throws IOException { int k; st=new StreamTokenizer(new BufferedReader(new FileReader("seceta.in"))); st.nextToken(); n=(int)st.nval; xg=new int[n+1]; yg=new int[n+1]; xf=new int[n+1]; yf=new int[n+1]; a=new int[n+1]; d=new double[n+1][n+1]; for(k=1;k<=n;k++) { st.nextToken(); st.nextToken(); } for(k=1;k<=n;k++) { st.nextToken(); st.nextToken(); } } static void rezolvare() throws IOException { int i,j; int s; for(i=1;i<=n;i++) // gradina i for(j=1;j<=n;j++) // fantana j { s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]); d[i][j]=Math.sqrt(s); } f(1); // generez permutari }

xg[k]=(int)st.nval; yg[k]=(int)st.nval;

xf[k]=(int)st.nval; yf[k]=(int)st.nval;

18.9. PROBLEME REZOLVATE

467

static void f(int k) { boolean ok; int i,j; for(i=1;i<=n;i++) { ok=true; // k=1 ==> nu am in stanga ... for nu se executa ! for(j=1;j<k;j++) if(i==a[j]) {ok=false; break;} if(!ok) continue; for(j=1;j<k;j++) if(seIntersecteaza(xg[k],yg[k],xf[i], yf[i], xg[j],yg[j],xf[a[j]],yf[a[j]])) { ok=false; break; } if(!ok) continue; a[k]=i; if(k<n) f(k+1); else verificCostul(); } } static void verificCostul() { int i; double s=0; for(i=1;i<=n;i++) s=s+d[i][a[i]]; if(s<costMin) costMin=s; } // de ce parte a dreptei [(xa,ya);(xb,yb)] se afla (xp,yp) static int s(int xp,int yp,int xa,int ya,int xb,int yb) { double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya; if(s<-0.001) return -1; // in zona "negativa" else if(s>0.001) return 1; // in zona "pozitiva" else return 0; // pe dreapta suport } // testeaza daca segmentul[P1,P1] se intersecteaza cu [P3,P4] static boolean seIntersecteaza(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { double x,y;

468

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸ if((x1==x2)&&(x3==x4)) // ambele segmente verticale if(x1!=x3) return false; else if(intre(y1,y3,y4)||intre(y2,y3,y4)) return true; else return false; if((y1==y2)&&(y3==y4)) // ambele segmente orizontale if(y1!=y3) return false; else if(intre(x1,x3,x4)||intre(x2,x3,x4)) return true; else return false; if((y2-y1)*(x4-x3)==(y4-y3)*(x2-x1)) // au aceeasi panta (oblica) if((x2-x1)*(y3-y1)==(y2-y1)*(x3-x1)) // au aceeasi dreapta suport if(intre(x1,x3,x4)||intre(x2,x3,x4)) return true; else return false; else return false;// nu au aceeasi dreapta suport else // nu au aceeasi panta (macar unul este oblic) { x=(double)((x4-x3)*(x2-x1)*(y3-y1)x3*(y4-y3)*(x2-x1)+ x1*(y2-y1)*(x4-x3))/ ((y2-y1)*(x4-x3)-(y4-y3)*(x2-x1)); if(x2!=x1) y=y1+(y2-y1)*(x-x1)/(x2-x1); else y=y3+(y4-y3)*(x-x3)/(x4-x3); if(intre(x,x1,x2)&&intre(y,y1,y2)&&intre(x,x3,x4)&&intre(y,y3,y4)) return true; else return false; }

} static boolean intre(int c, int a, int b) // c este in [a,b] ? { int aux; if(a>b) {aux=a; a=b; b=aux;} if((a<=c)&&(c<=b)) return true; else return false; } static boolean intre(double c, int a, int b) // c este in [a,b] ? { int aux; if(a>b) {aux=a; a=b; b=aux;} if((a<=c)&&(c<=b)) return true; else return false; } static void afisare() throws IOException { int k;

18.9. PROBLEME REZOLVATE

469

out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.out"))); out.println((int)(costMin*100)); out.close(); } } Variant˘ cu cu determinarea pozitiei punctelor in semiplane ¸i mesaje pentru a s depanare. import java.io.*; // cu determinarea pozitiei punctelor in semiplane class Seceta2 // cu mesaje pentru depanare ! { static int nv=0; static int n; static int[] xg, yg, xf, yf, t, c; static int[] a; // permutare: a[i]=fantana asociata gradinii i static double costMin=200*1.42*12*100; static double[][] d; static PrintWriter out; static StreamTokenizer st; public static void main(String[] args) throws IOException { long t1,t2; t1=System.currentTimeMillis(); citire(); rezolvare(); afisare(); t2=System.currentTimeMillis(); System.out.println("Timp = "+(t2-t1)+" ms"); } static void citire() throws IOException { int k; st=new StreamTokenizer(new BufferedReader(new FileReader("seceta.in"))); st.nextToken(); n=(int)st.nval; xg=new int[n+1]; yg=new int[n+1]; xf=new int[n+1]; yf=new int[n+1]; a=new int[n+1]; d=new double[n+1][n+1]; for(k=1;k<=n;k++)

470 {

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸

st.nextToken(); xg[k]=(int)st.nval; st.nextToken(); yg[k]=(int)st.nval; } for(k=1;k<=n;k++) { st.nextToken(); xf[k]=(int)st.nval; st.nextToken(); yf[k]=(int)st.nval; } } static void rezolvare() throws IOException { int i,j; int s; for(i=1;i<=n;i++) // gradina i for(j=1;j<=n;j++) // fantana j { s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]); d[i][j]=Math.sqrt(s); } f(1); // generez permutari } static void f(int k) { boolean ok; int i,j; for(i=1;i<=n;i++) { ok=true; // k=1 ==> nu am in stanga ... for nu se executa ! for(j=1;j<k;j++) if(i==a[j]) {ok=false; break;} if(!ok) continue; for(j=1;j<k;j++) if((s(xg[k],yg[k],xg[j],yg[j],xf[a[j]],yf[a[j]])* s(xf[i],yf[i],xg[j],yg[j],xf[a[j]],yf[a[j]])<0)&& (s(xg[j], yg[j], xg[k],yg[k],xf[i],yf[i])* s(xf[a[j]],yf[a[j]],xg[k],yg[k],xf[i],yf[i])<0)) { afisv(k-1);// pe pozitia k(gradina) vreau sa pun i(fantana) System.out.print(i+" ");// pe pozitia j(gradina) e pus a[j](fantana) System.out.print(k+""+i+" "+j+""+a[j]); System.out.print(" ("+xg[k]+","+yg[k]+") "+" ("+xf[i]+","+yf[i]+") ");

18.9. PROBLEME REZOLVATE System.out.println(" ok=false; break; } if(!ok) continue; a[k]=i; if(k<n) f(k+1); else verificCostul(); } } static void verificCostul() { int i; double s=0; for(i=1;i<=n;i++) s=s+d[i][a[i]]; if(s<costMin) costMin=s; afisv(n); System.out.println(" "+s+" "+costMin+" "+(++nv)); } static void afisv(int nn) { int i; for(i=1;i<=nn;i++) System.out.print(a[i]); } // de ce parte a dreptei [(xa,ya);(xb,yb)] se afla (xp,yp) static int s(int xp,int yp,int xa,int ya,int xb,int yb) { double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya; if(s<-0.001) return -1; // in zona "negativa" else if(s>0.001) return 1; // in zona "pozitiva" else return 0; // pe dreapta suport } ("+xg[j]+","+yg[j]+") "+"

471 ("+xf[a[j]]+","+yf[a[j]]+") ");

static void afisare() throws IOException { int k; out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.out"))); out.println((int)(costMin*100)); out.close(); } }

472

˘ CAPITOLUL 18. GEOMETRIE COMPUTATIONALA ¸

Variant˘ cu cu determinarea pozitiei punctelor in semiplane, f˘r˘ mesaje a aa pentru depanare. import java.io.*; // cu determinarea pozitiei punctelor in semiplane class Seceta3 // Java este "mai incet" decat Pascal si C/C++ { // test 9 ==> 2.18 sec static int n; static int[] xg, yg, xf, yf, t, c; static int[] a; // permutare: a[i]=fantana asociata gradinii i static double costMin=200*1.42*12*100; static double[][] d; static PrintWriter out; static StreamTokenizer st; public static void main(String[] args) throws IOException { long t1,t2; t1=System.currentTimeMillis(); citire(); rezolvare(); afisare(); t2=System.currentTimeMillis(); System.out.println("Timp = "+(t2-t1)+" ms"); } static void citire() throws IOException { int k; st=new StreamTokenizer(new BufferedReader( new FileReader("seceta.in"))); st.nextToken(); n=(int)st.nval; xg=new int[n+1]; yg=new int[n+1]; xf=new int[n+1]; yf=new int[n+1]; a=new int[n+1]; d=new double[n+1][n+1]; for(k=1;k<=n;k++) { st.nextToken(); xg[k]=(int)st.nval; st.nextToken(); yg[k]=(int)st.nval; } for(k=1;k<=n;k++)

18.9. PROBLEME REZOLVATE { st.nextToken(); xf[k]=(int)st.nval; st.nextToken(); yf[k]=(int)st.nval; } } static void rezolvare() throws IOException { int i,j; int s; for(i=1;i<=n;i++) // gradina i for(j=1;j<=n;j++) // fantana j { s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]); d[i][j]=Math.sqrt(s); } f(1); // generez permutari }

473

static void f(int k) { boolean ok; int i,j; for(i=1;i<=n;i++) { ok=true; // k=1 ==> nu am in stanga ... for nu se executa ! for(j=1;j<k;j++) if(i==a[j]) {ok=false; break;} if(!ok) continue; for(j=1;j<k;j++) if((s(xg[k], yg[k], xg[j],yg[j],xf[a[j]],yf[a[j]])* s(xf[i], yf[i], xg[j],yg[j],xf[a[j]],yf[a[j]])<0)&& (s(xg[j], yg[j], xg[k],yg[k],xf[i], yf[i])* s(xf[a[j]],yf[a[j]],xg[k],yg[k],xf[i], yf[i])<0)) { ok=false; break; } if(!ok) continue; a[k]=i; if(k<n) f(k+1); else verificCostul(); } } static void verificCostul()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GEOMETRIE COMPUTATIONALA ¸ .492 ˘ CAPITOLUL 18.

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

TEORIA JOCURILOR .494 CAPITOLUL 19.

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

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

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

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

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

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

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

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

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

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

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

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

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

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->