Culegere de probleme de informaticã

- contine rezolvari si implementarile lor in limbajul Pascal Bogdan Batog Cãtãlin Drulã

Aceasta este versiunea electronica a cartii "Tehnici de programare - Laborator clasa a X-a". Cartea contine probleme de informatica si solutiile lor impreuna cu implementari in limbajul Pascal. Se adreseaza tuturor elevilor, dar si celor cu un interes mai special pentru informatica. Astfel, sunt tratate cateva subiecte mai avansate si sunt prezentate si probleme intalnite la diferite concursuri.

Cuprins
Prefaþã
Capitolul 1

Backtracking
Capitolul 2

Recursivitate
Capitolul 3

Backtracking recursiv
Capitolul 4

Analiza timpului de calcul necesar algoritmilor
Capitolul 5

Divide et Impera
Capitolul 6

Structuri de date
Capitolul 7

Tehnica Greedy
Capitolul 8

Programare dinamicã

Capitolul 9

Branch and Bound
Capitolul 10

Teoria grafurilor Bibliografie

Culegere de probleme de informaticã
Bogdan Batog Cãtãlin Drulã

Prefata
la varianta electronica

O scurta istorie a acestei carti ar fi cam asa. In vara anului 1998, domnul profesor Tudor Sorin ne-a propus sa scriem un “manual de laborator” pentru cursul de informatica din clasa a X-a. La vremea respectiva, eram amandoi in vacanta de vara dinaintea clasei a XII-a. Pentru ca ni s-a parut un proiect accesibil, si probabil pentru ca eram dornici sa ne afirmam, ne-am apucat cu entuziasm sa “scriem”. Scopul acestei carti a fost initial sa fie o culegere de probleme, care sa-i ajute atat pe elevi cat si pe profesori, ca sursa de exercitii suplimentare. Acesta a si devenit in mare continutul cartii. Contine probleme structurate in functie de tehnica de programare folosita in solutiile lor. Fiecare problema este urmata de solutie si de implementarea acesteia in limbajul Pascal. Ca o parenteza, limbajul acesta ne este drag amandorura, pentru ca ne aduce aminte cu un pic de nostalgie de olimpiadele de informatica la care am luat parte in liceu. In plus, il consideram si un limbaj foarte potrivit pentru invatarea programarii si a algoritmicii. La acest proiect initial al cartii, noi am adaugat cateva subiecte “avansate”, pentru elevii care au un interes mai special in algoritmica sau pentru cei care se pregatesc pentru olimpiadele de informatica. Culegerea a aparut in doua editii tiparite care intre timp s-au epuizat. Recent, ne-am hotarat sa continuam experimentul inceput de domnul profesor Tudor Sorin, si ii spun experiment, pentru ca dumnealui a dat dovada de multa deschidere incredintand acest proiect unor liceeni. Asadar, ne-am hotarat sa facem publica pe Internet aceasta carte cu speranta ca va fi de ajutor elevilor pasionati de algoritmi si programare. De aceea, va rugam sa ne trimteti prin e-mail comentariile voastre. 3 martie 2002 Bogdan Batog, bbbb@ss.pub.ro Catalin Drula, catalin.drula@utoronto.ca

Multumiri

Multumirile noastre merg in primul rand catre domnul profesor Tudor Sorin pentru ca ne-a dat sansa sa scriem aceasta carte. Si as adauga eu (Catalin): pentru ca a scris cartile dupa care am invatat primul limbaj de programare si primii algoritmi.

Mihai Boicu. Dan Grigoriu. .Celelalte multumiri sunt cele din prefata originala a manualului pe care le reproducem aici: domnilor profesori Cristian Francu si Catalin Francu (pentru pregatirea efectuata in particular). Daniela Oprescu si Rodica Pintea.

ºtiind cã trebuie respectate urmãtoarele reguli: q orice drapel are culoarea din mijloc galben sau verde. roºu. reprezentând cele trei culori de pe drapel ºi codificãm culorile prin numere: 1 2 3 4 5 6 – – – – – alb galben roºu verde albastru negru Folosim un vector auxiliar fol de tip boolean: fol[i]= TRUE. dacã nu a fost folositã. Avem la dispoziþie 6 culori: alb. Sã se precizeze toate drapelele tricolore care se pot proiecta. dacã culoarea i a fost folositã în drapel deja. Condiþiile care trebuie îndeplinite pentru aºezarea unei anumite culori c pe un nivel al stivei sunt: q fol[c]=FALSE. FALSE. cele trei culori de pe drapel sunt distincte. Folosim o stivã cu 3 nivele. atunci culoarea q . "roºu verde galben" Rezolvare. adicã sã nu fi folosit deja culoarea respectivã în drapel. albastru ºi negru. q Exemple: "alb galben roºu".Capitolul 1 Backtracking Problema 1 Enunþ. verde. galben. dacã nivelul stivei este 2 (adicã culoarea din mijloc).

:array [1. while k>0 do begin repeat ST[k]:=ST[k]+1 until (ST[k]>6) or Valid. :integer. function Valid:boolean.'verde'. var ST Fol k. k:=1. procedure Printsol.trebuie sã fie 2 sau 4 (galben sau verde). const Culoare:array [1.4]) end.'negru'). ST[k]:=0 end else begin . k:=k+1.. begin if Fol[ST[k]] then Valid:=false else if k<>2 then Valid:=true else Valid:=(ST[k] in [2. writeln end. program Drapel.3] of integer. ST[k]:=0.6] of boolean. var i:integer. if ST[k]<=6 then if k=3 then Printsol else begin Fol[ST[k]]:=true.6] of string[10]=('alb'.. i :array [1. 'rosu'. begin for i:=1 to 6 do Fol[i]:=false.'galben'. 'albastru'. begin for i:=1 to 3 do write(Culoare[ST[i]].' ')..

0). readln(n). cu ordinea termenilor schimbatã. S. if k>0 then Fol[ST[k]]:=false end end end. readln(p). . write('P= ').. var n. forþãm ordinea crescãtoare (nu strict crescãtoare!) a termenilor din sumã. begin write('N= ').k:=k-1. program Descompuneri. begin for i:=1 to p do write(ST[i]. i ST :integer. prin iniþializarea unui nivel din stivã cu valoarea nivelului anterior. Condiþia de validitate devine: S+ST[k]≤N. Problema 2 Enunþ. :array [0. k:=1. p. fillchar(ST. Sã se descompunã un numãr natural N. k. în toate modurile posibile. Variabila S reþine suma primelor k nivele pentru a putea testa validitatea elementului aºezat pe poziþia curentã fãrã a mai face o parcurgere suplimentarã a stivei. Rezolvare. Procedure Printsol. Folosim o stivã cu P nivele. writeln end. unde fiecare nivel ia valoarea unui termen din sumã. var i:integer. S:=0.sizeof(ST).1000] of integer. Pentru a evita afiºarea descompunerilor identice. ca sumã de P numere naturale (P≤N).' '). while k>0 do begin ST[k]:=ST[k]+1.

Pentru rezolvarea problemei folosim o stivã cu C nivele. unde nivelele 1.C. . unde: MAX=p. la urcarea ºi coborârea pe nivelul L.L conþin indicii femeilor din delegaþia curentã.L (femeile) ºi N pentru nivelele L+1. S:=S-ST[k] end end end. Motivul pentru care st[i] se iniþializeazã cu st[i-1] este evitarea duplicãrii delegaþiilor. k:=k+1. Problema 3 Enunþ.. dintre care L femei. prin forþarea ordinii strict crescãtoare în fiecare delegaþie. trebuie formatã o delegaþie de C persoane. st[l] poate lua valori între p ºi n.if S+ST[k]<=N then if (k=p) and (S+ST[k]=N) then Printsol else begin S:=S+ST[k]. ºi bãrbaþii de la P+1 la N. De aceea. pentru i<=l... MAX=n. respectiv P. ST[k]:=ST[k-1]-1 end else begin k:=k-1.. pentru i>l. Limita pânã la care se pot majora elementele de pe un nivel este P pentru nivelele 1. Vom nota femeile cu numerele de la 1 la P. ºi nivelele L+1. dintre care P femei. Dintr-un grup de N persoane.C indicii bãrbaþilor. st[i] poate lua valori între st[i-1] ºi MAX. variabila MAX devine N. Sã se precizeze toate delegaþiile care se pot forma. Rezolvare.

procedure Printsol. pentru cã este primul dintre bãrbaþi ºi bãrbaþii au indicii între P+1 ºi N. var st MAX. if k=l then MAX:=P end end end. . ci cu P. if st[k]<=MAX then if k=C then Printsol else begin k:=k+1. N. writeln end. var i:integer.P.C. C. MAX:=N end end else begin k:=k-1. if k<>L+1 then st[k]:=st[k-1] else begin st[k]:=P. begin write('N. begin for i:=1 to C do write(st[i]. P. P. program Delegatie. C. k. st[1]:=0. L: '). MAX:=P.st[l] nu se iniþializeazã cu st[l-1].100] of integer. L :array [1.L). while k>0 do begin st[k]:=st[k]+1.' '). :integer. readln(N. k:=1..

Ce obiecte trebuie sã aleagã persoana pentru a-ºi maximiza câºtigul ºi care este acesta? Se va avea în vedere ºi faptul ca pentru acelaºi câºtig persoana sã transporte o greutate mai micã. Pus :array [1. greutatea acestor obiecte.N). Pe fiecare din urmãtoarele N linii se aflã douã numere întregi reprezentând greutatea ºi câºtigul asociat obiectului respectiv.S. . var i. program Rucsac. begin assign(f. greutatea ºi obiectele) reþinem pe timpul execuþiei programului cea mai bunã soluþie gãsitã.. CG este câºtigul obþinut în urma transportãrii obiectelor aºezate în stivã pânã la nivelul curent ºi S.'input. Gmax ºi STmax (câºtigul. adicã sã nu fi aºezat deja obiectul respectiv în stivã ºi 2.Problema 4 Enunþ.Cmax.Gmax.STmax :array [1.k.ST. c..txt. Pentru fiecare obiect se cunoaºte greutatea (mai micã decât capacitatea rucsacului) ºi câºtigul obþinut în urma transportului sãu. numãrul de obiecte ºi capacitatea rucsacului. Condiþiile care trebuie îndeplinite pentru aºezarea unui obiect i pe nivelul k al stivei sunt: 1. S+gr[ST[k]]<=G. adicã greutatea obiectelor sã fie mai micã sau cel mult egalã cu greutatea maximã care poate fi transportatã cu rucsacul. reset(f).100] of boolean. În variabilele Cmax.gr. Rezolvare.txt'). var f :text.G. procedure Citire. i :integer. readln(f. Pus[i]=FALSE.100] of integer. Pe primele douã linii ale fiºierului sunt N ºi G. Notã: Rezolvarea optimã a acestei probleme poate fi gãsitã în manualul “Tehnici de programare” de Tudor Sorin la capitolul “Programare dinamicã”.n. Datele de intrare se vor citi din fiºierul input.CG. O persoanã are la dispoziþie un rucsac cu o capacitate de G unitãþi de greutate ºi intenþioneazã sã efectueze un transport în urma cãruia sã obþinã un câºtig.NOb :integer. Persoanei i se pun la dispoziþie N obiecte. În vectorii gr ºi c avem greutatea ºi câºtigul obþinut în urma transportãrii fiecãrui obiect.

NOb:=k. S:=0. ST[k]:=0. CG:=0. Valid:=true end else Valid:=false end. begin Citire. ST[k]:=0 end else begin k:=k-1. for i:=1 to n do Pus[i]:=false. Pus[ST[k]]:=true.c[i]). end. k:=1. if k>0 then . if ST[k]<=n then begin if CG>Cmax then begin Cmax:=CG. while k>0 do begin repeat ST[k]:=ST[k]+1 until (ST[k]>n) or Valid. for i:=1 to n do readln(f.gr[i]. Gmax:=S. close(f) end.G). for i:=1 to k do STmax[i]:=ST[i]. begin if Pus[ST[k]] then Valid:=false else if S+gr[ST[k]]<=G then begin S:=S+gr[ST[k]].readln(f. k:=k+1. CG:=CG+c[ST[k]]. function Valid:boolean. Cmax:=0.

' '). Rezolvare. writeln('Cistigul maxim: '. Rutina de backtracking este similarã cu cea din problema 2. Pus[ST[k]]:=false end end end. for i:=1 to NOb do write(STmax[i]. var i:integer.begin S:=S-gr[ST[k]]. S.. Function Prim( X : integer):boolean. Problema 5 Enunþ. while i*i<=X do begin if X mod i=0 then begin Prim:=false. writeln end. Numar n. Program SumaPrime. write('Obiectele transportate: '). i:=2. Fiind dat un numãr natural N. :integer.Cmax). cu excepþia faptului cã termenii pot lua valori din Numar[1. writeln('Greutate transportata: '. P. K. acestea vor fi memorate în vectorul Numar.Gmax). CG:=CG-c[ST[k]]. i:=X ..P] ºi cã descompunerea nu trebuie sã aibã un numãr fix de termeni. var ST. se cere sã se afiºeze toate descom-punerile sale ca sumã de numere prime. begin Prim:=true. vom calcula mai întâi toate numerele prime mai mici ca N.1000] of integer. Pentru eficienþã. i :array[0.

P:=0.sizeof(ST). writeln end. Numar[P]:=i end. S:=0. var i:integer.' '). S:=S-Numar[St[k]] end end end. i:=i+1 end end. procedure Printsol. begin fillchar(ST. fillchar(Numar. if (ST[k]<=P) and (S+Numar[St[k]]<=N) then if S+Numar[St[k]]=N then Printsol else begin S:=S+Numar[ST[k]]. for i:=2 to N do if Prim(i) then begin inc(P).end.sizeof(Numar). while k>0 do begin ST[k]:=ST[k]+1. begin for i:=1 to k do write(Numar[ST[i]].0). write('N='). k:=1. readln(N). k:=k+1. Problema 6 . ST[k]:=ST[k-1]-1 end else begin k:=k-1.0).

Fiºierul de intrare input. dacã pãtrãþelul respectiv e ars an1 an2 an3 . vezi ºi problema 4). precum ºi poziþia regelui sunt considerate "nearse". dacã nu este ars Vom face o "bordare" a tablei de ºah cu douã rânduri de pãtrãþele "arse". poziþiile lor fiind cunoscute. Poziþia iniþialã a calului..txt trebuie sã aibã forma: N // pe prima linie. Calul nu poate cãlca pe câmpuri "arse". // aij = 0...linia ºi coloana poziþiei curente. Sã se afle dacã existã o succesiune de mutãri permise (cu restricþiile de mai sus).numãrul de linii (coloane) al tablei lCal cCal // pe urmãtoarele douã linii. mut . tabla de ºah . Unele câmpuri ale tablei sunt "arse". n . a1n // pe urmãtoarele n linii. Rezolvare.c . De exemplu dacã tabla este 3x3 ºi notãm cu 0 pãtrãþelele arse ºi cu 1 pe cele nearse: 0000000 0000000 0011100 0011100 0011100 0000000 0000000 Aceastã bordare este utilã pentru cã nu trebuie sã mai verificãm dacã coordonatele rezultate în urma efectuãrii unei mutãri sunt pe tabla de ºah. poziþia iniþialã a lRege cRege // calului ºi a regelui a11 a12 a13 . Pentru început vom citi datele de intrare cu procedura Citire. iar orice miºcare a calului face ca respectivul câmp sã devinã "ars". ..mutarea fãcutã din aceastã poziþie. prin care calul sã poatã ajunge la rege ºi sã revinã la poziþia iniþialã. ann // = 1. Vectorul Mutari (constant) conþine cele 8 mutãri pe care le poate efectua un cal (modificãrile coordonatelor calului prin mutarea respectivã.. ("Attila ºi regele") Un cal ºi un rege se aflã pe o tablã de ºah. Vom folosi o stivã triplã cu elemente de tip pozitie: l.Enunþ.

Astfel, presupunând cã l ºi c sunt coordonatele rezultate în urma unei mutãri, condiþia de validitate a acelei mutãri va fi: if a[l,c]=1 then ... , în loc de, if (l>=1) and (l<=n) and (c>=1) and (c<=n) and (a[l,c]=1) then … Condiþia de validitate a unei mutãri este ca poziþia rezultatã în urma mutãrii sã fie pe un pãtrat "nears" ºi diferitã de poziþia iniþialã a calului dacã acesta nu a ajuns încã la rege. Dacã poziþia rezultatã în urma mutãrii este poziþia iniþialã a calului ºi acesta a ajuns la rege atunci programul se terminã cu afiºarea soluþiei (variabila Terminat devine TRUE). Dacã mutarea este corectã, se "arde" câmpul respectiv ºi se verificã dacã poziþia rezultatã în urma mutãrii este poziþia regelui pentru da valoarea TRUE variabilei Rege (care este TRUE dacã s-a ajuns la rege ºi FALSE, altfel). La trecerea pe un nivel superior al stivei, se iniþializeazã acest nivel cu poziþia curentã. La trecerea pe un nivel inferior al stivei, se marcheazã poziþia respectivã ca "nearsã" ºi, dacã poziþia de pe care se coboarã în stivã era poziþia regelui, variabila Rege devine FALSE.

program attila; const Mutari:array [1..8,1..2] of integer= ((-2,1),(-1,2),(1,2),(2,1),(2,-1),(1,-2),(-1,-2),(-2,-1)); type pozitie=record l,c,mut:integer; end; var N, lCal, cCal, lRege, cRege, k, nlin, ncol, i T :array [-1..22,-1..22] of integer; ST :array [1..400] of pozitie; Rege, Terminat :boolean; procedure citire; var f:text; i,j:integer; begin assign(f,'input.txt'); reset(f); readln(f,N); readln(f,lCal,cCal); readln(f,lRege,cRege); for i:=1 to n do begin for j:=1 to n do :integer;

read(f,T[i,j]); readln(f); end; close(f); end; procedure Printsol; var i:integer; begin for i:=1 to k do writeln(ST[i].l,' ',ST[i].c); writeln(lCal,' ',cCal) end; function Valid:boolean; begin nlin:=ST[k].l+Mutari[ST[k].mut,1]; ncol:=ST[k].c+Mutari[ST[k].mut,2]; if (nlin=lCal) and (ncol=cCal) then if Rege then begin Terminat:=true; Valid:=true; end else Valid:=false else if T[nlin,ncol]=1 then Valid:=true else Valid:=false end; begin Citire; for i:=-1 to N+2 do begin T[-1,i]:=0; T[0,i]:=0; T[n+1,i]:=0; T[n+2,i]:=0; T[i,-1]:=0; T[i,0]:=0; T[i,n+1]:=0; T[i,n+2]:=0 end; k:=1; ST[1].mut:=0; ST[1].l:=lcal; ST[1].c:=ccal; Rege:=false; Terminat:=false; while (k>0) and (not Terminat) do begin repeat ST[k].mut:=ST[k].mut+1 until (ST[k].mut>8) or Valid; if ST[k].mut<=8 then

if Terminat then Printsol else begin k:=k+1; ST[k].mut:=0; ST[k].l:=nlin; ST[k].c:=ncol; T[nlin,ncol]:=0; if (nlin=lRege) and (ncol=cRege) then Rege:=true end else begin T[ST[k].l,ST[k].c]:=1; if (ST[k].l=lRege) and (ST[k].c=cRege) then Rege:=false; k:=k-1 end end; if k=0 then writeln('Nu exista solutie.') end.

Problema 7
Enunþ. (Proprietãþi numerice) Numãrul 123456789 are câteva caracteristici amuzante. Astfel, înmulþit cu 2,4,7 sau 8 dã ca rezultat tot un numãr de nouã cifre distincte (în afarã de 0): 123456789⋅ 2 = 246913578 ; 123456789⋅ 4 = 493827156 123456789⋅ 7 = 864197523 ; 123456789⋅ 8 = 987654312. Aceastã proprietate nu funcþioneazã pentru numerele 3,6 sau 9. Existã totuºi multe numere de nouã cifre distincte (fãrã 0) care înmulþite cu 3 au aceeaºi proprietate. Sã se listeze toate aceste numere în care ultima cifrã este 9. Concurs Tuymaada 1995 Rezolvare. În stivã se vor reþine cifrele numãrului cãutat. Pentru cã ultima cifrã este întotdeauna 9, stiva va avea numai 8 poziþii. Condiþia de "valid" este ca cifra selectatã sã nu fi fost folositã anterior (de fapt se genereazã toate permutãrile cifrelor 1..8). Datoritã faptului cã la fiecare nivel în stivã se alege cea mai micã cifrã nefolositã care nu a fost încã încercatã, numerele vor fi generate în ordine crescãtoare. Astfel, dacã înmulþind cu 3 numãrul obþinut la pasul curent se va obþine un numãr pe 10 cifre vom ºti cã ºi urmãtoarele numere vor da un produs pe 10 cifre. Rezultã cã procesul se opreºte când întâlneºte numãrul 341256789 (cel mai mic numãr cu cifrele distincte, mai mare decât 333333333).

{ rezultatului sunt distincte i:=0. while k>0 do if k=9 then begin e:=0. for f:=1 to length(s) do if not (s[f] in t) then begin i:=i+1. t:=t+[s[f]] end.f e t s :array[0. if ST[k]>8 then begin ST[k]:=0.s).sizeof(U).10] of integer. :double.sizeof(ST). fillchar(ST. writeln end.i. if length(s)=10 then exit.0). :set of char.program Numeric. repeat ST[k]:=ST[k]+1 until (U[ST[k]]=0) or (ST[k]>8). U[9]:=1. :string.0). k:=k-1 end else begin U[ST[k]]:=0.U K. ST[9]:=9. if i=9 then begin for f:=1 to 9 do write(ST[f]). { ºi se verificã dacã cifrele } t:=[]. k:=k-1 } . begin k:=1. { se înmulþeºte numãrul cu 3 } str(e:0:0.. :integer. { se valideazã o posibilã soluþie } for f:=1 to 9 do e:=e*10+ST[f]. e:=e*3. var ST. fillchar(U.

k:=k+1 end end end.. astfel încât suma numerelor aflate pe muchiile unei feþe sã fie aceeaºi pentru toate feþele. 1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 unde mi este valoarea din mulþimea {2.. Sã se dispunã pe cele 12 muchii ale unui cub toate numerele de la 1 la 12... Problema 8 Enunþ. O soluþie corectã este cea de mai jos. iar muchia 1 conþine valoarea 1 în toate soluþiile.. ilustratã ºi în figurã: .12...12} plasatã pe muchia i. i=2.end else begin U[ST[k]]:=1. Considerãm muchiile numerotate ca în figurã: Ieºirea va fi într-un fiºier text având numele solcub.txt care va conþine câte o soluþie pe fiecare linie sub forma: 1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 .

. dar m1+m2+. .. F1 = F2 = . dupã ce aºezãm m4 ºi m5 se deduce m9=26-m4-m5. 1996 Rezolvare.F6 suma numerelor de pe muchiile feþelor cubului.+m12).. m1=1 întotdeauna. m11=26-m5-m7-m3 ºi m12=26m9-m10-m11.. Dacã se utiliza soluþia banalã.. . Deci numai 6 muchii variazã independent una de celelalte: m2. Dupã ce aºezãm m2 ºi m3. la aceastã problemã a fost impus un timp de execuþie de maxim 10 secunde. timpul de calcul fiind O( )≈ 55000.m7. La fel. .. celelalte valori deducându-se din acestea 6. Observãm cã este suficient sã folosim doar 6 nivele ale stivei. timpul de calcul era O(11!)≈ 39 milioane ºi timpul de execuþie era de aproximativ 30 de secunde... În concurs. = F6 = F F1+F2+F3+F4+F5+F6 = 2(m1+m2+. adicã sub o secundã.+m12).1 12 9 6 8 5 7 4 11 3 2 10 Baraj Sibiu. Notãm cu F1.. pentru cã fiecare muchie apare în douã feþe ale cubului. adicã permutãri de 11.+m12 = 1+2+…+12 =78 => 6*F=2*78=156 => F=26.... se deduc m10=26-m2-m4. Programul face aranjamente de 11 luate câte 6.. Deci. Deci suma muchiilor de pe fiecare faþã trebuie sã fie 26. Astfel. Analog.. m8 se deduce ca fiind 26-m1-m2-m3. Vectorul auxiliar Pus este folosit pentru a ºti ce numere au fost deja aºezate în stivã. 6*F=2*(m1+m2+.

program cub_magic. :integer. 5:begin Pus[ST[5]]:=true. ST[9]:=26-ST[5]-ST[4]-1. if not Pus[ST[8]] then begin Pus[ST[8]]:=true.. 4:begin Pus[ST[4]]:=true. Valid:=false end end. begin if Pus[ST[k]] then Valid:=false else case k of 2:begin Pus[ST[2]]:=true. 3:begin Pus[ST[3]]:=true. function Valid:boolean. if not Pus[ST[9]] then begin Pus[ST[9]]:=true.26] of boolean. Valid:=true end else begin Pus[ST[3]]:=false.12] of integer. 6:begin Pus[ST[6]]:=true. :array [1. Valid:=true end else begin Pus[ST[5]]:=false. i :array [2. Valid:=true end.. Valid:=false end end. . var ST Pus k. Valid:=true end. ST[8]:=26-ST[2]-ST[3]-1.

Valid:=false end. Valid:=true end else begin Pus[ST[7]]:=false. if (ST[10]>0) and (not Pus[ST[10]]) then begin Pus[ST[10]]:=true. for i:=2 to 12 do write(ST[i].ST[10]:=26-ST[4]-ST[6]-ST[2]. while k>1 do begin repeat ST[k]:=ST[k]+1 . end. end. if (ST[11]>0) and (ST[12]>0) and (ST[11]<>ST[12]) and (not Pus[ST[11]]) and (not Pus[ST[12]]) and (ST[9]+ST[10]+ST[11]+ST[12]=26) then begin Pus[ST[7]]:=false. Valid:=false end. ST[11]:=26-ST[5]-ST[7]-ST[3]. ST[k]:=0. end. for i:=2 to 26 do Pus[i]:=false. ST[12]:=26-ST[6]-ST[7]-ST[8].' '). end. Pus[1]:=true. procedure Printsol. Valid:=true end else begin Pus[ST[6]]:=false. 7:begin Pus[ST[7]]:=true. begin k:=2. begin write('1 '). writeln end.

4:Pus[ST[4]]:=false. care are valoarea 1. deci avem patru cazuri.j) (i. 6:begin Pus[ST[6]]:=false.n o matrice binarã. Fie Am. Pus[ST[10]]:=false end end end end end.j+1) (i.n.m. case k of 2:Pus[ST[2]]:=false. se subînþelege cã dintr-o cãsuþã se poate merge doar în cele patru vecine cu ea: (i-1. Pus[ST[9]]:=false end. ST[k]:=0 end else begin k:=k-1. Sã se gãseascã toate ieºirile din matrice mergând numai pe elementele cu valoarea 1. Problema 9 Enunþ. 3:begin Pus[ST[3]]:=false. . Rezolvare. Se cunosc coordonatele (i.j) (i+1.j-1) Condiþia pentru o soluþie o reprezintã verificarea atingerii marginilor matricei. 5:begin Pus[ST[5]]:=false.until (ST[k]>12) or Valid. pentru fiecare din cele patru margini: i=1 sau i=M sau j=1 sau j=N. Pus[ST[8]]:=false end.. j aparþinând 1. Vom proceda ca la problema 4: reþinem un vector cu deplasãrile relative ale miºcãrilor posibile.. if ST[k]<=12 then if k=7 then Printsol else begin k:=k+1.j) ale unui element... i aparþinând 1.

N:'). :array[0.pj :integer. begin x:=oi. dec(k) end. var x. for i:=1 to M do .0.2] of integer= ( (0. var pi.k. writeln.. (0.x. begin pi:=i+Muta[ST[k]. const Muta : array[0.100] of integer. for f:=1 to k do begin write('('.100.1000] of integer.N).1.oj A ST :integer.y. Function Valid:boolean.. readln(M. y:=y+Muta[ST[f]. (1. Procedure Sol. poziþia respectivã este marcatã cu 1. x:=x+Muta[ST[f].oi.'.. (-1.1].-1) ). q Poziþiile deja parcurse sunt marcate cu 2 pentru a nu fi considerate încã o datã în soluþie.2] end. var N. y:=oj.0).j.0).0)..i.f :integer.4.') '). :array[0. Valid:=(pi>0) and (pj>0) and (pi<=M) and (pj<=N) and (A[pi..y.1]. begin write('M.pj]=1) end.M. (0.2]. Program Matrice.1).O mutare este consideratã validã dacã: q este în matrice.'. pj:=j+Muta[ST[k].

Sã se afiºeze toate ºirurile de N paranteze care se închid corect. ((())) . write('i. este un ºir în care fiecãrei paranteze deschise îi corespunde o parantezã închisã la o poziþie ulterioarã în ºir. readln(A[i.j]:=2.j. )()()( . A[i. if ST[k]=5 then begin ST[k]:=0. i:=i+Muta[ST[k]. Un ºir de N paranteze care se închid corect.']=').2].0). fillchar(ST. ()()() Exemple de ºiruri incorecte: ())()) . dec(k) end else begin A[i.i.sizeof(ST). repeat inc(ST[k]) until (ST[k]>4) or valid. oj:=j. Se dã un numãr natural par N.'. j:=j+Muta[ST[k]. j:=j-Muta[ST[k]. (((()) .j]=2 then A[i.j]) end. Problema 10 Enunþ. i:=i-Muta[ST[k]. if A[i.'. k:=1.1]. Exemple de ºiruri corecte: (())() .1].j).for j:=1 to N do begin write('A['. Rezolvare.j]:=-1. while k>0 do begin if (i=1) or (j=1) or (i=M) or (j=N) then Sol.2]. readln(i. inc(k) end end end.j]:=1.j:'). oi:=i.

Deducem din propoziþia de mai sus o primã condiþie necesarã pentru ca un ºir de paranteze sã se închidã corect ºi anume cã nu trebuie sã deschidem mai mult de N/2 paranteze. Dupã cum se vede însã din aceste exemple (incorecte), aceastã condiþie nu este suficientã: (()))( ; )))((( ; )()()( . A doua condiþie este sã nu închidem mai multe paranteze decât am deschis. Pentru formalizarea condiþiilor notãm cu Nd(k) ºi Ni(k) numãrul de paranteze deschise, respectiv închise, pânã la poziþia k a ºirului, inclusiv. Cele douã condiþii sunt:

1. Nd(k) ≤ N/2, ∀ 1 ≤ k ≤ n 2. Nd(k) ≥ Ni(k), ∀ 1 ≤ k ≤ n
Pentru implementare folosim o stivã cu N nivele: st[i] = 1, dacã paranteza de pe poziþia i este deschisã 2, dacã paranteza de pe poziþia i este închisã. În variabilele Nd ºi Ni avem numãrul de paranteze de fiecare fel aºezate în ºir pânã la poziþia curentã, k. Pentru a aºeza o parantezã deschisã pe poziþia curentã trebuie sã fi deschis mai puþin de N/2 paranteze, adicã Nd<N/2. Pentru a aºeza o parantezã închisã pe poziþia curentã trebuie sã mai avem o parantezã pe care sã o închidem, adicã Nd>Ni. La urcarea ºi coborârea în stivã se modificã Nd sau Ni în funcþie de tipul parantezei de pe nivelul respectiv.

program Paranteze; var ST k, N, Nd, Ni :array [0..100] of integer; :integer;

procedure Printsol; var i:integer; begin for i:=1 to N do if ST[i]=1 then write('(') else write(')'); writeln end;

function valid:boolean; begin if ST[k]=1 then if Nd<N div 2 then valid:=true else valid:=false else if Nd>Ni then valid:=true else valid:=false end; begin write('N= '); readln(N); Nd:=0; Ni:=0; k:=1; ST[k]:=0; while k>0 do begin repeat ST[k]:=ST[k]+1 until (ST[k]>2) or valid; if ST[k]<=2 then if k=N then Printsol else begin if ST[k]=1 then Nd:=Nd+1 else Ni:=Ni+1; k:=k+1; ST[k]:=0 end else begin k:=k-1; if ST[k]=1 then Nd:=Nd-1 else Ni:=Ni-1 end end end.

Problema 11
Enunþ. Se considerã o mulþime de N elemente ºi un numãr natural K nenul. Sã se calculeze câte submulþimi cu k elemente satisfac pe rând condiþiile de mai jos ºi sã se afiºeze aceste submulþimi:

a. conþin p obiecte date; b. nu conþin nici unul din q obiecte date c. conþin exact un obiect dat, dar nu conþin un altul d. conþin exact un obiect din p obiecte date e. conþin cel puþin un obiect din p obiecte date f. conþin r obiecte din p obiecte date, dar nu conþin alte q obiecte date.
Rezolvare. Pentru calcularea ºi generarea soluþiilor se pot folosi doar combinãrile. Pentru simplitatea programului se poate considera, fãrã a restrânge generalitatea, cã cele p, respectiv q obiecte date au numerele de ordine 1,2,3, .. ,p, respectiv p+1,p+2, .. , p+q (în cazul c) avem p=q=1). Fiecãrui nivel al stivei îi este asociatã o mulþime de elemente care pot ocupa acel loc în soluþie. Pentru a generaliza, vom considera cã fiecare mulþime este de forma {Li,Li+1,Li+2,...,LS}. Anume: pentru fiecare nivel i al stivei vom reþine doi vectori Li[i] ºi Ls[i] care indicã limita inferioarã, respectiv superioarã a elementului ce poate fi ales pe nivelul i (deci pentru Li[i]=2 ºi Ls[i]=5, pe nivelul i vom putea avea doar elementele 2,3,4,5 ). Acum sã vedem cu ce valori vom iniþializa vectorii Li ºi Ls pentru fiecare caz: a) practic trebuie sã alegem întotdeauna elementele 1,2,3, .. ,p ºi apoi încã oricare k-p din cele rãmase (p+1,p+2, .. , N). Pentru a forþa alegerea primelor p elemente vom considera Li[i]=Ls [i]=i. Deci vectorii vor arãta astfel: I Li Ls 1 1 1 2 2 2 3 3 3 … … … p p p p+1 p+1 N p+2 p+1 N … … … N p+1 N

b) aici considerãm p=0: deci obiectele care nu trebuie selectate vor fi 1,2,3 ..., q. Avem Li[i]=Q+1 ºi Ls[i]=N pentru oricare i. c) p=1 ºi q=1 Li[1]=1 ºi Ls[1]=1 obiectul 1 trebuie ales mereu; Li[i]=3 ºi Ls[i]=N, pentru i>1 cel cu numãrul 2 trebuie evitat.

program submultimi. Li[i]=p+q+1 ºi Ls[i]=N. e) Li[1]=1 ºi Ls[1]=p un obiect între 1 ºi p. end end. Ls.C.. 'D':begin end.K. Q.P. u N. P.E sau F):'). K. Li[i]=p+1 ºi Ls[i]=N. f sol ch :array[0. Ls[i]:=N 'C':begin for i:= 2 to K do begin Li[i]:=3.1000] of integer. 'B': for i:= 1 to K do begin Li[i]:=Q+1. f) Li[i]=1 ºi Ls[i]=p. Programul va mai folosi ºi vectorul u care va indica dacã elementul i a fost sau nu utilizat în soluþie pânã la pasul curent. Case UpCase(ch) of 'A':begin for i:= 1 to P do begin Li[i]:=i. Ls[i]:=i for i:=P+1 to K do begin Li[i]:=P+1. pentru R<i≤K restul trebuie sã nu fie printre cele p sau q. R. nivelul curent în stivã va fi memorat în variabila i. Li.Q). care. Pentru cã variabila k face parte din datele de intrare. Ls[1]:=1 end. Ls[i]:=N Li[1]:=1. Deci vom scrie o singurã rutinã backtracking. i. readln(N. var ST. va furniza rãspunsul la oricare din cele ºase cerinþe. Ls[i]:=N end.B. în funcþie de iniþializãrile vectorilor Li ºi Ls.P.D.d) Li[1]=1 ºi Ls[1]=p alegem un obiect între 1 ºi p. write('subpunctul (A. pentru i>1 restul pot lua orice valori.K. :integer. begin write('N. . pentru 1≤i≤R pe primele r poziþii alegem obiecte din primele p.Q:'). pentru i>1 ºi restul între p+1 ºi N. :char. readln(ch). end. :longint. Li[i]=1 ºi Ls[i]=N.

i:=i-1 end else begin U[ST[i]]:=1. Li[1]:=1.Ls[i]:=N end end end. Ls[1]:=p end. Li[1]:=1.sizeof(ST). if ST[i-1]<Li[i] then ST[i]:=Li[i]-1 else ST[i]:=ST[i-1]-1 end end.0). Ls[i]:=N end. Ls[1]:=p end. Ls[i]:=N end. fillchar(ST. writeln. if ST[i]>LS[i] then begin ST[i]:=0. for i:= 1 to R do begin Li[i]:=i. 'F':begin write('R='). readln(R). fillchar(u. i:=i+1.for i:= 2 to K do begin Li[i]:=P+1. Ls[i]:=P end.0). repeat ST[i]:=ST[i]+1 until (ST[i]>LS[i]) or (U[ST[i]]=0). for i:=R+1 to K do begin Li[i]:=P+Q+1. ST[1]:=Li[1]-1. . i:=1. for f:=1 to K do write(ST[f]. sol:=0. 'E':begin for i:= 2 to K do begin Li[i]:=1.sizeof(u). while i>0 do if i=k+1 then begin sol:=sol+1. i:=i-1 end else begin U[ST[i]]:=0.' ').

Condiþiile care trebuie îndeplinite pentru aºezarea unui numãr c pe nivelul k al stivei sunt: q Pus[c]=FALSE..st[k-1]. existã 1≤ j≤ i astfel încât |V(i)-V(j)|=1. .. function Valid:boolean. Sã se genereze toate permutãrile de N cu proprietatea cã oricare ar fi 2≤ i≤ N. var ST k..20] of integer. Problema 12 Enunþ. 4321.sol) end. :integer. 1234 Rezolvare. :array [1.writeln('Numar solutii= '. Exemplu: pentru N=4. N Pus :array [1. if not Pus[ST[k]] then for i:=1 to k-1 do if abs(ST[k]-ST[i])=1 then tmp:=true. permutãrile cu proprietatea de mai sus sunt: 2134. begin if k=1 then Valid:=true else begin tmp:=false. q program Permutari. adicã sã nu fi aºezat deja numãrul c în permutare.20] of boolean. dacã numãrul i a fost aºezat deja în permutare.respectiv st[1].. var tmp:boolean. i. FALSE. Pentru generarea permutãrilor folosim o stivã cu N nivele ºi un vector auxiliar: Pus[i]=TRUE. 3421. cel puþin unul din numerele aºezate pe poziþiile 1. 3214. sã aibã diferenþa absolutã faþã de c egalã cu 1. 3241.k-1 . altfel. 2314.

aºezând pe primele N poziþii punctele . Pus[ST[k]]:=false end end end. begin for i:=1 to N do write(st[i]. writeln end. while k>0 do begin repeat ST[k]:=ST[k]+1 until (ST[k]>N) or Valid. Se dau N puncte albe ºi N puncte negre în plan.Valid:=tmp end end. Se citesc 2N perechi de coordonate corespunzând punctelor. fie el alb sau negru. ST[k]:=0. Sã se determine o astfel de configuraþie de segmente încât oricare douã segmente sã nu se intersecteze. Problema 13 Enunþ. begin write('N= '). for i:=1 to N do Pus[i]:=false. de coordonate întregi. readln(N). Vom reþine în vectorii X ºi Y coordonatele punctelor. k:=1. procedure Printsol. var i:integer.' '). ST[k]:=0 end else begin k:=k-1. Fiecare punct alb se uneºte cu câte un punct negru. Rezolvare. k:=k+1. if ST[k]<=N then if k=N then Printsol else begin Pus[ST[k]]:=true. pleacã exact un segment. astfel încât din fiecare punct.

albe ºi pe poziþiile N+1, ... ,2N punctele negre. Soluþia problemei este de fapt o permutare. Aceasta va fi generatã în vectorul ST: ST[i] reprezintã punctul negru cu care va fi conectat punctul alb i. Funcþia Intersect întoarce o valoare booleanã care indicã dacã segmentul determinat de punctul alb i ºi punctul negru ST[i] se intersecteazã cu cel determinat de j ºi respectiv ST[j]. Condiþia de validitate pentru un segment este, evident, ca acesta sã nu se intersecteze cu nici unul din cele construite anterior.

program Puncte; var N,i,k X,Y ST,u :integer; :array[1..1000] of real; :array[0..1000] of integer;

function Intersect(i,j:integer):boolean; var a1,a2,b1,b2:real; begin if x[i]=x[st[i]] then a1:=9e10 else a1:=(y[st[i]]-y[i])/(x[st[i]]-x[i]); if x[j]=x[st[j]] then a2:=9e10 else a2:=(y[st[j]]-y[j])/(x[st[j]]-x[j]); b1:=y[i]-a1*x[i]; b2:=y[j]-a2*x[j]; Intersect:= ((x[i]*a2+b2-y[i])*(x[st[i]]*a2+b2-y[st[i]])<=0) and ((x[j]*a1+b1-y[j])*(x[st[j]]*a1+b1-y[st[j]])<=0) end; function Valid:boolean; var i:integer; r:boolean; begin r:=(U[ST[k]]=0); i:=1; while r and (i<k) do begin r:=r and (not InterSect(k,i)); i:=i+1 end; valid:=r end;

procedure Printsol; var i:integer; begin for i:=1 to N do write(ST[i],' '); writeln; k:=0; end; begin write('N='); readln(N); writeln('punctele albe:'); for i:=1 to N do begin write(i:3,':X,Y='); readln(X[i],Y[i]); end; writeln('punctele negre:'); for i:=N+1 to 2*N do begin write(i:3,':X,Y='); readln(X[i],Y[i]); end; k:=1; fillchar(ST,sizeof(ST),0); fillchar(u,sizeof(u),0); ST[1]:=N; while k>0 do begin repeat St[k]:=St[k]+1 until (ST[k]>2*N) or valid; if ST[k]<=2*n then if k=N then Printsol else begin U[ST[k]]:=1; k:=k+1; ST[k]:=N end else begin k:=k-1; U[ST[k]]:=0 end end end.

Problema 14
Enunþ: Se considerã n puncte în plan, de coordonate reale, (X1,Y1), (X2,Y2), (X3,Y3), ... , (Xn,Yn). Elaboraþi un program care selecteazã din aceste puncte vârfurile unui pãtrat, ce conþine numãrul maximal de puncte din cele date. Afiºaþi coordonatele punctelor selectate ºi numãrul de puncte incluse în pãtratul respectiv. Notã: Punctele care aparþin laturilor pãtratului se considerã incluse în pãtrat (inclusiv colþurile pãtratului). Datele de intrare se vor citi din fiºierul input.txt, astfel:
q

pe prima linie n, numãrul de puncte pe urmãtoarele n linii coordonatele punctelor.

q

Rezolvare. Coordonatele punctelor se vor citi în vectorul Pct. Stiva are 4 nivele reprezentând indicii celor 4 puncte care formeazã pãtratul. Dacã se gaseºte un astfel de pãtrat, se numãrã câte puncte sunt incluse în acest pãtrat. Se pãstreazã cea mai bunã soluþie în vectorul MaxP. Condiþia pentru ca 3 puncte sã poatã face parte dintr-un pãtrat este sã formeze un triunghi dreptunghic isoscel. Acest lucru se verificã prin relaþia între distanþele dintre puncte (d12,d23,d13). Douã dintre acestea trebuie sã fie egale ºi a treia egalã cu una din primele douã înmulþitã cu .

Atenþie! În geometria analiticã aplicatã, testul de egalitate a douã variabile reale a ºi b nu trebuie sã fie "dacã a=b", ci "dacã abs(a-b)<1e-5", adicã dacã diferenþã absolutã dintre a ºi b este mai micã decât -5 10 . Acest test evitã erorile de aproximaþie (în programul nostru acestea pot apãrea, de exemplu, la operaþia mai sus. ). Din acest motiv, am folosit funcþia booleanã Egal care face testul de egalitate prezentat

Dacã cele 3 puncte formeazã un triunghi dreptunghic isoscel se pãstreazã în variabila colt indicele vârfului triunghiului (unghiul drept) ºi în vectorul P (pe primele 3 poziþii) indicii celor 3 puncte în ordinea în care apar în pãtrat. Astfel, P[2] este vârful triunghiului dreptunghic isoscel ºi P[1], P[3] celelalte douã vârfuri. Pe nivelul 4, condiþia de validitate este ca punctul aºezat sã aibã distanþele la P[1] ºi P[3], egale cu

. de acelaºi sens. f :text. punctul Pct[i] trebuie sã se afle între dreptele suport ale segmentelor P[1]P[2] ºi P[4]P[3]. type punct=record x. colt. Pct[i]. procedure Citire.txt'). Pentru a fi inclus în pãtrat. begin if k<=2 then valid:=true .y:real end.y pct[p2].p2) întoarce distanþa între Pct[p1] ºi Pct[p2]. i Pct ST P. var n. Pentru a se afla între douã drepte paralele. ºi de asemenea între dreptele suport ale lui P[2]P[3] ºi P[1]P [4].20] of punct. function Dist(p1. function Egal(val1.x-pct[p2]. begin Egal:=(abs(val1-val2)<=1e-5) end. begin assign(f.y)) end.d1.'input.p2:integer):real.x)+sqr(pct[p1].d2.distanþele de la P[2] la aceste douã puncte. program puncte.maxp :integer. Funcþia Dist(p1.y). altfel. for i:=1 to n do readln(f. var i :integer.4] of integer. reset(f). function Valid:boolean. max. var d12. begin Dist:=sqrt(sqr(pct[p1].d3:real. Funcþia booleanã Inclus(i) întoarce TRUE dacã punctul Pct[i] este inclus în pãtratul P[1]P[2]P[3] P[4] ºi FALSE. k. :array [1. n).4] of punct. :array [1.. readln(f.d23.val2:real):boolean. un punct trebuie sã se afle în semiplane de semne opuse faþã de acele drepte. Pct[i].d13..x. :array [1. close(f) end.

Valid:=true end else Valid:=false else if Egal(d12.ST[3]). P[3]:=Pct[ST[2]].ST[4]).ST[4]). d2:=Dist(ST[1].ST[2]) end.d12*sqrt(2)) then begin colt:=1.ST[2]) end. d23:=Dist(ST[2]. P[1]:=Pct[ST[2]].ST[4]). P[1]:=Pct[ST[1]].d23) then if Egal(d13. P[3]:=Pct[ST[3]].ST[3]).d23) and Egal(d12. 2:begin d1:=Dist(ST[1].ST[4]).d23*sqrt(2)) then begin colt:=3. d3:=Dist(ST[1]. P[1]:=Pct[ST[1]].ST[4]).ST[4]). d13:=Dist(ST[1]. 3:begin d1:=Dist(ST[2].else if k=3 then begin d12:=Dist(ST[1]. .d12*sqrt(2)) then begin colt:=2. P[2]:=Pct[ST[1]].P[2]:=Pct[ST[2]].ST[2]). Valid:=true end else Valid:=false else if Egal(d13.ST[2]) end.d13) then if Egal(d23. if Egal(d12. d2:=Dist(ST[3]. d3:=Dist(ST[3]. Valid:=true end else Valid:=false end else begin case colt of 1:begin d1:=Dist(ST[2]. d3:=Dist(ST[1]. P[3]:=Pct[ST[3]]. d2:=Dist(ST[3]. P[2]:=Pct[ST[3]].

y)*(P[4].y-P[3]. begin c:=0.x)*(P[1].x-P[3].P[3].y-P[4].y)*(P[1].y)-(Pct[i].x))<=0 then tmp:=true inclus:=tmp end.x-P[3].y-P[3].x))* ((Pct[i].y)-(Pct[i]. procedure Numara. if Egal(d1. var c. for i:=1 to 4 do Maxp[i]:=P[i] end end. ST[k]:=0. while k>0 do begin repeat ST[k]:=ST[k]+1 until (ST[k]>n) or Valid.end.d3) then begin Valid:=true.x)*(P[2].y)*(P[1].y)* (P[2].y-P[2].x-P[2].x-P[3].y.y-P[4]. if c>Max then begin Max:=c.y)-(Pct[i]. P[4]:=Pct[ST[4]] end else Valid:=false end end. if ST[k]<=n then if k=4 then Numara . begin Citire.x)*(P[1].x)*(P[4].d3) and Egal(d2. for i:=1 to n do if Inclus(i) then c:=c+1.x-P[4].i:integer. k:=1. if ((Pct[i]. begin tmp:=false. function Inclus(i:integer):boolean.x-P[3].y-P[2].y-P[3].x-P[4]. var tmp:boolean.x))<=0 then if ((Pct[i].x-P[2].x))* ((Pct[i]. Max:=0.y)-(Pct[i].

q Vom scrie o singurã rutinã de backtracking. ST[k]:=ST[k-1] end else k:=k-1 end. În procedura de backtracking. :boolean. :integer. condiþia de validitate pentru a pune un element pe poziþia i este ca acesta sã se afle în ºirul iniþial dupã elementul anterior lui din subºir (ST[i]>ST[i-1]) ºi valoarea sa efectivã sã fie mai mare decât a elementului selectat anterior: V[ST[i]]≥ V[ST[i-1]].' '. unde p(k) reprezintã numãrul subºirurilor crescãtoare de lungime k. sã se calculeze p(1)+p(2)+. care va genera subºirurile crescãtoare de lungime L. cu L luând valori între 1 ºi k. .V N. if Max>0 then for i:=1 to 4 do writeln(Maxp[i]. Fiind dat un numãr natural N ºi un vector V cu N componente întregi.k..else begin k:=k+1. În stivã vom reþine indicele din vectorul iniþial al elementului aºezat pe poziþia i în subºir. procedure Printsol(l:integer).x:0:3. program Subsiruri.Maxp[i]. :longint.. writeln(Max) end.. var ST.+p(k). Punctul doi se realizeazã prin însumarea numãrului de soluþii generate de aceeaºi procedurã.1000] of integer. Problema 15 Enunþ. Pentru a afiºa soluþiile doar pentru L=[N/5] folosim variabila booleanã scrie. se cer urmãtoarele: q sã se determine toate subºirurile crescãtoare de lungime [N/5].y:0:3).i sol scrie :array[0. S-a considerat "mai mare sau egal" pentru cã în cerinþã se cer subºirurile crescãtoare ºi nu strictcrescãtoare.

' '). var f. writeln end.var i:integer. scrie:=true. procedure Bkt(l:integer). . if ST[i]<=N then if i=l then Printsol(l) else begin i:=i+1. sol:=sol+1 end. scrie:=false. begin write('N. ST[1]:=0. while i>0 do begin repeat ST[i]:=ST[i]+1 until ((ST[i]>ST[i-1]) and (V[ST[i]]>=V[ST[i-1]])) or (ST[i]>N). writeln('Subsirurile crescatoare de lungime '.i. begin i:=1. readln(N.k).i:integer.' este: '.N div 5. sol:=0. ST[i]:=0 end else i:=i-1 end end. writeln('Numarul subsirurilor crescatore de lungime <= '. for i:=1 to k do Bkt(i).k:').sol) end. Bkt(N div 5).']='). for i:=1 to N do begin write('V['. readln(V[i]) end.' sunt: '). begin if scrie then begin for i:=1 to l do write(V[ST[i]].K.

[ Cuprins ] [ Capitolul 2] .

Funcþia recursivã Suma calculeazã suma celor n numere. begin write('n= '). var n :integer.']= '). Rezolvare. var A:integer. Funcþia primeºte ca parametru k. Suma:=A+Suma(k-1) end else Suma:=0. end. . Când k=0 se iese din funcþie. function Suma(k:integer):integer. writeln(Suma(n)).n-k+1. end. un numãr din ºir ºi se adunã la sumã. begin if k>0 then begin write('A['.Capitolul 2 Recursivitate Problema 1 Enunþ. readln(A). numãrul de numere care au mai rãmas de citit. program sum. Calculaþi recursiv suma a n numere naturale citite. În caz contrar. readln(n). se citeºte de la tastaturã A.

. Funcþia recursivã este întocmai transcrierea formulei recurente.k).k) end. Acestea se definesc recursiv astfel: S(n. Problema 3 ..=S(n.k:integer):longint.k). var N.k) Sã se calculeze numãrul partiþiilor unei mulþimi cu n elemente.1)=. for k:=1 to N do R:=R+Part(N.k-1) + k*Part(n-1. Fiind datã o mulþime cu n elemente.k)=S(n.k R :integer. writeln(R) end. numãrul total de partiþii în k clase (k submulþimi) este dat de numerele lui Stirling de speþa a doua S(n.Problema 2 Enunþ. Trebuie precizat cã numãrul partiþiilor unei mulþimi este egal cu suma numãrului de partiþii în k clase. function Part(n. Rezolvare. program Partitii. readln(N). begin if (k=1) or (k=n) then Part:=1 else Part:=Part(n-1.k-1)+kS(n.n)=1 S(n+1. :longint. R:=0. dând lui k valori între 1 ºi numãrul de elemente din mulþime. begin write('N=').

begin write('N='). function Min(m1. begin if m1<m2 then Min:=m1 else Min:=m2 end.m) verificã relaþia de recurenþã: P(n. P(n. if (k=n) or (k=1) then R:=1 else for i:=1 to Min(k. var i:integer. Rezolvare. :longint. Prin P(n.N-k) do R:=R+P(n-k. Exemplu: P(4.k:integer):longint. program Suma.Enunþ. var N. se poate descompune ca sumã unicã de numere naturale.1)=P(n. r:longint. De exemplu.k R :integer.m2:integer):integer.k)=P(n+k.1)+P(n. Un numãr natural n. . Numerele P(n.k). begin r:=0. P:=R end. 4=2+2). pentru numãrul 4 se scrie descompunerea 2+1+1 (secvenþã descrescãtoare).n)=1 Sã se calculeze numãrul total de descompuneri ale numãrului natural n. m) notãm numãrul de împãrþiri ale lui n ca sumã (unicã) de m numere. nu ºi 1+2+1.i). function P(n.2)+…+P(n.2)=2 (4=3+1.

k-1) apare de douã ori). dacã k>n. Problema 4 Enunþ. Se citesc n ºi k (numere naturale n>k).k) corespunzãtoare. fie k=1).readln(N). Acest coeficient va creºte exponenþial cu numãrul de nivele. este evident cã în final perechea (n. Este necesar sã construim doar douã nivele ale arborelui pentru a observa cã etichetele nodurilor nu sunt distincte (perechea (n-2. orice pereche (n. Pentru a funcþiona corect. orice program (recursiv) bazat pe o relaþie recurentã trebuie sã porneascã de la câteva rezultate cunoscute pentru valori mici ale datelor de intrare.k) se va încadra într-una din cele douã ramuri de iniþializare ale formulei de recurenþã (fie n<k.k) de pe parcursul calculului. Este eficient? utilizând formula de recurenþã: Rezolvare. writeln(R) end.k) trece în (n-1.k). Apoi prin aplicarea succesivã a formulei de recurenþã. Dupã cum se vede. Pe cazul general. Astfel. Dacã mai construim un nivel din arbore vom observa cã programul va calcula este calculat de este ºase ori. for k:=1 to N do R:=R+P(N. programul reprezintã întocmai implementarea formulei de recurenþã la care s-au adãugat douã ramuri: =n ºi =0. Acum sã rãspundem la întrebarea dacã programul este eficient. Succesiunii apelurilor recursive i se poate asocia un arbore binar în care fiecare nod este etichetat cu perechea (n. . Deci pentru a calcula de douã ori . Calculaþi recursiv .k) ºi (n-1. orice instanþã a intrãrii trebuie redusã la unul din rezultatele cunoscute. el va efectua de un numãr de ori exponenþial aceeaºi secvenþã de operaþii.k-1). R:=0. calculat de Deci programul nostru în nici un caz nu este eficient: pentru o pereche (n.

k:integer):longint.program Combinari_recursiv.k)+Comb(n-1. begin write('N. var N.K). function Comb(n.K='). Aceasta se face folosind matricea Comb[n.k :integer.k-1) end. ºi anume evitã calcularea de mai multe ori a aceloraºi rezultate.K)) end. writeln(Comb(N. begin if k=1 then Comb:=n else if k>n then Comb:=0 else Comb:=Comb(n-1. Astfel se înlocuieºte o operaþie consumatoare de timp (numãrul de calcule era exponenþial) cu o operaþie elementarã (acces la matrice). Scrieþi un program iterativ care rezolvã problema anterioarã utilizând aceeaºi formulã. Problema 5 Enunþ. Programul de mai jos rezolvã neajunsurile celui dinaintea sa. Rezolvare. . readln(N. Modificarea esenþialã faþã de programul precedent constã în faptul cã apelurile recursive sunt înlocuite cu citiri în matrice.k] în care se va reþine valoarea .

k:integer):longint. for i:=1 to N do for j:=1 to k do Comb[i. Problema 6 Enunþ..k=').2]. var N. program Combinari_iterativ.Ca ºi la varianta recursivã. Function Comb(n. :array[0.j]. var N. Rezolvare. cunoscând valorile pentru un anumit N. writeln(Comb[N.0]:=1. begin write('N.50] of longint.k).50. . readln(N.j Comb :integer.j-1]+Comb[i-1. readln(N. care acum este parcurs de "jos în sus". for i:=0 to N do Comb[i. se pleacã de la valorile cunoscute Comb[i. cele pentru N+1 se obþin prin simpla aplicare a formulei de recurenþã.k :integer. begin write('N. aici combinãrile se calculeazã pe baza aceluiaºi arbore.0. Gãsiþi o formulã de recurenþã care sã rezolve eficient problema 4 (recursiv).k-1) div k end.K.k]) end. Program Combinari_recursiv2.1].K='). begin if k=1 then Comb:=N else Comb:=(n-k+1)*Comb(n.i. Anume..K). apoi se aplicã relaþia de recurenþã ºi se obþin valorile pentru Comb[i.j]:=Comb[i-1.

deci implicit ºi acelaºi cmmdc. Rezultã cã cele douã perechi de numere (u. Analog. Varianta iterativã a algoritmului se scrie astfel: while v<>0 do . Ecuaþia devine: cmmdc(u.v)=cmmdc(v. u-q*v). cât ºi pe v. orice divizor comun al lui v ºi u-q*v. r=u-q⋅ v ºi cum r=u mod v => u mod v=u-q⋅ v.k)) end.writeln(Comb(n. Atunci.v)=cmmdc(v. notat cmmdc(u.v) ºi (v. atunci x/(u-q⋅ v). u mod v). Considerãm cã u=q⋅ v+r (unde q este întreg). Deci orice divizor comun al lui u ºi v este ºi divizor al lui u-q⋅ v. Calculul celui mai mare divizor comun a douã numere naturale Algoritmul lui Euclid Definiþie: Cel mai mare divizor comun a douã numere întregi u ºi v.u-q*v) au aceiaºi divizori comuni. Algoritmul se bazeazã pe urmãtoarea formulã recursivã pentru cmmdc: Sã demonstrãm acum corectitudinea ecuaþiei cmmdc(u. este divizor al lui u.v). Dacã x este un divizor comun al lui u ºi v. este cel mai mare întreg care îl divide atât pe u.

v: '). program euclid_recursiv.v).u mod v). writeln(u) end. function cmmdc(u. var u. var u.v:integer. v: '). while v<>0 do begin r:=u mod v. begin write('u. end.r:=u mod v (u mod v = restul impartirii lui u la v) u:=v v:=r (la sfârºitul execuþiei programului. valoarea cmmdc se va afla în u) Vom prezenta acum implementãrile celor douã variante ale algoritmului. v:=r end.v:integer):integer. begin if v=0 then cmmdc:=u else cmmdc:=cmmdc(v. readln(u.v. . program euclid_iterativ.v). begin write('u.r:integer. readln(u. u:=v.

Dacã u ºi v sunt pare. Dacã u este par ºi v este impar. end.v)=cmmdc(u-v.v) ⇒ dacã unul dintre numere este par. v:=v/2. if v-par then interschimba(u.v)=2cmmdc(u/2. cmmdc(u. Lãsãm demonstraþia acestor propoziþii în seama cititorului.v)=cmmdc(u/2.v/2).v)). atunci cmmdc(u.v). Vom schiþa algoritmul iterativ în pseudocod: k:=0. c. ºi |u-v|<max(u. Algoritmul binar se bazeazã pe urmãtoarele patru afirmaþii despre douã întregi pozitive u ºi v: a. atunci u-v este par. conform afirmaþiei a) ºi cel puþin unul dintre u ºi v este impar. while u-par ºi v-par do u:=u/2.v).v). repeat .v). k:=k+1 ⇒ dupã acest prim pas al algoritmului. cmmdc=2k*cmmdc(u. b. d. atunci acesta trebuie sã fie u.writeln(cmmdc(u. Algoritmul binar Vom prezenta acum un alt algoritm pentru aflarea cmmdc descoperit de Josef Stein în 1961. Dacã u ºi v sunt impare. atunci cmmdc(u.

i:integer. until u=0. iterativã ºi recursivã. begin aux:=u. u:=u-v ⇒ conform afirmaþiei c). procedure intersch(var u. v: ').v). ⇒ u trebuie sã fie întotdeauna cel mai mare dintre cele douã numere. ar fi folosit în locul operaþiei x mod 2. begin write('u.v)=cmmdc(u-v. în cele douã variante.v). u:=v. Notã: O implementare mai eficientã. program binar_iterativ. cmmdc(u. v:=aux end. writeln(2k*v) Vom prezenta acum programele care implementeazã algoritmul binar. cmmdc(u.v:integer). Aceste perechi de operaþii sunt echivalente. dar operaþiile binare "and" ºi "shr" sunt mult mai rapide. conform afirmaþiei b).v). var aux:integer. operaþia x and 1 ºi în locul operaþiei x div 2. x shr 1. if v>u then interschimba(u.v. dacã u-par ºi v-impar.k.r.while u-par do u=u/2 ⇒ în urma acestui pas valoarea cmmdc se pãstreazã pentru cã. var u.v)=cmmdc(u/2. .

procedure intersch(var u. .v).v) end. begin if u=0 then cmmdc:=v else if u mod 2=0 then cmmdc:=cmmdc(u div 2. v:=aux end.v). var u.v).v) else if v>u then cmmdc:=cmmdc(v-u.v. program binar_recursiv.r. repeat while u mod 2=0 do u:=u div 2.u) else cmmdc:=cmmdc(u-v. var aux:integer. writeln(v) end. if v mod 2=0 then intersch(u. begin aux:=u. k:=0.v:integer). while (u mod 2=0) and (v mod 2=0) do begin v:=v div 2. readln(u. if v>u then intersch(u. u:=v. v: '). end. k:=0.i:integer. u:=u div 2. k:=k+1. while (u mod 2=0) and (v mod 2=0) do begin v:=v div 2. begin write('u. function cmmdc(u.readln(u. for i:=1 to k do v:=v*2.v:integer):integer.k.v). u:=u-v until u=0.

u:=u div 2. end. writeln(cmmdc(u.v).v)*r) end. if v mod 2=0 then intersch(u. for i:=1 to k do r:=r*2. [ Capitolul 1] [ Cuprins ] [ Capitolul 3] . k:=k+1. r:=1.

Înainte ºi dupã apelare se seteazã corespunzãtor Fol [i]. procedure Printsol. ºtiind cã trebuie respectate urmãtoarele reguli: r orice drapel are culoarea din mijloc galben sau verde.6] of string[10]=('alb'. galben. program drapel.. var ST:array [1. se apeleazã recursiv procedura Back pentru nivelul urmãtor. i. "roºu verde galben" Rezolvare.'negru')..'galben'. roºu. avem o soluþie ºi aceasta este tipãritã.'albastru'. . Sã se precizeze toate drapelele tricolore care se pot proiecta. nivelul curent al stivei (poziþia în drapel). Dacã se depãºeºte nivelul 3. În cazul în care culoarea este "validã". dacã este validã. Fol:array [1. Procedura Back primeºte ca parametru k.' '). var i:integer. 'rosu'. r Exemple: "alb galben roºu".. verde. begin for i:=1 to 3 do write(Culoare[ST[i]]. Funcþia booleanã Valid primeºte ca parametrii nivelul curent al stivei (pentru cã acesta nu mai este þinut într-o variabilã globalã) ºi culoarea pentru care se face testul de validitate.Capitolul 3 Backtracking recursiv Problema 1 Enunþ. const Culoare:array [1. cele trei culori de pe drapel sunt distincte.6] of boolean. Se încearcã aºezarea unei culori pe nivelul curent. k. 'verde'. Avem la dispoziþie 6 culori: alb.3] of integer. i:integer. albastru ºi negru.

care se scad din suma iniþialã. end. Back(1). procedure Back(k:integer). var i:integer. Procedura recursivã rezolvã problema pentru o sumã s. end. . end. dar de dimensiuni mai mici. în toate modurile posibile. Rezolvare. ca sumã de P numere naturale (P≤N). Se considerã pe rând toþi termenii între 1 ºi S. Fol[i]:=true. end. Sã se descompunã un numãr natural N. function Valid(niv.val:integer):boolean. reþinând termenii în vectorul solutie începând de pe pozitia k. begin for i:=1 to 6 do Fol[i]:=false. begin if Fol[val] then Valid:=false else if niv<>2 then Valid:=true else if (val=2) or (val=4) then Valid:=true else Valid:=false. ºi apoi se apeleazã recursiv pentru a rezolva aceeaºi problemã. Back(k+1).i) then begin ST[k]:=i. Fol[i]:=false. end. Problema 2 Enunþ.writeln. begin if k>3 then Printsol else for i:=1 to 6 do if Valid(k.

Pr.1000] of integer. var ST:array [0. sunt limitele între care poate lua valori nivelul curent al stivei (explicaþia se gãseºte în Cap. Back(1. La trecerea pe nivelul c+1. Back(k+1. readln(P). readln(N). k. Variabilele Min ºi Max. Dintr-un grup de N persoane. var i:integer. dintre care L femei.s:integer). var N. .' '). 1. :array[1. p. begin if (S=0) and (k=p+1) then begin for i:=1 to k-1 do write(ST[i]. Problema 3 Enunþ. begin write('N='). Procedure Back(k. am ajuns la o soluþie. l:integer. writeln end else for i:=1 to S do begin ST[k]:=i.program Descompuneri..N) end. c. trebuie formatã o delegaþie de C persoane.. P. Rezolvare. S. write('P='). 3).100] of integer. i ST :integer. K.s-i) end end. n. dintre care P femei. care se tipãreºte. Sã se precizeze toate delegaþiile care se pot forma. program Delegatie.

c. begin if k>c then Printsol else begin if k<>l+1 then Min:=ST[k-1]+1 else Min:=p+1. var i:integer. begin for i:=1 to c do write(ST[i]. var i. l: '). end. respectiv închise. Rezolvare. Procedura recursivã Back are trei parametri: q k. p. nivelul curent al stivei. Back(1).p. c.Max:integer. procedure Back(k:integer). if k<=l then Max:=p else Max:=n.l). end. q . Back(k+1).Min. for i:=Min to Max do begin ST[k]:=i.' '). end. Se dã un numãr natural par N. Nd ºi Ni.procedure Printsol. numãrul de paranteze deschise. Sã se afiºeze toate ºirurile de N paranteze care se închid corect. end. end. Problema 4 Enunþ. readln(n. writeln. ST[0]:=1. begin write('n. pânã la poziþia curentã.

Ni:integer).Nd+1. end end end. Back(k+1.Ni+1). end. var i:integer.Ni). if Ni<Nd then begin ST[k]:=2. procedure Back(k. var ST:array [1.. readln(N). N:integer. begin if k>N then Printsol else begin if Nd<N div 2 then begin ST[k]:=1.0.0). writeln. Back(1. begin for i:=1 to N do if ST[i]=1 then write('(') else write(')'). Back(k+1. end. procedure Printsol.Nd. end. begin write('N= ').20] of integer.program Paranteze.Nd. Problema 5 .

P. Rezolvare. while (Numar[i]<=s) and (i<=p) do begin ST[k]:=Numar[i]. begin Prim:=true. function Prim( X : integer):boolean. writeln end else begin i:=1.Enunþ. f:=f+1. begin write('N='). procedure Back(k. i:=i+1. while f*f<=X do begin if X mod f=0 then begin Prim:=false..s:integer). var ST. Fiind dat un numãr natural N.1000] of integer. readln(N). i :array[0. program Prime.s-Numar[i]). end. :integer. var f:integer. . end. Back(k+1.' '). f:=2. se cere sã se afiºeze toate descompunerile sale ca sumã de numere prime. begin if S=0 then begin for i:=1 to k-1 do write(ST[i]. var i:integer. end end end. Numar N. exit end.

. Problema 6 Enunþ. procedure Printsol. Valid:=tmp end end. for i:=1 to k-1 do if abs(V-ST[i])=1 then tmp:=true. :integer. existã 1≤ j≤ i astfel încât |V(i)-V(j)|=1. function Valid(k.N) end.' '). var ST n.20] of integer. begin for i:=1 to n do write(ST[i]. i:integer. program Permutari. :array [1.P:=0..20] of boolean. writeln end. begin if k=1 then Valid:=true else begin tmp:=false. var tmp:boolean. Rezolvare. Numar[P]:=i end. Back(1. for i:=2 to N do if Prim(i) then begin P:=P+1. Sã se genereze toate permutãrile de N cu proprietatea cã oricare ar fi 2≤ i≤ N. var i:integer. i Pus :array [1.v:integer):boolean..

end.n o matrice binarã.i) then begin ST[k]:=i. Pus[i]:=false.n.j) ale unui element. end..100. . Pus[i]:=true. for i:=1 to n do Pus[i]:=false. :array[0.. (1. (0. var x.0).0.. readln(n). j aparþinând 1. program Matrice.procedure Back(k:integer).1.1000] of integer. Back(1). i aparþinând 1. Problema 7 Enunþ. :array[0.2] of integer= ( (0.m.. end..y.100] of integer...M. begin if k>n then Printsol else for i:=1 to n do if not Pus[i] and Valid(k.f :integer. Sã se gãseascã toate ieºirile din matrice mergând numai pe elementele cu valoarea 1. Back(k+1).oi. Rezolvare.j..0).0). :integer. (0.oj A ST procedure Sol(k:integer). const Muta : array[0. Fie Am. var N.-1) ).1)..i.4. begin write('N= '). var i:integer. Se cunosc coordonatele (i. care are valoarea 1. (-1.

pj:integer). begin i:=pi+Muta[ST[k]. oj:=j. if Valid(k. readln(M.2] end. readln(A[i.y. .j). for f:=1 to k do begin write('('. var i.1].pi.2].j. if (pi=1) or (pj=1) or (pi=M) or (pj=N) then Sol(k). begin A[pi.j:').'.pj]:=1 end. y:=oj.begin x:=oi.'. begin write('M.N).pi+Muta[i. A[i. var i:integer. for i:=1 to 4 do begin ST[k]:=i.']='). for i:=1 to M do for j:=1 to N do begin write('A['.pj) then Back(k+1.2]) end.j :integer. Valid:=(i>0) and (j>0) and (i<=M) and (j<=N) and (A[i.pj+Muta[i.j]) end.pj]:=2.x.1].pi. procedure Back(k. writeln end.'. readln(i. x:=x+Muta[ST[f]. j:=pj+Muta[ST[k].j]:=1.pj:integer):boolean. y:=y+Muta[ST[f]. oi:=i.'. function Valid(k.pi.1]. write('i. A[pi.') ').N:').i.j]=1) end.

1000] of integer. dar nu conþin alte q obiecte date. conþin r obiecte din p obiecte date. Se considerã o mulþime de N elemente ºi un numãr natural K nenul. Rezolvare. :char. var i. for i:=1 to K do write(ST[i]. :integer. Ls. K. procedure Back(l:integer).i. Q. P. Problema 8 Enunþ. var ST.b:integer. R. u N. :longint.j) end. conþin exact un obiect din p obiecte date e. Li.Back(1.' '). dar nu conþin un altul d. conþin cel puþin un obiect din p obiecte date f. if l=k+1 then begin sol:=sol+1. Sã se calculeze câte submulþimi cu k elemente satisfac pe rând condiþiile de mai jos ºi sã se afiºeze aceste submulþimi: a. program Submultimi. begin if st[l-1]<Li[l] then b:=Li[l] else b:=st[l-1]. conþin p obiecte date. conþin exact un obiect dat. i sol ch :array[0. b. .. nu conþin nici unul din q obiecte date c.

Ls[1]:=1 end. 'C':begin for i:=2 to K do begin Li[i]:=3.P. for i:=P+1 to K do begin Li[i]:=P+1. Ls[1]:=p end. readln(N. Ls[i]:=N end end.E sau F):').K. U[i]:=0 end end.Q:'). Ls[i]:=N end. Case UpCase(ch) of 'A':begin for i:=1 to P do begin Li[i]:=i. readln(ch). 'E':begin for i:=2 to K do . Ls[i]:=N end.C. Back(l+1). 'D':begin for i:=2 to K do begin Li[i]:=P+1. write('subpunctul (A.D.K.P. Li[1]:=1. Ls[i]:=i end.writeln end else for i:=b to Ls[l] do if U[i]=0 then begin ST[l]:=i.Q).B. U[i]:=1. Ls[i]:=N end. 'B': for i:=1 to K do begin Li[i]:=Q+1. begin write('N. Li[1]:=1.

Ls[1]:=p end.sol) end. for i:=R+1 to K do begin Li[i]:=P+Q+1. Ls[i]:=P end. Back(1).begin Li[i]:=1. Li[1]:=1. 'F':begin for i:=1 to R do begin Li[i]:=i.Ls[i]:=N end end end. [ Capitolul 2] [ Cuprins ] [ Capitolul 4] . writeln('Numar solutii= '. Ls[i]:=N end.

Acest algoritm are însã neajunsul cã necesitã ca vectorul A sã fie sortat crescãtor. if Gasit_v then writeln(p) else writeln('0'). care testeazã elementele vectorului unul dupã altul. numãrul de astfel de operaþii de bazã efectuate va fi egal cu numãrul de elemente al lui A. Deci complexitatea algoritmului pe cazul cel mai defavorabil este O(n). începând cu primul. Sã se afiºeze p astfel încât v=A[p] sau 0 dacã nu existã un element cu valoarea v în A. În acest caz se va face o parcurgere completã a lui A. p:=0. este cãutarea binarã.n] ºi o valoare v de acelaºi tip cu elementele din A. if v=A[p] then Gasit_v:=True. Cel mai simplu algoritm care rezolvã aceastã problemã este cãutarea liniarã. mai eficient. Sã analizãm complexitatea acestui algoritm pe cazul cel mai defavorabil.. Iatã implementarea acestui algoritm: Gasit_v:=False. în cãutarea lui v. while not Gasit_v do begin p:=p+1. Iatã procedura recursivã care implementeazã algoritmul: . end. Un alt algoritm. adicã n.Capitolul 4 Analiza timpului de calcul necesar algoritmilor Cãutare binarã Definim problema cãutãrii: Se dã un vector A[1. acela în care v nu se gãseºte în A. Considerând ca operaþie de bazã testul v=A[p].

j). if v=A[m] then writeln('Valoarea gasita la pozitia: '.. înseamnã cã v nu se gãseºte în A ºi apelarea recursivã înceteazã datoritã neîndeplinirii condiþiei i<j. Procedura primeºte ca parametri i ºi j. pentru cã vectorul este sortat crescãtor.j) end.j]. dacã v>A[m]. dacã se gãseºte în vector.j:integer).. În acest caz procedura se apeleazã recursiv cu noua porþiune de vector (i. este în stânga lui m. ºi acest element este diferit de v. adicã indicele elementului din mijlocul lui A[i. begin m=(i+j) div 2. m primeºte valoarea (i+j) div 2.procedure caut(i. limitele porþiunii din vector în care cãutãm. adicã o porþiune de vector de un element. Analog. procedura se apeleazã recursiv pentru (m+1. Iniþial procedura este apelatã cu i=1 ºi j=n.m-1) ca parametru.m) else if i<j then if v<A[m] then caut(i. Dacã v<A[m] înseamnã cã poziþia lui v. atunci programul se terminã cu afiºarea indicelui acestui element. Dacã elementul din mijlocul acestei porþiuni.m-1) else caut(m+1. A[m]. Exemplu: . Dacã se ajunge la i=j. este chiar valoarea cãutatã..

La ultimul apel. i=j. Problema este deci. Se demonstreazã uºor 1 cã acelaºi lucru este valabil ºi dacã 2k<n<2k+1. adica elementul din A corespunzãtor valorii elementului curent din V. Presupunem cã n=2k. În concluzie. Se iniþializeazã A cu 0 ºi apoi se face o parcurgere a vectorului V incrementându-se la fiecare pas A[V [i]]. În urma acestei parcurgeri A[i] va fi numãrul de elemente cu valoarea i din V (respectiv 0. adicã n=21. în cazul în care valoarea cãutatã nu se gãseºte în vector. este O(log2n). Deci numãrul maxim de execuþii ale procedurii caut ºi implicit. care este ca ºi la cãutarea liniarã acela în care valoarea cãutatã nu se gãseºte în vector. Iniþial procedura este apelatã cu i=1 ºi j=n. pentru un vector cu n=100000 de elemente. . o execuþie a procedurii caut. Ne propunem sã sortãm crescãtor vectorul V. dacã nu existã nici un element cu valoarea i în V). trebuie sã vedem de câte ori se executã procedura caut. log2n=1000. o astfel de metodã fiind sortarea prin numãrare. de câte ori se poate împãrþi un numãr natural n la 2. j-i+1=n. dar necesitã ca vectorul sã fie sortat. care are aproximativ 300 de cifre. La fiecare apel recursiv. Existã totuºi metode de sortare O(n) care nu folosesc comparaþia elementelor ca operaþie de bazã. unde L≥ M. Folosim de asemenea. Sortare prin numãrare Este demonstrat cã metodele de sortare care folosesc ca operaþie de bazã pentru determinarea ordinii elementelor comparaþia (ex.. mai bunã de O(n⋅ lg n). cãutarea binarã este mult mai eficientã decât cea liniarã. De exemplu. în timp ce cãutarea binarã va face numai log2n=log2100000=16 operaþii. deci mãrimea porþiunii va fi i-j+1=1. Considerãm ca operaþie elementarã . pânã când câtul împãrþirii va fi 1. adicã pânã când i=j. pe cel mai defavorabil caz. Dacã n=2k sau 2k<n<2k+ . complexitatea algoritmului. Logaritmul este o funcþie care creºte mult mai încet decât funcþiile polinomiale. Definim M ca fiind valoarea maximã din V.Sã analizãm complexitatea acestui algoritm pe cel mai defavorabil caz. sortarea prin interclasare. sortarea prin inserþie) nu pot avea o complexitate. iar sortarea nu poate fi fãcutã (prin metodele care folosesc comparaþii) într-o complexitate mai bunã de O(n⋅ lg n). quicksort. un vector auxiliar A[1. porþiunea de vector se înjumãtãþeºte. pânã ce câtul împãrþirii va fi 1.L]. mãrimea porþiunii de vector fiind . Dacã n=21000. atunci k=[log2n]. care are n elemente naturale. La fiecare împãrþire a lui n la 2 exponentul acestuia scade cu o unitate. Pentru a determina complexitatea algoritmului. Deci n se poate împãrþi la 2 de k ori. heapsort. cãutarea liniarã va face n=100000 de operaþii elementare. Procedura se va apela recursiv pânã când se va ajunge la o porþiune de vector de un element.

Parcurgem apoi A ºi V în paralel. Exemplu: n=8 V=<7.0.5> M=7 Prima parcurgere: i=1. În final.4. V[7]:=5 i=6 j=8 A[i]=A[6]=0 i=7 j=8 A[i]=A[7]=1 ⇒ V[8]:=7 Rezultã vectorul V sortat: V=<1.5.Se parcurg apoi vectorii A ºi V în paralel.0.4.2.1> i=2. începând cu poziþia 1.0.4.5. incrementãm A[V[1]]=A[7] ⇒ A=<0.0.0. incrementãm A[V[3]]=A[4] ⇒ A=<0.0. incrementãm A[V[2]]=A[5] ⇒ A=<0.5.1.5.2.5. V[6]:=5. V[4]:=4 i=5 j=5 A[i]=A[5]=3 ⇒ V[5]:=5.0.1.7> .2.0.1>. cu i indicele de parcurgere al lui A ºi j indicele de parcurgere al lui V: i=1 j=1 A[i]=A[1]=1 ⇒ V[1]:=1 i=2 j=2 A[i]=A[2]=1 ⇒ V[2]:=2 i=3 j=3 A[i]=A[3]=0 i=4 j=3 A[i]=A[4]=2 ⇒ V[3]:=4.0.2.1> i=4.0. Când se întâlneºte un element A[i] >0.0.0.0.1.1> Procedeul continuã pânã la i=8.1.0.0. se dã valoarea i urmãtoarelor A[i] poziþii din V.3.1.1.1> i=3.0.0. incrementãm A[V[4]]=A[5] ⇒ A=<0.4. A=<1.

begin Citire. a cãrui complexitate este O(n⋅ lg n) ºi spaþiu de memorie O(n).. Se observã cã algoritmul nu este practic decât pentru valori mari ale lui n ºi valori relativ mici ale lui M. În plus. preferãm un algoritm clasic. Altfel. j. program Sortare_prin_numarare. const L=200. end.L] of integer. var i:integer. begin write('n= ').0). ca heapsort sau quicksort. for i:=1 to M do if A[i]>0 then for k:=1 to A[i] do begin .100] of integer. for i:=1 to n do begin write('V['. readln(n). :integer. sortarea prin numãrare are neajunsul cã nu este aplicabilã decât pe vectori cu elemente naturale (sau eventual întregi).2*L. for i:=1 to n do begin if V[i]>V[M] then M:=i. FillChar(A.. A[V[i]]:=A[V[i]]+1.i. k :array [1.']= '). :array [1. readln(V[i]) end end. j:=0. deci complexitatea algoritmului este O(max(n. M:=1. M:=V[M]. i.Programul constã din douã parcurgeri (nu considerãm ºi parcurgerea pentru iniþializarea vectorului A): una de lungime n ºi una de lungime M. Spaþiul de memorie folosit pentru vectorii A ºi V este O(M+n).M)). n. var V A M. procedure Citire.

program Eratostene. este mai mic sau egal decât acest numãr prim. atunci i este prim. sau dacã câtul împãrþirii lui i la numãrul prim respectiv. Demonstraþia se face prin reducere la absurd: dacã i nu este prim. caz în care i este prim ºi este adãugat în vectorul Prime. Pentru a determina dacã un numãr i este prim sau nu. Term begin :array [1. for i:=1 to n do write(V[i]. Dacã Q≤ Prime[j]⇒ Prime[j]> .j:=j+1. atunci are cel puþin 2 divizori: Contradicþie Complexitatea acestei metode este aproximativ O(n⋅ lg n). end. Dacã un numãr i nu are nici un divizor pânã la .' '). Ciurul lui Eratostene Ciurul lui Eratostene este o metodã de determinare a numerelor prime mai mici decât n.. în ordine. C. se testeazã divizibilitatea acestuia cu numerele prime deja determinate (þinute în vectorul Prime). V[j]:=i. . Demonstraþia corectitudinii algoritmului: i:Prime[j]=Q (rest R) ⇒ i=Prime[j]⋅ Q+R. :boolean. writeln. i. Testarea se opreºte dacã i se divide cu unul din numere. var Prime n. j. :integer. Prime[j].13000] of integer. NrP. end. R Prim.

Problema poate fi rezolvatã însã. Se fac astfel 3 comparaþii la fiecare pas ºi în total avem n/2 paºi (pentru cã la fiecare pas procesãm o pereche de elemente ºi în total sunt n elemente). R:=i mod Prime[j].write('n= '). Prime[1]:=2. end. în mod clar. for i:=1 to NrP do write(Prime[i]. if Prim then begin NrP:=NrP+1. Maxim ºi minim simultan Problema determinãrii maximului ºi minimului dintr-un vector este. O(n). În total vom avea 2(n-1)=2n-2 comparaþii.n. C:=i div Prime[j].' '). end. Term:=false. Prime[NrP]:=i. Prim:=true. NrP:=2.NrP). deci complexitatea O(n). apoi cel mai mic dintre ele este comparat cu minimul ºi celãlalt cu maximul. La fiecare pas se comparã o pereche de elemente din vector. Prime[2]:=3. for i:=4 to n do begin j:=0.': '. if C<=Prime[j] then Term:=true. Se face o singurã parcurgere a vectorului pentru determinarea simultanã a maximului ºi a minimului. if R=0 then Prim:=false. fãcând acelaºi lucru cu mai puþine comparaþii. una pentru determinarea maximului ºi una pentru determinarea minimului. while Prim and not Term do begin j:=j+1. writeln('Numarul de nr. Considerãm comparaþia ca operaþie de bazã ºi facem douã parcurgeri ale vectorului. Complexitatea ambelor variante este O(n). doar cu 3(n/2)-2 comparaþii. prime mai mici decit '. dar cea de-a doua este mai eficientã. readln(n). . writeln end. end.

readln(n).A[Min]). Max:=1. Min:=1. if A[Max]<A[n] then Max:=n end. n procedure Citire. begin write('n= ').']= '). Mare:=2*i+1. i.100] of integer. . var i:integer. end. writeln('Minimul: '. if A[Max]<A[Mare] then Max:=Mare. for i:=1 to n do begin write('A['. if n mod 2=0 then begin if A[n]<A[Min] then Min:=n. for i:=1 to (n-1) div 2 do begin if A[2*i]<A[2*i+1] then begin Mic:=2*i. end else begin Mic:=2*i+1.program MinMax. :array [1. Max. begin Citire. Mare:=2*i. end. :integer.. Mare. Mic.i. writeln('Maximul: '. var A Min. readln(A[i]) end end.A[Max]) end. if A[Mic]<A[Min] then Min:=Mic.

Prezentãm acum programele care implementeazã cele douã metode de rezolvare. . ceea ce înseamnã. Aceastã operaþie va fi efectuatã de un numãr de ori egal cu numãrul de termeni al sumei. O primã metodã constã în a calcula valoarea tuturor termenilor (produselor) ºi a face suma lor. Cel de-al doilea program este pur ºi simplu implementarea formulei de mai sus. În total avem vedere operaþia de bazã aleasã. Sã analizãm complexitatea acestei metode. deci o complexitate exponenþialã. Primul program (metoda “exponenþialã”) foloseºte o rutinã de generare a combinãrilor (backtracking) pentru calcularea celor 2n termeni. a2. Se dau n întregi: a1. Sã calculãm acum numãrul de termeni al expresiei. complexitatea acestei metode este O(n) pentru cã produsul are n factori. deci se efectueazã n înmulþiri.Problema 1 Enunþ. Acest Suma coeficienþilor acestui polinom este exact valoarea expresiei din problema noastrã (minus 1. Sunt termeni în expresie. an.…. Suma coeficienþilor unui polinom este P(1). Considerãm ca operaþie de bazã calcularea valorii unui termen (produs) ºi adunarea lui la suma totalã. plus 1 (coeficientul lui polinom se poate scrie sub formã de produs ca: ). având în termeni cu k factori. Coeficienþii acestui polinom sunt exact termenii expresiei noastre. Sã se afiºeze valoarea expresiei: Rezolvare. coeficientul lui egalã cu ). Considerãm polinomul: . cã complexitatea acestei metode este O(2n). Rezultã cã valoarea expresiei este -1 Considerând ca operaþie de bazã înmulþirea.

Suma:=Suma+Termen end. for i:=1 to nrfact do Termen:=Termen*a[ST[i]]. begin write('n= '). .. end. timpul de execuþie fiind de 20 de secunde pe un Pentium la 90MHz. i :integer. var a. begin if k>nrfact then Aduna else for i:=ST[k-1]+1 to n do begin ST[k]:=i. procedure Back(k:integer). var Termen :longint. readln(a[i]). în timp ce al doilea va efectua numai n=20 operaþii. var i:integer. for i:=1 to n do begin write('a['. primul program va efectua 2n=220=1048576 operaþii de bazã. end. nrfact Suma :array [0.Invitãm cititorul sã ruleze cele douã programe. end. cu diferite valori ale lui n.']= '). Back(k+1) end.i.100] of integer. timpul de execuþie fiind insesizabil. pentru n=20. procedure Aduna. :integer. pentru a vedea diferenþa la timpul de execuþie. ST n. program Expresie_exponential. De exemplu. :longint. begin Termen:=1. readln(n). var i:integer. procedure Citire.

program Expresie_polinomial. ST[0]:=0.…. end. var i:integer.i.. begin write('n= '). for nrfact:=1 to n do Back(1). . :longint. Suma:=0. an lei. for i:=1 to n do begin write('a['.100] of integer. for i:=1 to n do Suma:=Suma*(a[i]+1).']= '). var a n. end. a2. Se cere numãrul modalitãþilor distincte de platã a sumei X utilizând aceste monede. :integer.begin Citire. i Suma :array [1. Se dau suma X ºi n tipuri de monede având valori de a1. end. writeln(Suma). readln(n). writeln(Suma-1). Suma:=1. readln(a[i]). end. begin Citire. Problema 2 Enunþ. procedure Citire.

Programul nu foloseºte o stivã explicitã pentru cã nu este nevoie ºi de generarea propriu-zisã a modalitãþilor de platã. write('n= '). program Suma_exponential. Nivelul curent din stivã poate lua valori numai între valoarea nivelului anterior ºi n. a3=4 sunt 4 modalitãþi distincte de platã: 1. 4=4 (cu 1 moneda de 4 lei) Metoda I (exponenþialã) Aceastã metodã foloseºte tehnica backtracking pentru rezolvare. var a X. este exponenþialã (nu vom prezenta demonstraþia aici întrucât este foarte complexã). S – suma elementelor aºezate pânã la nivelul curent în stivã ºi lim – valoarea nivelului k-1 din stivã. tocmai pentru a evita folosirea unui vector-stivã. ci numai de numãrarea lor. fiind vorba de folosirea tehnicii backtracking. pentru a evita duplicarea modalitãþilor de platã. Rutina recursivã Back primeºte trei parametri: k – nivelul curent al stivei. .100] of integer. 4=1+1+2 (cu 2 monede de 1 leu ºi una de 2 lei) 3.Exemplu: Pentru X=4. var i:integer. respectiv S=X ºi pentru a opri recurenþa dacã S>X. :integer.. a2=2. begin write('X= '). 4=2+2 (cu 2 monede de 2 lei) 4. n procedure Citire. NrMod. Aceastã valoare a nivelului anterior este transmisã prin variabila lim. readln(X). n=3. Complexitatea acestei metode. 4=1+1+1+1 (cu 4 monede de 1 leu) 2. readln(n). a1=1. for i:=1 to n do begin :array [1. Avem nevoie de variabila S pentru a ºti când avem o modalitate de platã validã. prin forþarea ordinii crescãtoare în stivã.

prezenþa lui în sumã este justificatã prin nefolosirea niciunei monede de tipul i. Primul termen este numãrul de modalitãþi în care poate fi obþinutã aceeaºi sumã S=7. se adaugã la sumã numãrul de modalitãþi în care poate fi obþinutã suma S-3=7-3=4.i).3).1).4) având deja calculate M(x.3)+M(4. NrMod:=0.lim:integer). adicã M .']= '). readln(a[i]) end end. unde M(S.S. trebuie sã calculãm M(7. end. Conform formulei.0.S+a[i]. M(7. Back(1. Metoda II (pseudo-polinomialã) Cea de-a doua metodã de rezolvare utilizeazã urmãtoarea formulã de recurenþã: . cu primele 3 tipuri de monede.4)=M(7.write('a['.3)+M(1. Dacã folosim douã monede de tipul i. adicã de 3 lei. pentru x de la 1 la X. begin if S=X then NrMod:=NrMod+1 else if S<X then for i:=lim to n do Back(k+1.3). Exemplu: Dacã S=7. adicã de 3 lei. writeln(NrMod). begin Citire. cu primele 3 tipuri de monede. end. var i:integer.i) este numãrul de modalitãþi de platã ale sumei S folosind numai primele i tipuri de monede. procedure Back(k. i=4 ºi ai=3.i. folosind doar primele 3 tipuri de monede. se adunã numãrul de modalitãþi în care poate fi obþinutã suma S-6=1. Dacã folosim o monedã de tipul i.

i-1). var a X. readln(a[i]) end end.i) ºi al doilea M(x. unde primul reprezintã M(x.n). Mant :array [1. pentru x de la 1 la X.100] of integer.1000] of longint. var i:integer. pentru cã nu depinde numai de n.0). adicã numãrul de modalitãþi de platã ale sumei X. Complexitatea acestei metode este O(n⋅ X2).']= ').n). readln(X).. n. folosim doi vectori. :integer. k M. Pentru aceasta. pânã la obþinerea lui M(X. write('n= '). readln(n).i.. :Vector. for i:=1 to n do begin write('a['.2*(X+1). S. for i:=1 to n do begin . cu toate cele n tipuri de monede. Aceasta este o aºa numitã complexitate pseudopolinomialã. problema fiind NPcompletã. adicã face parte dintr-o clasã de probleme pentru care nu se cunoaºte o rezolvare polinomialã. type Vector=array [0. begin Citire. i. Mant[0]:=1. begin write('X= '). Calculãm succesiv valoarea lui M din Mant prin formula prezentatã.2*(X+1). Folosind aceastã formulã trebuie sã obþinem M(X. program Suma_pseudopolinomial. FillChar(M. FillChar(Mant.0). procedure Citire. Dacã valorile monedelor erau numere reale.(1. M ºi Mant. M[0]:=1.3). metoda aceasta nu poate fi aplicatã.

numãr care are De exemplu. ba mai mult cã este suficient sã înmulþim numai ultima cifrã a lui a cu ea însãºi de x1 …⋅ xn ori ºi sã pãstrãm de ⋅ fiecare datã numai ultima cifrã a produsului. x :integer. n ºi x1. :longint. xn naturale nenule. .…. writeln(M[X]) end. va trebui sã calculãm 10 1024 de cifre. aceastã metodã are complexitatea O(x1 …⋅ xn). xn. end. Observãm cã este suficient sã pãstrãm numai ultima cifrã a numãrului la fiecare înmulþire. procedure Citire. C O. x2.for S:=1 to X do for k:=1 to S div a[i] do M[S]:=M[S]+Mant[S-k*a[i]]. Problema 3 Enunþ. var a. Mant:=M. pentru a=n=10 ºi x1= x2=…= xn=2. begin write('a= '). Este clar cã aceastã metodã nu este practicã.…. Metoda I Cea mai simplã metodã de rezolvare ar fi sã-l înmulþim pur ºi simplu pe a cu sine însuºi de x1 …⋅ xn ⋅ ori. Dându-se a ºi x1. ⋅ program Cifra_1. readln(a). var i. 1024 . n. i :integer. Este limpede însã cã numãrul rezultat ar fi imens chiar ºi pentru valori mici ale lui a. Dacã considerãm ca operaþie de bazã înmulþirea. sã se afiºeze ultima cifrã a numãrului .

1. readln(x).49. end. end. for i:=1 to O do C:=C*a mod 10. .']= '). Sã vedem ce se întâmplã pentru 7: 7.7. 3.i.21. 9. begin Citire. 5 sau 6. Deci perioada lui 7 este 7. orice putere a unui numãr terminat în cifra 0 se va termina cu cifra 0. La fel ºi pentru numerele terminate în 1. O:=O*x. for i:=1 to n do begin write('x['. De exemplu.63. . writeln(C)...a:=a mod 10. . . C:=1. Toate cifrele au perioade de lungime maxim 4. O:=1. dupã cum se poate vedea în tabelul de mai jos: K 0 1 2 3 4 K2 0 1 4 9 6 K3 0 1 8 7 4 K4 0 1 6 1 6 K5 0 1 2 3 4 . write('n= ').63. readln(n).. Metoda II Cea de-a doua metodã porneºte de la observaþia cã cifra în care se terminã puterile unui numãr se repetã cu o anumitã perioadã.. .. 49.… ºi aºa mai departe. end.

:longint.6.8. write('n= ').0.5.5. (1.8). var a O :integer.3.1.4.2. pentru a ºti cifra în care se terminã puterea respectivã ⋅ a lui a.0.0)..5). const Perioada:array [0. x. (6.9. program Cifra_2.7. (6.4).1). .5 6 7 8 9 5 6 9 4 1 5 6 3 2 9 5 6 1 6 1 5 6 7 8 9 Este suficient sã reþinem pentru fiecare cifrã perioada (ca în tabelul de mai sus) ºi sã determinãm forma lui x1 …⋅ xn: 4K.. considerând ca operaþie de bazã înmulþirea. (1.9.6. begin write('a= ').4. procedure Citire. readln(a).1. (6.4.6. (6.3).3] of integer= ((0. readln(n). Complexitatea acestei metode este O(n). (1. (5.9)).9.7).1. 4K+1.2).6). n :integer. var i. (1. 4K+2 sau 4K+3. a:=a mod 10.0.9.

Complexitate cerutã: O(n⋅ lg n). 2. Ce algoritm este de preferat. Calculaþi valoarea lui P(x)=anxn+ an-1xn-1+…+ a1x+ a0.i. writeln(Perioada[a. x2. for i:=1 to n do begin write('x['. Apoi se cautã cel mai mic element dintre cele rãmase în A ºi se pune pe poziþia a doua în B. pe cazul cel mai favorabil ºi pe cel mai defavorabil: Se cautã cel mai mic element din vectorul care trebuie sortat. sortarea prin selecþie sau sortarea cu bule ? . an ºi un numãr real x. end.O:=1. Estimaþi timpul de calcul al urmãtorului algoritm (sortarea prin selecþie).O mod 4]). xn. Probleme propuse: 1.…. a1. (Problema evaluãrii unui polinom într-un punct) Se dau n coeficienþi a0.…. Se dã o secvenþã de numere x1. A. readln(x). begin Citire. end. Indicaþie: Sortaþi vectorul printr-o metodã de sortare n⋅ lg n ºi apoi comparaþi elementele de pe poziþii consecutive. ºi se pune pe prima poziþie a unui alt vector B. Ce complexitate are algoritmul banal ? Descrieþi un algoritm O(n) care foloseºte metoda lui Horner de rescriere a unui polinom: P(x)=(…((anx+an-1)x+an-2)x+…+ a1)x+ a0.']= '). O:=O*x. end. 4. Sã se determine dacã existã un numãr care apare de mai multe ori în aceastã secvenþã. Procedeul continuã pentru restul elementelor. Estimaþi timpul de calcul al sortãrii cu bule (bubble-sort) pe cazurile favorabil ºi defavorabil. 3.

datã fiind o mulþime M cu n numere reale ºi un alt numãr real x. Sã se gãseascã elementul din X sau Y. 5. a cãror sumã este x. determinã dacã existã sau nu 2 elemente în M. care are proprietatea cã este mai mare decât exact i-1 elemente din cele 2n conþinute de cei doi vectori.n] ºi 1≤ i≤ 2n.Indicaþie: Comparaþi timpii de calcul pe cazul defavorabil ºi favorabil. Se dau doi vectori sortaþi crescãtor X[1. Complexitate cerutã: O(lg n) 6... Gãsiþi un algoritm O(n⋅ lg n) care. [ Capitolul 3] [ Cuprins ] [ Capitolul 5] .n] ºi Y[1.

iar cel drept al lui V[7.Capitolul 5 Divide et Impera Problema 1 Enunþ. Ca rãdãcinã a subarborelui stâng (adicã fiu stânga al rãdãcinii). subarborele drept este arborele cartezian al subvectorului drept. alegem elementul minim din V[1. subarborele stâng este arborele cartezian al subvectorului stâng (faþã de poziþia elementului din rãdãcinã).. Un arbore cartezian al unui vector este un arbore binar definit recursiv astfel: q rãdãcina arborelui este elementul cel mai mic din vector. adicã V[2]=8. Procedeul continuã recursiv pentru restul vectorului..5]. q q Exemplu: Pentru vectorul I V[I] 1 9 2 8 3 23 4 10 5 16 6 3 7 12 8 4 9 7 Se pune în rãdãcinã elementul cel mai mic din vector.9]. anume 3.5].. Subarborele stâng este la rândul lui un arbore cartezian al subvectorului V[1. Iatã arborele rezultat: .

4. for i:=1 to n do begin write('V['.Tata)... begin write('n= ')..16..Min-1] ºi V[Min +1. Se afiºeazã muchia (V[Tata]..j]. Exemplu: n=9 V=(9. Se determinã Min. Tata :integer).V [Min]). Apelãm recursiv pentru V[1.. begin if i<=j then begin . Pentru construirea arborelui folosim o procedurã recursivã divide(i.']= ').8).10. var V n M :array [1. Minimul este gãsit pe poziþia Min=6. Tata este nodul al cãrui subarbore este arborele cartezian al lui V[i. Se apeleazã recursiv pentru V[i. Analizãm doar primul apel. var k..1.. Rezolvare.j].Se dã un vector cu n elemente. j.j]. readln(n).. procedure Citire. Sã se afiºeze muchiile arborelui sãu cartezian. cu parametrul Tata=Min=6. ºi se apeleazã recursiv procedura pentru V[i. :array [1. indicele elementului minim din V[i. :integer. Notã: O rezolvare O(n) a acestei probleme poate fi gãsitã în cartea “Psihologia concursurilor de informaticã” de Cãtãlin Frâncu.12.5].. ºi pentru V[Min+1. readln(V[i]) end.5]. adicã muchia (3.i. i=1.9].101. var i:integer.1] ºi V[3.7) Iniþial procedura se apeleazã cu i=1 ºi j=n=9.Min-1]. Se gãseºte Min=2. end.j].2] of integer.Min:integer. Tata=6..3.. adicã V[7. j=5.j.100] of integer.8.23. program Arbore_cartezian. procedure Divide(i. adicã V[1..

Min:=i.' '. are o suprafaþã în comun cu cercul.n. Coordonatele se citesc de la tastaturã ºi sunt numere reale. Divide(1. begin Citire.Min). Se cere sã se calculeze aria lor comunã cu precizie de o zecimalã. Divide(Min+1. Aria se va afiºa pe ecran. end.V[Tata]). dr. for k:=i+1 to j do if V[k]0 then writeln(V[Min]. Problema 2 Enunþ. Divide(i. end.Min).0). q q . Se dau un pãtrat ºi un cerc. writeln('Muchiile arborelui sunt:'). Adrian Atanasiu. enunþ reformulat Rezolvare. Paco 1998. Prof. end.Min-1.j. este exterior cercului. Distingem trei poziþii relative ale pãtratului faþã de cerc: q este inclus în cerc.

pentru cazul al treilea vom urmãri sã reducem problema tot la una din situaþiile 1 sau 2. împãrþind pãtratul în alte patru mai mici.x2.Primele douã cazuri se pot rezolva banal. Program Cerc_si_Patrat. begin if (sqr(x-cx)+sqr(y-cy)<=r*r) then Inside:=1 else Inside:=0 end. Evident.x1. Forward. (în program 1e-3=0. Aplicând strategia generalã Divide et Impera. analiza matematicã ne spune cã aceastã metodã nu va calcula aria exactã.r. . var cx. Totuºi.y1.001). vom calcula aria comunã dintre pãtrat ºi cerc.y1. pentru care vom rezolva aceeaºi problemã.cy. acest lucru nu este posibil.x2. decât dupã un numãr infinit de apeluri recursive. Function Arie(var x1. Function Inside(var x.y2:double):double.y2 :double. Deci va trebui sã ne mulþumim cu o aproximare a rãspunsului cãutat: aceasta se face prin renunþarea la o nouã divizare a pãtratului în cazul în care aria acestuia este mai micã decât o constantã. Deoarece “întregul este egal cu suma pãrþilor sale” aria comunã dintre pãtrat ºi cerc va fi egalã cu suma ariilor comune dintre pãtratele în care a fost împãrþit ºi acelaºi cerc.y:double):byte.

y2) end. readln(cx.y2). Problema 3 Enunþ. Process:=r end..my.my)+ Process(mx.y1)+ Inside(x2. begin r:=abs((x1-x2)*(y1-y2)).y2:double):double. r:double. my:=(y1+y2)/2.y1.my. t:=Inside(x1.y1. Se cere sã se afiºeze o acoperire completã a tablei (cu excepþia gãurii). readln(x1.x2.Cg).Function Process(var x1.x2. Function Arie(var x1.x2.x2.y2)+Process(x1. Pentru acoperirea acestei table avem la dispoziþie piese de forma: Aceste piese pot fi rotite cu 90. if (t in [1.y2) else if t<>4 then r:=0.y1.y2).y2)+Inside(x1.y1.my)+Process(mx.x2.x2. writeln(Arie(x1.y1. Pe fiecare linie se vor afiºa 6 valori separate prin spaþii: .mx.y1)+Inside(x2. var t:byte.y1.mx. begin write('Cerc (x.y2:double):double. Se dã o tablã de dimensiuni 2nx2n.x2. write('Patrat (x1. Arie:=Process(x1.y2):0:4) end.3]) and (r>1e-3) then r:=Arie(x1.r):'). Pe aceastã tablã existã o gaurã la poziþia (Lg.r). begin mx:=(x1+x2)/2.my :double.y. var mx.x2.y1. 180 sau 270° .y1.y2):').cy.

coordonatele gãurii de pe aceastã bucatã de tablã. (Lg. ca în figurã. la fiecare nivel descompunem problema curentã în 4 subprobleme. Olimpiada de Informaticã Bucureºti Faza pe sector – 1995 Rezolvare. q Urmând strategia generalã Divide et Impera. se aºeazã o piesã pe cele 3 pãtrate libere (un pãtrat fiind gaurã) ºi se revine din apel. dupã care apelãm procedura recursivã pentru fiecare din ele. Exemplu: N=3 Lg=3 Cg=6 Notãm cele 4 sferturi ale pãtratului cu cifre: .l1 c1 l2 c2 l3 c3. ºi (L2. Una dintre aceste 4 bucãþi are deja o gaurã în ea.Cg).C1) – coordonatele pãtratului din colþul stânga-sus de pe tablã. Procedura recursivã Acopera primeºte ca parametrii: q o bucatã de tablã prin (L1. În celelalte trei bucãþi facem câte o gaurã prin aºezarea unei piese la îmbinarea bucãþilor. Împãrþim tabla în 4 bucãþi egale. reprezentând coordonatele pãtratelor din care este formatã fiecare piesã aºezatã pe tablã.C2) coordonatele pãtratului din colþul dreapta-jos. Dacã se ajunge la o bucatã 2x2 se opreºte apelarea recursivã.

begin write('n= '). .Cg.n.În sfertul 3 avem deja gaura iniþialã. var Lg. :longint. procedure Citire.i L :integer. Pentru a face o gaurã ºi în celelalte trei sferturi aºezãm o piesã la îmbinarea lor. readln(n). ca mai jos: Afiºãm coordonatele piesei pe care am aºezat-o ºi apelãm recursiv pentru cele 4 subprobleme: program Luri.

Ml. Acopera(Ml+1.Mc+1).Cg). end.L2.Ml+1.Mc+1).L2. Acopera(L1.' '.Mc+1. readln(Lg.Ml.C2. Acopera(L1.Cg).Mc+1).Mc.Ml.Lg.C1.' '.' '.' '.' '.Ml.' '.' '.Mc+1.' '.Lg. Acopera(Ml+1.Ml.' '. if L2-L1>1 then begin Acopera(L1.C2:longint. if L2-L1>1 then begin Acopera(L1.Ml.Mc.Ml.' '.Ml.Ml.Mc+1) end end else begin writeln(Ml+1. procedure Acopera(L1. Acopera(Ml+1. begin Ml:=(L1+L2) div 2.L2.Mc.Ml. Cg: ').L2. var Ml.Mc).L2.Ml+1.Mc.C1.Ml.Mc).C1.C2.C2.Mc+1.Mc.Cg) end.Mc+1).Mc).C2.Mc.' '.C1.Mc.Mc+1. if L2-L1>1 then begin Acopera(L1.' '. Acopera(Ml+1. Acopera(L1.' '.C2.' '.Ml.Mc+1).Mc).Mc.Ml+1.Cg).C2.L2.Mc+1).Ml.Mc:longint.Ml+1.Ml+1.C1.Ml+1.Mc.Mc+1.Mc.Ml.Mc+1.Mc).write('Lg. if (Lg>Ml) and (Cg>Mc) then begin writeln(Ml+1. if L2-L1>1 then begin .' '.' '.Mc+1) end end else if (Lg<=Ml) and (Cg>Mc) then begin writeln(Ml. end else if (Lg>Ml) and (Cg<=Mc) then begin writeln(Ml.Ml+1.L2. Acopera(Ml+1.Mc+1.Ml+1. Acopera(Ml+1.Lg.Mc.' '.' '.C1.Cg:integer).C1.Lg.Mc.Mc+1.Ml.' '. Mc:=(C1+C2) div 2.' '.

Mc+1) end end. begin Citire.Ml+1.Mc+1.Mc). în caz contrar. de câte 2n-1x2n-1 pixeli.Lg. end.Mc.Acopera(L1.out sub forma datã în exemplul de mai jos: Fiºier de intrare:2 7271 7737 1477 7977 Fiºier de ieºire: .Ml.Mc+1. Pentru codificarea unei imagini de pe ecranul calculatorului. afiºarea arborelui se va face într-un fiºier text arbore.C1. end.Mc+1). imagine presupusã pãtraticã de dimensiuni 2nx2n. Sã considerãm acum o imagine datã într-un fiºier sub formã de matrice. Acopera(L1.L2. atunci arborele conþine un singur nod (nodul rãdãcinã) care are asociatã culoarea pixelilor respectivi. n fiind dat în prima linie din fiºier (imagine.L.C2. pânã se obþin bucãþi de aceeaºi culoare.Ml. care îi ataºeazã un arbore în felul urmãtor (imaginea fiind construitã folosind 16 culori numerotate de la 0 la 15): dacã toþi pixelii au aceeaºi culoare. fiecare element reprezentând culoarea pixelului de pe linia ºi coloana corespunzãtoare.Mc.L.C1. se poate folosi aºa-numitul cod Oliver & Wiseman. for i:=1 to n do L:=L*2. iar arborii corespunzãtori acestor patru imagini vor fi subarbori ai nodului rãdãcinã.Cg). L:=1.Ml.Cg). Se cere sã se construiascã arborele corespunzãtor imaginii date.C2. Acopera(Ml+1. Problema 4 Enunþ. imaginea se împarte în patru pãrþi egale. Nodului rãdãcinã i se va asocia valoarea -1 (fãrã semnificaþie de culoare).1.L2. iar cele patru pãrþi vor fi parcurse în sensul: 1234 Procedeul se repetã apoi separat pentru fiecare parte.Lg. Acopera(Ml+1.in).Ml+1. Imaginea este de dimensiuni 2nx2n. Acopera(1.

:array[0.k s a :text. var fil n. pentru a memora câte rânduri de linii verticale trebuie afiºate. Clara Ionescu Baraj.201] of byte.i. :integer.-1 %%% -1 % %%%7 % %%%2 % %%%7 % %%%7 % %%%7 %%% -1 % %%%7 % %%%1 % %%%3 % %%%7 %%% -1 % %%%1 % %%%4 % %%%7 % %%%9 %%% 7 prof.y1. în program este chiar ºirul s.x2. Trebuie folositã o stivã. august 1994. Procedure Print(x1.201.y2:integer). enunþ modificat Rezolvare. la momentul curent al recursivitãþii..0. Dificultatea poate consta în implementarea acestui tip de afiºare. Program Imagine.. Aplicarea principiului Divide et Impera este deja descrisã chiar în enunþ.j. . :string.

s. Print(1. if f=1 then break.s.var f.in').my). reset(fil).'.'L '). my:=(y1+y2) div 2.out').my).'+¦'). mx:=(x1+x2) div 2.s. write(fil.mx.y1. end.n. write(fil.x2.my+1. assign(fil. for i:=y1 to y2 do begin for j:=x1 to x2 do if a[i.y2). s:=s+'.my+1. close(fil). Print(x1. Print(mx+1.mx. break end. write(fil. dec(byte(s[0]). n:=1 shl n. readln(fil) end.j]<>a[y1.x1] then begin f:=1.a[y1. close(fil) end.'+¦'). begin assign(fil.'+¦').'.y1.1.x1]) else begin writeln(fil. s:=s+'.s. s:=s+'.n). if f=0 then writeln(fil.'imagine.j]).x2. for i:=1 to n do begin for j:=1 to n do read(fil.y2) end. Print(x1. begin f:=0.n). [ Capitolul 4] [ Cuprins ] [ Capitolul 6] .'.' -1').3) end. rewrite(fil).my:integer. s:=s+' '.a[i.mx.'arbore. readln(fil. Print(mx+1. write(fil.

Acest algoritm foloseºte pentru parcurgerea listei doi pointeri: P1 ºi P2. Tarjan care necesitã memorie O(1). unde n este lungimea listei. Se cere sã se spunã dacã aceastã listã cicleazã.Capitolul 6 Structuri de date Algoritmul lui Tarjan Se dã un pointer cãtre capãtul unei liste simplu înlãnþuite. P2 se “miºcã” de douã ori mai repede decât P1. folosindu-se spaþiu de memorie O(1). Cei doi pointeri “aratã” iniþial cãtre primul element din listã. O listã simplu înlãnþuitã cicleazã dacã unul din elemente pointeazã cãtre un element anterior lui din listã. .E. P1 se mutã la elementul urmãtor din listã în timp ce P2 se mutã cu douã elemente. caz în care lista nu cicleazã. Timpul de calcul este O(n). sau dacã P2 îl “ajunge din spate” pe P1. O rezolvare simplã ar fi sã marcãm elementele parcurse din listã. Dezavantajul acestei metode este cã necesitã spaþiu de memorie O(n). Algoritmul se terminã dacã P2 ajunge la sfârºitul listei (la elementul NIL). O listã care nu cicleazã se va termina cu elementul NIL. caz în care lista are un ciclu. Prezentãm un algoritm descoperit de R. Dacã întâlnim un element marcat înseamnã cã lista cicleazã. Adicã la fiecare pas.

P2:=L^. var L. Lista dublu înlãnþuitã Lista proiectatã în programul de mai jos este conceputã sã foloseascã. programatorul îºi va putea construi propriul tip de date. P1. cãutare. if (P2<>nil) and (P2<>P1) then P2:=P2^. În continuare vom prezenta o implementare bazatã pe obiecte a principalelor structuri de date studiate în manual. orice structurã creatã de utilizator. end. pentru a putea folosi obiectul.program Tarjan. anterior. P2 :nod. Anume. Pentru folosirea metodei Cauta este necesarã precizarea în . type nod=^nod. while (P2<>P1) and (P2<>nil) do begin P2:=P2^. if (P2<>P1) then P1:=P1^. if P2=nil then writeln('Lista nu cicleaza') else writeln('Lista cicleaza'). trebuie apelat constructorul Dim ce primeºte ca parametru lungimea în bytes a informaþiei din nod (aceasta include ºi cei doi pointeri). end. parcurgere (numele metodelor sunt mai mult decât asemãnãtoare). begin { Initializeaza lista } P1:=L. pentru informaþia utilã din nod. Singura restricþie este ca primele douã câmpuri din informaþia din nod sã fie cei doi pointeri pentru referinþa la nodul urmãtor respectiv. cu oricâte câmpuri ºi de orice dimensiuni. ºi va putea folosi obiectul nostru pentru a gestiona o listã cu înregistrãri de acest tip de date. Datoritã generalizãrii sunt necesare câteva “intervenþii” ale utilizatorului: în primul rând. Obiectul lista implementeazã toate operaþiile elementare definite pentru aceastã structurã de date: inserþie. ºtergere.

Dim(v:integer). pentru parcurgerea listei este necesarã precizarea unei proceduri care va fi apelatã primind ca parametru. ParcurgeInv(v:pointer). Evident utilizatorul poate crea mai multe astfel de funcþii. aceasta realizându-se printr-un apel al metodei SetFunc.urm ds comp Constructor Procedure Procedure Procedure Procedure Function Procedure end. v). SetFunc(v:pointer).b):boolean. :pointer. urm:=nil end. De asemenea. Parcurge(v:pointer). Cauta(var data) :pointer. fiecare înregistrare din listã. begin ds:=v.Dim(v:integer). prim:=nil.SetFunc(v:pointer). Constructor Lista. interface type ComparFunc ProcessProc Lista = Function (var = Procedure (var = Object prim. a. begin . Procedure Lista. prev : pointer. :ComparFunc. apelând SetFunc la fiecare schimbare a funcþiei de comparare.prealabil a unei funcþii pentru compararea a douã noduri. care sã compare înregistrãrile dupã diferite câmpuri ºi sã le foloseascã alternativ. Unit ListaDef. Sterge(v:pointer). pe rând. :integer. comp:=nil. implementation type leg = record next. Adauga(var data). end.

Cauta(var data):pointer. begin t:=urm. var t :pointer. begin if @comp=nil then begin Cauta:=nil. while t<>nil do begin ProcessProc(v)(t^). urm:=t end. if urm<>nil then leg(urm^). Procedure Lista.comp:=ComparFunc(v) end.Sterge(v:pointer).data)) do t:=pointer(t^).next:=t else prim:=t. t:=pointer(t^) end end. while t<>nil do begin ProcessProc(v)(t^).next:=nil.prev:=urm. begin .ds). Cauta:=t end.prev end end.Parcurge(v:pointer). Procedure Lista. while (t<>nil) and (not Comp(t^. Procedure Lista. begin getmem(t. Move(data. leg(t^). Procedure Lista. begin t:=prim.t^. var t:pointer.ParcurgeInv(v:pointer). var t:pointer. t:=prim.Adauga(var data). Function Lista. t:=leg(t^). var t:pointer. exit end. leg(t^).ds).

Dim(sizeof(Telefon)).prev:=leg(v^).next else prim:=leg(v^).prev<>nil then leg(leg(v^).nume=''. end.next. Întâi vom defini structura de date necesarã. :Lista. dispose(v) end. :string[20]. :Telefon. :ref.prev Nume numar end.prev else urm :=leg(v^). write('Numar:'). Sã dãm un exemplu: crearea ºi actualizarea unei agende telefonice. .Adauga(tmp) until tmp. repeat write('Nume:').if leg(v^). :longint. Agenda.next^).nume).next<>nil then leg(leg(v^). =record next.next:=leg(v^).numar). var Agenda Tmp Pentru crearea listei este necesar apelul constructorului Dim ºi apoi inserarea succesivã în listã a înregistrãrilor: Begin Agenda.prev^). end. type ref Telefon =^telefon. if leg(v^).prev. readln(tmp. readln(tmp. uses ListaDef.

Parcurge(@Print). { . } end. Utilizatorul îºi va defini o procedurã ce se va ocupa cu procesarea fiecãrui element din listã (în cazul nostru va face doar o afiºare) ºi apoi o va transmite metodei Parcurge: Procedure Print(v:telefon).nume=b. . p^. tmp. . begin { . begin { .Numar:=89 { ºi aici noul numãr { . . Vom fi nevoiþi sã cãutãm întregistrarea corespunzãtoare în listã ºi sã modificãm unul dintre câmpuri.Nume:25.Acum sã vedem cum se realizeazã implementarea unei parcurgeri a listei.b:telefon):boolean.Cauta(tmp). . begin ComparNume:=(a. . . } end. Sã presupunem cã unul dintre cunoscuþii noºtri tocmai ºi-a schimbat numãrul de telefon.nume:='Cunoscut'.numar:10) end.SetFunc(@ComparNume).v. . begin writeln(v. } } Un mare avantaj pe care-l prezintã aceastã implementare este independenþa de structura folositã .nume) end. { aici pui numele pe care-l cauþi p:=Agenda. } Agenda.' '. . funcþia construitã de utilizator pentru a compara douã înregistrãri va verifica doar dacã cele douã nume conincid: Var p :ref. Deoarece cãutarea se face dupã numele respectivului. Function ComparNume(a. } Agenda.

Deoarece informaþia utilã propriu-zisã este de dimensiuni mult mai mici decât ale matricei.4). Obiectul MatriceRara prezentat mai jos. realizeazã o implementare bazatã pe liste simplu înlãnþuite: pentru fiecare rând al matricei se þine o listã a valorilor nenule pe care le conþine.2).7) Cele douã metode care implementeazã operaþiile fundamentale pentru matrice (Citeste ºi Scrie) .(8.1).(8.4). aceasta se poate rezolva fãrã a fi nevoie de alte modificãri în sursã: se adaugã pur ºi simplu în definiþie noul câmp ºi se scrie codul necesar pentru procesarea lui.5) (3. în care majoritatea elementelor au valoarea 0.(5.(7.9).pentru informaþia dintr-o înregistrare.6) (6. în general de dimensiuni mari.4) (1. dupã ce aceastã aplicaþie a fost în totalitate scrisã. de cele mai multe ori este ineficient sã folosim alocarea staticã (se consumã prea multã memorie).(4. dacã apare nevoia modificãrii structurii telefon (de exemplu adãugarea unui câmp pentru memorarea adresei). Exemplu: matricea 0 0 0 0 2 1 0 0 0 0 0 0 0 4 0 9 0 0 0 0 0 0 0 0 7 0 0 5 0 0 0 0 0 4 0 6 0 0 4 0 va fi reþinutã astfel (prima valoare din fiecare pereche reprezintã coloana pe care se aflã valoarea ºi cea de-a doua valoarea propriu-zisã) : 1: 2: 3: 4: 5: (2. Matricea rarã O matrice rarã este o matrice. Astfel.

begin fillchar(l.info else Citeste:=0 end. var t. : integer.Scrie (x.y:integer.oferã utilizatorului posibilitatea de a accesa matricea ca ºi cum ar fi fost alocatã static. : ref MatriceRara = Object l : array[1. Constructor Init. : real.sizeof(l). realizând: inserarea unui nou element în listã pe poziþia corespunzãtoare (dacã elementul se gãseºte deja în listã este actualizat). Procedure Scrie (x.y:integer):real. ºtergerea din listã a elementelor care primesc valoarea zero.0) end. end.v:real).c>nil) and (t^. while (t<>nil) and (t^. Function Citeste(x.y:integer.c>nil) and (t^. var t:ref. = record c info next end. implementation Constructor MatriceRara. begin t:=l[x].y:integer):real.d :ref. Metoda Scrie are în sarcinã actualizarea listelor.Init.p. p:=nil.v:real).c=y) then . begin t:=L[x].c=y) then Citeste:=t^. interface type ref nod = ^nod. Function MatriceRara. unit MatrDef. while (t<>nil) and (t^.Citeste(x. Procedure MatriceRara..10000] of ref.

componentele conexe ale unui graf dat prin matricea de adiacenþã. end. Sã se determine.next:=t.next:=d end end.c:=y. d^.info:=v. [ Capitolul 5] [ Cuprins ] [ Capitolul 7] . d^. if p=nil then L[x]:=d else p^. 2. d^.next:=t^. dispose(t) end else begin if v=0 then exit.info:=v else begin if p=nil then L[x]:=t^. Sã se realizeze obiectele Stiva ºi Coada prin moºtenirea obiectului Lista.if v<>0 then t^. folosind obiectul MatriceRara. Probleme propuse: 1. new(d).next.next else p^.

Exemplu: MONEZI. ªeful depozitului cunoaºte numãrul de monezi din fiecare sac ºi ar vrea sã modifice conþinutul sacilor.IN MONEZI. Date de ieºire: Pe fiecare linie a fiºierului text MONEZI. reprezentând numerele de monezi din fiecare sac (numãrul total de monezi din toþi sacii≤ 1.000. reprezentând numãrul de saci. în fiecare sac sã fie acelaºi numãr de monezi. prin mutãri de monezi dintrun sac în altul. Ajutaþi ºeful depozitului sã obþinã acelaºi numãr de monezi în fiecare sac.OUT .000. prin efectuarea unui numãr minim de mutãri.000). Într-un depozit al monetãriei statului sosesc n saci cu monezi.OUT se vor scrie triplete de numere întregi a b c unde: q a reprezintã numãrul de ordine al sacului din care se mutã monezi. Date de intrare: În fiºierul text MONEZI. c reprezintã numãrul de monezi care se mutã din sacul a în sacul b. q q Observaþie: În cazul în care problema nu are soluþie.IN se va scrie pe prima linie un numãr întreg n (2≤ n≤ 2000). b reprezintã numãrul de ordine al sacului în care se mutã monezi. se va scrie în fiºier cuvântul: ‘NU’.Capitolul 7 Tehnica Greedy Problema 1 Enunþ. Pe urmãtoarele n linii sunt scrise numere întregi. astfel încât în final.

dacã în sacul din care am pus monede au rãmas Med monede. reprezentând indicele sacului în ordinea iniþialã ºi numãrul de monezi din sac. cât este nevoie pentru a rãmâne Med monede în acesta din urmã se afiºeazã mutarea fãcutã se eliminã din listã sacul în care am pus monede ºi.3 35 48 37 2 215 233 Timp maxim de execuþie: 8 secunde. adicã soluþia datã de el nu este întotdeauna cea optimã. Altfel. Pentru a reþine numãrul de monezi din fiecare sac. Olimpiada Naþionalã de Informaticã 1998 Clasa a X-a Rezolvare. folosim o listã dublu înlãnþuitã. atunci problema nu are soluþie. unde fiecare nod conþine. se eliminã ºi acesta r r r r Trebuie precizat cã algoritmul este euristic. {$F+} (* Aceastã directivã de compilare este necesarã pentru transmiterea ca parameteri a adreselor de proceduri *) . pe lângã pointerii necesari. atâta timp cât lista nu este vidã. calculãm media numãrului de monezi din saci în variabila Med. Iatã algoritmul folosit pentru rezolvare: r se eliminã din listã sacii care au deja Med monezi apoi. se mutã atâtea monezi din sacul cel mai plin în cel mai gol. adicã numãrul de monezi pe care va trebui sã îl aibã fiecare sac în final. Pentru implementare folosim obiectul Lista definit în capitolul “Structuri de date”. Dacã suma numãrului de monezi din toþi sacii nu este divizibilã cu numãrul de saci. doi întregi.

L. var tmp: Nod. .n). halt(1) end else Med:=suma div n. if suma mod n<>0 then begin writeln(outp.'monezi.program Suma. prev:ref. for i:=1 to n do begin tmp. mon: integer end.i:=i. procedure Del(var N:nod). readln(f. var L outp Med Min. type ref=^Nod. :Lista. assign(f.Adauga(tmp) end. Nod=record next. reset(f).tmp. readln(f. n. begin L. f:text.Dim(sizeof(Nod)).mon). suma :integer. begin if N. end. i. uses ListaDef.mon=Med then L.in').Sterge(@N). :ref.mon. close(outp). :text. close(f). i.'NU'). Max procedure Citire. :integer. suma:=suma+tmp. suma:=0.

if Max^. dar este foarte eficient.monMax^.mon=Med then L. Min^. close(outp) end.mon then Max:=@N.Sterge(Min).end. Citire.' '. L. Algoritmul folosit nu conduce tot timpul la soluþia optimã.'monezi. L.new(Min). Max^.out').Sterge(Max). rewrite(outp). L. begin assign(outp.prim<>nil do begin new(Max). while L.mon). Pentru plata unei sume S avem la dispoziþie n tipuri de monede.mon-(Med-Min^. printre care ºi moneda cu valoarea 1. Putem gãsi uºor un .i.i. end.mon). Problema 2 Enunþ. Exemplu: S=13 n=3 M1=7 M2=3 M3=1 Se foloseºte moneda M1 de S div M1=13 div 7=1 ori.Max^.Parcurge(@Del).mon:=0. begin if N. Rezolvare. Se ia moneda cu valoarea cea mai mare ºi se foloseºte de câte ori este posibil. Algoritmul folosit este foarte simplu.Min^.mon:=maxint. writeln(outp.mon:=Max^. procedure MinMax(var N:nod). end. Sã se gãseascã o modalitate de platã a sumei cu un numãr minim de monede. Apoi M2 de S div M2=6 div 2=3 ori.Med-Min^. Apoi moneda cu valoarea imediat urmãtoare ºi aºa mai departe.Parcurge(@MinMax).' '. Max^. Am gãsit o modalitate de platã cu 4 monede: S=13=7+2+2+2.

end.contraexemplu: S=17 n=3 M1=7 M2=5 M3=1 . begin . repeat while M[i] > x do i := i + 1. procedure QuickSort(li. :integer. x := M[(li+ls) div 2]. Complexitatea algoritmului este O(n⋅ lg n+n)=O(n⋅ lg n). readln(M[i]) end end. Programul constã dintr-o sortare ºi o parcurgere a vectorului de monede. var i. end. soluþia algoritmului va avea 5 monede: S=17=7+7+1 +1+1. write('n= '). M[i] := M[j]. j := j .100] of integer. var M.']= '). i := i + 1. y: integer. i :array [1. begin i := li. program Plata_sumei. if i < ls then QuickSort(i. if i <= j then begin y := M[i]. procedure Citire. while x > M[j] do j := j . for i:=1 to n do begin write('M['. n.. M[j] := y. j. readln(S). until i > j. ls: integer). în timp ce soluþia optimã are numai 3 monede: S=17=7+5+5. F S.1.1.i. readln(n). x. if li < j then QuickSort(li. j := ls. begin write('S= '). j). var i:integer. ls).

QuickSort(1.' monede de '.M[i]. end. writeln('Am folosit: '). S:=S-M[i]*F[i] end.Citire. Vom face întâi observaþia cã nxn trebuie sã fie multiplu de 4 pentru cã toate cele 7 piese sunt formate din câte 4 pãtrãþele. i:=0. Gheorghe Petrov – Universitatea Timiºoara Concursul Naþional de Informaticã Lugoj 1998 Rezolvare.' lei'). Problema 3 Enunþ.2*n. F[i]:=S div M[i]. for i:=1 to n do writeln(F[i].n). Prof. Iatã o aºezare a pieselor prin care putem umple perfect un pãtrat de 6x6.0). Rezultã cã pentru a putea face acoperirea n trebuie sã fie par. folosind cel puþin o piesã de fiecare tip: . while S>0 do begin i:=i+1. Sã se umple perfect o tablã de dimensiuni nxn (n≥ 6) cu piese de urmãtoarele forme: Este obligatoriu sã se foloseascã cel puþin o piesã din fiecare tip. FillChar(F.

dupã cum se vede în figurã: Aceste douã dreptunghiuri.. ne rãmân pe tablã neumplute douã dreptunghiuri de dimensiuni 6x(n-6).4. const Colt:array [1.7).. având amândouã dimensiunile pare.7).1.6. respectiv (n-6)xn.4. Apoi.4.4. program TETRIS.4.1. .1.6] of integer= ((2.7). pot fi umplute numai cu pãtrate (piesa 3). (2.Se aºeazã acest pãtrat 6x6 în colþul din stânga-sus al tablei. (2.2.4.6.1.4.

j]:=Colt[i. 3. Se dau n ºi k.2.3. readln(n). begin write('n= '). suma elementelor de pe coloana k sã fie minimã.5.2.100] of integer.100.3).' '). Rezolvare.1. :array [1.3. for i:=7 to n do for j:=1 to n do begin T[i.2. j T :integer. for i:=1 to n do begin for j:=1 to n do write(T[i.1. Sã se construiascã un tablou nxn care îndeplineºte simultan condiþiile: 1.. (5. end.6. naturale.j]:=3. for i:=1 to 6 do for j:=1 to 6 do T[i. i. var n.5.4.2.5. de la stânga la dreapta. (6.j]. k≤ n. Fiecare din elementele de pe coloana k trebuie sã fie mai .j]..3)). pe fiecare linie numerele sunt aºezate în ordine crescãtoare. conþine toate numerele de la 1 la n2 o singurã datã. Problema 4 Enunþ.(6. writeln end end.i]:=3. T[j. 2.7).

Sã se construiascã. for i:=1 to n do begin for j:=1 to n do write(T[i.. Rezultã. j :array [1.1.k]. ... for i:=1 to n do for j:=1 to k do T[i.Pj) sã fie laturã a unui poligon convex trebuie ca toate celelalte puncte sã se gãseascã într-unul din semiplanele determinate de dreapta-suport a segmentului. Se dau n puncte în plan.k). writeln end end.n.100. k: ').k] . begin write('n.1.j]:=(i-1)*k+j-k+T[n. Pentru ca un segment (Pi. Numerele de la 1 la nk se aºeazã în A[1. cã suma minimã a elementelor de pe coloana k este ordine. în program Tablou.' ').mare decât cele k-1 elemente de pe linia sa. Rezolvare.j]. readln(n.j]:=(i-1)*k+j. k.. dacã este posibil. Problema 5 Enunþ. :integer.100] of integer. for i:=1 to n do for j:=k+1 to n do T[i. i. . var T n. un poligon convex care sã aibã ca vârfuri aceste puncte.

dacã: q Puse[j]=False. begin assign(f. La fiecare pas al algoritmului alegem un vârf al poligonului j.. unde Latura(i.100.'input. procedure Citire.txt'). adicã acest vârf nu trebuie sã fi fost deja ales Latura(P[i-1]. :set of 1. var V n. var f:text. readln(f. :integer. :array [1. având complexitatea O(n⋅ lg n). reset(f). i:integer. rezultã cã nu se poate forma un poligon convex cu punctele date. Notã: Acest algoritm are complexitatea O(n3).. O metodã mai eficientã de rezolvare ar folosi un algoritm de determinare a înfãºurãtorii convexe.Folosim un vector boolean Puse.100] of Punct. :boolean. ºi False. altfel. unde Puse[i] este True dacã am ales deja vârful I. . program Poligon. q Dacã nu se gãseºte un astfel de punct. j Puse P Convex :array [1.. type Punct=record X.Y:real end. i.j])=True.n).100] of integer.j) este procedura care verificã dacã toate celelalte puncte sunt într-unul din semiplane cu ajutorul ecuaþiei dreptei PiPj ºi P[i-1] este ultimul punct ales.

.V[i].X-P1.Y)-(P3. while (i>p1) and (i<>p2) then if pr then begin s:=Sgn(V[p1]. P[i]:=j. i:=1.for i:=1 to n do readln(f. function Latura(p1. while (in) and not Convex do begin j:=j+1. Latura:=bun. begin pr:=true.X). end.P3:Punct):integer.X. bun:=true.V[i]. bun. begin Citire.V[p2]. end. end.Y-P1. Puse:=Puse+[j].X)*(P2.V[i]).pr:boolean. pr:=false end else if Sgn(V[p1].i:integer. var s:real. Puse:=[1].p2:integer):boolean. function Sgn(P1.j) then begin Convex:=true.Y-P1. i:=0. var s.P2. P[1]:=1.X-P1. if not (j in Puse) and Latura(P[i-1].Y)*(P2. begin s:=(P3. close(f). end.Y). Convex:=true. if s<0 then Sgn:=-1 else if s>0 then Sgn:=1 else Sgn:=0.V[p2].V[i])<>s then bun:=false.

for i:=1 to n do write(' '. end. Demonstraþia acestei teoreme poate fi gãsitã în manualul de Algebrã.P[i]). dar nu neapãrat ºi suficiente pentru ca r sã fie rãdãcinã a polinomului. Sã se afle toate rãdãcinile raþionale ale polinomului. Aceastã verificare se face în procedura Rãdãcinã(p.end. writeln. if Convex then begin write('Poligonul:').'). Se dã un polinom P(X) cu coeficienþi întregi. . Sã observãm cã cele douã condiþii de mai sus. Dacã numere prime între ele) este o rãdãcinã raþionalã a lui f atunci: (p. 2. q 1. end else writeln('Nu se poate forma un poligon convex. q divide coeficientul termenului de grad maxim an. dupã determinarea numerelor raþionale care satisfac aceste douã condiþii trebuie sã ºi verificãm dacã chiar sunt rãdãcini ale polinomului. end.q) care practic calculeazã . Rezolvarea se bazeazã pe urmãtoarea teoremã: Fie f=a0+a1X+…+anXn un polinom de gradul n (n≥ 1) cu coeficienþi întregi. sunt necesare. Problema 6 Enunþ. end. clasa a X-a. p divide termenul liber a0. De aceea. Rezolvare.

function Radacina(p. end. :integer. begin write('n= '). var suma. end. suma:=a[0]. for i:=1 to n do begin X:=X*(p/q).v:integer):integer. i:integer. q. p:=1. suma:=suma+a[i]*X. var i:integer. end. readln(n). X :real. n :array [0.program Polinom. for i:=0 to n do begin write('a'. function cmmdc(u. .i.q:integer):boolean. end..'= '). while p*pa[n] do begin repeat q:=q+1. begin Citire.u mod v).100] of integer. until a[n] mod q=0. readln(a[i]). begin X:=1. end. var a p. procedure Citire. begin if v=0 then cmmdc:=u else cmmdc:=cmmdc(v. if abs(suma)<1e-5 then Radacina:=true else Radacina:=false.

q). [ Capitolul 6] [ Cuprins ] [ Capitolul 8] .q)=1) and Radacina(p. end.if (cmmdc(p. end. end.q) then writeln(p.'/'.

Capitolul 8 Programare dinamicã Problema 1 Enunþ. C[1]:=2..100] of comp.c sau d. ºi cu C[i] numãrul de cuvinte care se terminã în c sau d. .b. Vom nota cu A[i] numãrul de cuvinte de lungime i care se terminã în a sau b. Deducem urmãtoarele relaþii de recurenþã: A[N] = A[N-1] + 2 * C[N-1] C[N] = 2 * A[N-1] + 2 * C[N-1] De aici pânã la implementare mai e doar un pas … Program Cuvinte.d} astfel încât a ºi b sã nu se afle pe poziþii alãturate ? Ioan Tomescu – Probleme de combinatoricã ºi teoria grafurilor Rezolvare. var N. dacã cuvântul se terminã cu a litera b.c sau d. readln(N). dacã cuvântul se terminã cu b orice literã dacã cuvântul se terminã cu c sau d q q Se observã cã numãrul de cuvinte obþinute prin adãugarea unei litere variazã în funcþie de litera adãugatã dar ºi de ultima literã a cuvântului de la care se porneºte. A[1]:=2. :array[0.c.C begin write('N='). care respectã condiþiile problemei. Un cuvânt valid de lungime N se poate obþine adãugând la sfârºitul celui pe care-l avem deja: q litera a.i A. Câte cuvinte de lungime N se pot forma cu litere din alfabetul {a. :integer. Sã considerãm cã am construit deja un cuvânt de lungime N-1.

unde n reprezintã numãrul de partide ce trebuie câºtigate pentru a deveni campioanã (n≤45). Titlul de "Campioanã Absolutã" al unei discipline sportive este disputat de douã echipe cu forþã de joc egalã.0000 0.7500 . i numãrul de partide câºtigate de echipa 1. Fiºierul de intrare conþine pe fiecare linie câte un set de date sub formã de triplete "n i j". Problema 2 Enunþ. iar j numãrul de partide câºtigate de echipa 2. Analiºtii sportivi sunt interesaþi în estimarea ºansei pe care o are una dintre echipe la un moment dat oarecare al turneului. În orice partidã. echipa care câºtigã prima. Exemplu: Daca fiºierul de intrare are urmãtorul conþinut: 3 3 5 2 3 1 3 1 1 3 3 0 Ieºirea trebuie sã arate ca mai jos: 1. sã se calculeze probabilitatea ca echipa 1 sã câºtige turneul ºi sã devinã campioanã. n partide.for i:=2 to N do begin C[i]:=2*C[i-1]+2*A[i-1]. cu patru zecimale exacte. Pentru fiecare set de date trebuie afiºatã probabilitatea ca echipa 1 sã devinã campioanã.0000 0.5000 0. federaþia de specialitate a hotãrât organizarea unui turneu în care între cele douã echipe sã aibã loc mai multe partide ºi sã fie declaratã campioanã. ºi anume numarul i de partide câºtigate de prima echipã ºi numãrul j de partide câºtigate de a doua echipã. Pentru desemnarea campioanei. Echipele sunt desemnate cu numerele 1 ºi 2. de a deveni campioanã. cele douã echipe au ºanse egale de câºtig indiferent de rezultatele din partidele anterioare. A[i]:=A[i-1] +2*C[i-1] end. ªtiindu-se situaþia turneului la un moment dat. writeln(A[N]+C[N]:20:0) end. Orice partidã se terminã cu victoria uneia dintre echipe.

3.i.n-j]:4:4) end. Mai multor elevi li se cere sã punã într-o anumitã ordine un numãr de n<200 cuvinte formate numai din litere mici.j-1) dacã echipa unu pierde. Deci probabilitatea ca echipa unu sã câºtige turneul din nodul (i..x.'campion. Program Probabil.i. for i:=1 to 45 do for j:=1 to 45 do p[i. ªtiind cã echipele au ºanse egale rezultã cã în oricare fiu se poate ajunge cu o probabilitate de 0. writeln(P[n-i. mai fiindu-i necesare i-1 victorii. Problema 3 Enunþ. :array[0. dacã echipa unu câºtigã meciul.y p fil begin for i:=1 to 45 do p[0.in').j). Secvenþei de ni-j meciuri îi vom asocia un arbore binar strict. while not eof(fil) do begin readln(fil.j]:=(p[i-1. :integer.j]+p[i. astfel: fiecare nod va reprezenta numãrul de meciuri necesare fiecãrei echipe pentru a câºtiga turneul..0. Exemplu: platon kant marx stalin havel Logica succesiunii constã în faptul cã fiecare cuvânt poate fi definit folosind numai cuvintele anterioare .j).n.i]:=1.j-1])/2.n corespunzãtoare numãrului de cuvinte aºezate într-o succesiune corectã.0]:=0.5.45.45] of real. :text. var n. for i:=1 to 45 do p[i. Sarcina profesorului constã în a acorda fiecãrui elev una din notele 1..j) va fi egalã cu suma probabilitãþilor din nodurile fii înmulþitã cu 0.5. ordinea exprimã faptul cã aceste cuvinte se succed dupã o anumitã logicã. assign(fil.. echipa 1 are nevoie de n-i victorii din cele n-i-j meciuri rãmase.j) va avea ca descendenþi nodul (i-1. Fiecare elev îi transmite profesorului succesiunea de cuvinte care i se pare logicã.Rezolvare. ºi nodul (i. Un nod (i.2. close(Fil) end. reset(fil). . Pentru a câºtiga.j.

:string. Adicã un subºir crescãtor maximal. Lungimea subºirului crescãtor maximal din acest ºir. Program Filozofi. Pentru fiecare ºir de examinat vom construi ºirul corespunzãtor al numerelor asociate cuvintelor.200] of string[20].2. Pentru a reduce problema exact la forma studiatã..Pentru exemplul 1 avem urmãtoarele note : marx stalin kant platon havel 3 havel marx stalin kant platon 2 havel stalin marx kant platon 1 Fiºierul de intrare platon. vom numerota cuvintele în ordinea corectã cu numerele de la 1 la N. pãstrând relaþia de ordine între ele. q Programul va afiºa pe ecran notele acordate de profesor. îi vom asocia ºirul 5. acesta va fi reþinut în vectorul A. “Numãrul de cuvinte aºezate într-o succesiune corectã” este echivalent cu numãrul maxim de cuvinte care pot fi extrase din ºir. deci nota corespunzãtoare va fi 2. reprezintã întocmai nota acordatã de profesor.4..i.in va avea urmãtoarea formã: q Pe prima linie apar cuvintele în succesiunea lor corectã.3. despãrþite între ele prin cel puþin un blank.V N. :char. Deci fiecãrui cuvânt îi este asociat în mod unic un numãr. :array[1. :array[0. Subºirul crescãtor maximal care se poate forma este 3. întocmai problema studiatã în manual. . Rezolvare. Exemplu: platon kant marx stalin havel 1 2 3 4 5 Pentru a evalua succesiunea havel marx stalin kant platon. var fil Nume A. :integer. ºi care se gãsesc în aceeaºi succesiune ºi în ºirul corect. Pe urmãtoarele linii apar soluþiile propuse de elevi.j c tmp :text.4.200] of integer.1.

readln(fil). begin assign(fil. while not eoln(fil) do begin inc(N). reset(fil).c). var r:string. j:=1. while tmp<>Nume[j] do inc(j). while c=' ' do read(fil.c) end. close(fil) end. j:=1. A[i]:=j end. writeln(j) end. Nume[N]:=GetWord end. while not eof(fil) do begin for i:=1 to N do begin tmp:=GetWord.in'). GetWord:=r end. for i:=2 to N do for j:=1 to i-1 do if (A[j]<A[i]) and (V[j]+1>V[i]) then V[i]:=V[j]+1. for i:=1 to N do V[i]:=1. begin r:=''.'platon.Function GetWord:string. while (c<>' ') and (not eoln(fil)) do begin r:=r+c. if c<>' ' then r:=r+c. read(fil. Problema 4 . readln(fil). for i:=1 to N do if V[i]>j then j:=V[i]. N:=0. read(fil.c).

Sã presupunem cã ºtim deja cel mai lung subºir comun al celor douã ºiruri de lungime N ºi respectiv M.100] of integer.9. :array[0.2 Orice element am adãuga. dacã se adaugã acelaºi element la sfârºitul ambelor ºiruri.4.j]. Dându-se douã ºiruri X1.3. obþinând astfel douã ºiruri ce au acelaºi ultim element.…. Dacã adãugãm un 8 în finalul ambelor ºiruri. var X.100. la oricare ºir. De exemplu..3. Rezolvare. este evident cã lungimea subºirului comun va creºte cu 1 (elementul adãugat fãcând parte din cele douã ºiruri va apãrea ºi în subºir comun). A[i. obþinem urmãtoarea relaþie de recurenþã: A[i. În cazul în care se adaugã un singur element la sfârºitul unuia din ºiruri se poate întâmpla.7. Întotdeauna. Problema constã în a gãsi un subºir de lungime maximã a douã ºiruri date.j] lungimea celui mai lung subºir comun care se poate forma folosind doar primele i elemente ale primului ºir ºi primele j elemente ale celui de-al doilea ºir.3. De exemplu: 2. dacã avem ºirurile 4.6 cel mai lung subºir comun va fi 4.1.2.9.2. Un subºir al unui ºir X1.3 ºi 1. situaþia din exemplul 1 se poate reduce la cazul în care am adãugat acelaºi element la ambele ºiruri: elementele situate dupã ultima valoare din subºirul comun pot fi ignorate (în exemplu doar 7 este în aceasã situaþie).Y2.….4 cu subºirul comun 1.2 cu subºirul comun 1.M. Elementele care se ºterg nu trebuie sã fie neapãrat pe poziþii consecutive în ºir.j-1] Max ( A[i-1. subºirul comun va pãstra aceeaºi lungime.3 2) 1.100] of integer.k :array[1.7 ºi 1.. noua soluþie va fi 4.j. :integer.4.2 ºi 1.2.7.1 este un subºir al ºirului 2. În cel mai simplu caz.Xn ºi Y1.Y A N.2.2 Dacã adãugãm un 3 la ºirul al doilea noul subºir comun va deveni 1.i. ca lungimea subºirului comun sã creascã cu 1: Exemple: 1) 1.….Xn este un ºir care se obþine ºtergând zero sau mai multe elemente din ºirul iniþial.4. Sã studiem ce se întâmplã cu acesta în cazul în care se mai adaugã elemente celor douã ºiruri.Ym un subºir comun a celor douã ºiruri este un ºir care este subºir ºi pentru primul ºir ºi pentru al doilea.9.. .5.X2. altfel Program SubsirComun.8.X2.Enunþ.1 el obþinându-se prin ºtergerea lui 4 ºi a primei apariþii a lui 1 în ºirul iniþial.9.0.j] = = 1 + A[i-1.j-1] ) dacã X[i]=Y[j].2. Dacã notãm cu A[i. sau nu.2.

j]:=A[i-1. Se considerã mulþimea formatã din numerele naturale mai mici sau egale decât N (N≤ 39). readln(M).j-1). Fiºierul de intrare input.j-1]). Procedure Print(i.j-1]>0 then Print(i-1. Se cere sã se calculeze numãrul de astfel de partiþionãri ºtiind cã nu are importanþã ordinea mulþimilor dintr-o soluþie ({1. write(X[i].M]).j:integer).sizeof(A). write('M='). for i:=1 to M do begin write('Y['.' ') end.m2:integer):integer.i. Print(N.Function Max(m1. fillchar(A.2} ºi {3} reprezintã aceeaºi soluþie ca {3} ºi {1.txt conþine pe prima linie numãrul N. begin while X[i]<>Y[j] do if A[i-1.A[i. De exemplu. . begin if m1>m2 then Max:=m1 else Max:=m2 end.2} ºi {3}.M). mulþimea {1.j]>A[i.2}).i.j-1]+1 else A[i.j].']='). Problema 5 Enunþ.']='). readln(Y[i]) end. writeln end.0). for i:=1 to N do for j:=1 to M do if X[i]=Y[j] then A[i. begin write('N=').j-1] then dec(i) else dec(j).2. if A[i-1.3} se poate împãrþi în {1. readln(X[i]) end. readln(N). writeln(A[N.j]:=Max(A[i-1. dacã N=3. O astfel de mulþime se poate partiþiona în douã submulþimi care au aceeaºi sumã. for i:=1 to N do begin write('X['.

readln(fil.0). sunt patru soluþii: {1. close(fil). dacã pânã la etapa curentã avem S[5]=3 ºi S[9]=8.5.4. Exemplu: Pentru N=7. Spring Open 1998 Rezolvare. Vectorul S este iniþializat cu 0.5.3.N).2.7} ºi {1.6} USACO. De exemplu.txt ºi va consta în numãrul de partiþionãri. doar S[0] va primi valoarea 1. reset(fil).TXT').. Vectorul va fi completat progresiv.k S begin fillchar(S. iar i=4 (am gãsit deja 3 moduri de a forma suma 5 ºi 8 de a forma suma 9) atunci putem sã adãugãm numãrul 4 la oricare din cele 3 soluþii gãsite pentru suma 5. ºi vom obþine încã 3 soluþii cu suma 9.3.4. deci N*(N-1)/4. var fil N. Problema este analogã cu problema rucsacului. assign(fil.'INPUT.TXT'). :text. assign(fil. Vom þine un vector S[i] de dimensiune egalã cu suma maximã care se poate obþine într-o submulþime adicã jumãtate din suma primelor N numere naturale.i. :integer. i care va spori numãrul de soluþii obþinute folosindu-le doar pe primele i-1.2.7} ºi {1. .'OUTPUT.Ieºirea se va face în output. este imposibil sã formãm douã submulþimi cu sume egale. Program Submultimi.4. la fiecare etapã considerându-se un nou numãr. :array[0.5. S[i] va reprezenta numãrul de moduri în care se poate obþine suma i (trebuie remarcat cã în situaþia în care suma primelor N numere este imparã. Deci numãrul de modalitãþi de a forma suma 9 va creºte exact cu numãrul de sume gãsite pentru 5: S[9]=S[9]+S[5].6.7} ºi {3.6} {1.7} ºi {2.4.j.6} {3.1000] of comp.sizeof(S). deci numãrul de soluþii va fi 0).5} {2.

Pentru o mulþime datã de K numere prime S={p1. Problema cere sã se afle al N-lea numãr subjugat HN pentru o mulþime datã S. p1p2p3 (când K≥3). halt end. p1p2. Problema 6 Enunþ. HN. Aceasta este mulþimea "numerelor subjugate" pentru mulþimea de intrare S. . close(fil). Ieºirea se va face pe ecran ºi va consta într-un singur numãr..000.p2.S[k div 2]/2:0:0).. close(fil) end. 1≤ N≤10.in) conþine douã linii: K N p1 p2 . Ea conþine de exemplu printre altele numerele p1.. }.. k:=(N*N+N) div 2. writeln(fil. for i:=1 to N do for j:=k-i downto 0 do if S[j]<>0 then S[j+i]:=S[j+i]+S[j]. Atenþie: prin definiþie 1 este declarat un numãr nesubjugat. sã considerãm mulþimea tuturor numerelor ale cãror factori primi formeazã o submulþime a lui S. if odd(k) then begin writeln(fil. pK}. Vom considera aceastã mulþime ordonatã crescator: {H1.rewrite(fil). pK Limite: 1≤ K≤ 100. p1p1. Fiºierul de intrare (humble.H2.. Exemplu: Pentru intrarea: .0). S[0]:=1.

..100] of longint. deci.N).IN'). Vom genera în ordine primele N numere subjugate. Astfel vom obþine încã i⋅ k numere subjugate. Pentru a optimiza acest proces de calculare a urmãtorului numãr subjugat.10000] of longint. readln(fil. close(fil). dintre acestea cel mult unul poate candida la poziþia V[i+1].P[i]). .'HUMBLE. Aprilie 1997 Rezolvare. for j:=1 to K do while V[pi[j]]*P[j]<=V[i] do inc(pi[j]) end. :array[1. putem sã înmulþim fiecare din cele i numere subjugate cu fiecare din cele k numere prime. for i:=1 to K do pi[i]:=0. v[0]:=1. :text. :array[0. Acestea vor fi reþinute în vectorul V. reset(fil). for i:=1 to N do begin min:=maxlongint. mai mare decât V[i]. Pornind de la cele pe care le cunoaºtem. var fil N.4 19 2 3 5 7 Ieºirea este:27 USA Computing Olympiad.100] of integer. acest vector va trebui actualizat la fiecare selecþie a unui nou numãr subjugat. tot un ºir de valori sortate crescãtor. :longint. Sã presupunem cã cunoaºtem deja primele i numere subjugate ºi ne intereseazã sã-l aflãm pe cel cu numãrul de ordine i+1. ºi în mod sigur cel de-al i+1 – lea (pe care-l cãutãm) se va gãsi printre ele. pj vom obþine. Evident. for i:=1 to K do read(fil.K.min p V pi begin assign(fil. V[i]:=min. dacã le vom înmulþi pe toate cu acelaºi numãr prim.f.j. :array[1.K. for j:=1 to K do if V[pi[j]]*P[j] < min then Min:=V[pi[j]]*P[j]. Deci vom mai folosi încã un vector pi[j] care va memora indicele celui mai mic numãr subjugat. Deci dintre toate numerele generate în ultima etapã îl vom pãstra pe cel mai mic dintre ele care este mai mare decât V[i] (al i-lea numãr subjugat). Program Numere_subjugate.i. vom þine seama de urmãtoarea observaþie: ºtim cã numerele din V sunt sortate ºi.. ºi anume cel mai mic. care înmulþit cu pj va da o valoare mai mare decât V[i].

. 1 ≤ i ≤ n numãrul de ordine al stãrilor finale.pe primlele n linii pentru c evoluþia prin cele n stãri. un alfabet. ºamd. 0 < l ≤ n.txt..txt conþine pe m linii cuvinte:cuvânt_1cuvânt_2. q q q Un ºir de caractere formeazã un cuvânt..i2 s . Problema 7 Enunþ. s 2.1 2..in*r numãrul de stãri.txt). s n*r. Datele de ieºire se scriu în fiºierul limbaj. Cerinþe: Datele de intrare se citesc din: .. s 1. 1 . care are structura: .cuvânt_m În fiºierul cuvinte. o mulþime de stãri finale.writeln(V[N]) end.i1 s .1 n*r. Exemplu: .. numere naturale despãrþite prin spaþiu. n ≤ 200. Se construieºte un limbaj nou.1 1. numãrul de ordine al stãrii iniþiale. format din caractere litere mici din alfabetul englez.. despãrþite prin spaþiu. o stare iniþialã.txt având urmãtoarea structurã: n c c c .Pe m linii se scrie YES sau NO... o funcþie care descrie evoluþia stãrilor în funcþie de caracterele introduse în alfabet. dupã cum cuvântul de pe linia corespunzãtoare este sau nu cuvânt al limbajului realizat. c r 1 2 3 i k k k ... dacã pornind de la starea iniþialã. kl 1 2 3 s .txt se vor introduce cuvinte formate doar din caracterele permise (cele introduse în diction.fiºierul diction.fiºierul cuvinte. pe urmãtoarele n*r linii se descrie funcþia de evoluþie astfel: . având urmatoarele elemente: q q un sistem format din n stãri. reprezentând caracterele alfabetului.."0" desemneazã mulþimea vidã .. urmãrind funcþia de evoluþie se ajunge la o stare finalã. al limbajului descris mai sus.

celelalte valori din vector fiind iniþializate cu 0. i .j]^[k] va fi starea cu indicele k din lista corespunzãtoare rezultatelor funcþiei pentru parametrii i ºi j (i este stare. s1 ºi s2. … .j (corespunde valorilor i . Aniko Sos Liceul Teoretic "Emanuil Gojdu" Oradea Rezolvare. j este caracter). Dupã i etape. Prof. pentru 586/133MHz.txt abb aabaa aaab bbb abaa Fiºierul de ieºire este limbaj. la etapa 0 vom avea doar s1[stare iniþialã] =1. Funcþia va fi memoratã în matricea A: A[i. Deci: “funcþia” primeºte ca parametri starea curentã ºi un caracter. existã un 1. Prof. Pentru a testa un cuvânt vom folosi doi vectori auxiliari. Deci. ºi caracterul din cuvânt egal cu numãrul etapei. s [j] va fi egal cu 1 dacã.txt: YES YES NO YES YES Timp maxim de execuþie: 10 sec/test. pornind din starea iniþialã. A[i. Stãrile finale vor 1 2 n*r fi memorate în vectorul sf. adicã 1. i din schema de intrare). Aceºtia vor fi completaþi într-un numãr de etape egal cu lungimea cuvântului. la etapa 1 se parcurge vectorul s1 ºi pentru fiecare valoare de 1 întâlnitã.diction.txt: 4 a b 1 3 4 2 3 4 0 4 3 1 3 2 4 0 1 cuvinte. În final s2 este copiat în s1 . iar rezultatul este tot o stare care poate fi aleasã arbitrar din lista corespunzãtoare parametrilor primiþi (citim n⋅ r linii cu stãri).j]^[0] va fi egal cu numãrul de rezultate posibile ale funcþiei pentru parametrii i. Maria ºi Adrian Niþã. se completeazã în s2 toate stãrile în care funcþia poate ajunge având ca parametri : starea corespunzãtoare valorii 1.2 drum de lungime i care sã aducã sistemul în starea j.

rãspunsul îl gãsim prin simpla verificare a stãrilor finale în vectorul s1: dacã cel puþin uneia din stãrile finale îi corespunde în s1 o valoare 1 atunci..c). nf:=0. :array[1.sf[nf]) end.i. .tmp.s2 begin assign(fil. readln(fil. cuvântul face parte din limbaj.200] of byte. :integer.nf. :string. while c=' ' do read(fil.f+1). read(fil.si.f s c A sf.tmp[f]) end. readln(fil). var fil.c). Dupã ce s-a parcurs întreg cuvântul. move(tmp. readln(fil.f+1).s[i]]^.s[i]]^[0]:=0 :text.pentru a putea efectua aceleaºi operaþii ºi la etapa urmãtoare. for i:=1 to r do for j:=1 to n do begin f:=0.s1.'a'. while not seekeoln(fil) do begin read(fil.A[j. while not eoln(fil) do begin inc(f).sizeof(A). reset(fil). while not seekeoln(fil) do begin inc(nf).s[i]].'DICTION. r:=length(s). type vec=array[0.0).j. :vec.fo N.200..TXT'). :char. fillchar(A.si). getmem(A[j.. read(fil. s:=s+c end.r.'z'] of ^vec.N). Program Limbaj. if (f=1) and (tmp[1]=0) then A[j.

în ordinea datã de numerele în baza 10 ce corespund ºirurilor de biþi.'NO') end.s[i]]^[0] do s2[A[j.txt va conþine o singurã linie. Biþii. evident. for i:=1 to length(s) do begin fillchar(s2. close(fil). s1[si]:=1.0). assign(fil. Vi se cere sã tipãriþi al C-ulea ºir de biþi din aceastã mulþime.'YES') else writeln(fo.0). for j:=1 to N do if (s1[j]=1) and (A[j. close(fo) end. f:=0. while not seekeof(fil) do begin readln(fil. pot lua valoarea 1 sau 0. assign(fo. 'LIMBAJ. Ieºire: Fiºierul output.s[i]]<>nil) then for f:=1 to A[j.sizeof(s1). Intrare: Fiºierul input.s). Problema 8 Enunþ. cu un singur numãr. if f=1 then writeln(fo. anume al C-ulea ºir de biþi din mulþime. Mulþimea conþine toate ºirurile posibile de N biþi care au cel mult L biþi de 1. readln(fil) end.TXT'). Exemplu: input.sizeof(s2).s[i]]^[f]]:=1.s[i]]^[0]:=f. reset(fil). rewrite(fo).'CUVINTE.TXT'). close(fil). L ºi C.txt conþine trei numere întregi separate printr-un spaþiu: N. fillchar(s1. for i:=1 to nf do if s2[sf[i]]=1 then f:=1.txt: . Se considerã o mulþime ordonatã de ºiruri de N biþi.else a[j. s1:=s2 end.

begin assign(fil. Un ºir de i biþi evident va fi obþinut prin adãugarea unui bit la un ºir de i-1 biþi. atunci ar mai rãmâne N-1 biþi de aflat dintre care L-1 biþi 1. for i:=1 to N do begin for j:=1 to i do M[i.j] este egal cu numãrul de ºiruri de i biþi.j C fil :array[0. var M N.0).0. deoarece în total trebuie sã avem maxim j biþi de 1.i]:=1 end. dorim sã calculãm M[i. în caz contrar valoarea din C va depãºi numãrul maxim de ºiruri care se pot crea în condiþiile rezultate (adicã: punând un bit 0. for i:=0 to N do begin M[i. :comp. Deci valoarea din C va evolua pe parcursul determinãrii biþilor. reset(fil).j-1]. Biþii din ºirul cãutat vor fi deduºi.j]=M[i-1.TXT'). începând cu cel mai semnificativ. actualizat pentru restul ºirului de biþi.j].'INPUT.j-1]+M[i-1. :longint.L.33] of comp.L] (aceasta deoarece noul nostru ºir va conþine doar L-1 biþi de 1.j]+M[i-1.. indicând în fiecare moment. read(fil. Vom calcula o matrice M cu urmãtoarea semnificaþie: M[i.txt: 10100 USACO. C va rãmâne constant depaºind astfel valoarea M[Ni-1.i.Li-1]). se pot afla cel mult j biþi de 1.5 3 19 output. care au cel mult j biþi de 1.L.i] end.N. M[0. Program Bizzi. for j:=i+1 to N do M[i. fillchar(M.C). Deoarece C trebuie sã fie în permanenþã mai mare decât zero ºi mai mic decât M[Ni.. Dacã bitul adãugat este 1 rezultã cã în ºirul de i-1 pot fi maxim j-1 biþi de 1. :text. al câtelea ºir trebuie gãsit folosind doar N biþi din care i doar L de 1. în ºirul de lungime i-1.0]:=1.j]. folosind urmãtorul raþionament: dacã pe poziþia curentã am aºeza un bit de 1.32.j]:=M[i-1.Li] rezultã i cã de fiecare datã când C-M[N-1. ar avea valoarea C-M[N-1. Spring Open 1998 Rezolvare. Deci rezultã formula de recurenþã: M[i.sizeof(M). close(fil). deci toate ºirurile cu L biþi de 1 trebuie eliminate).j]:=M[i. . iar noul C. În schimb dacã adãugãm un bit 0. Presupunând cã cunoaºtem toate valorile acestei matrici pentru indici mai mici decât i ºi j.L] este mai mare decât zero va trebui sã aºezãm un bit 1.

j].0). [ Capitolul 7] [ Cuprins ] [ Capitolul 9] .j] then begin write(fil. for i:=N downto 1 do if C>M[i-1.'OUTPUT. C:=C-M[i-1.assign(fil. rewrite(fil). writeln(fil). dec(j) end else write(fil.1).TXT'). j:=L. close(fil) end.

reducerea timpului de calcul. aceastã listã a configuraþiilor devine ºi ea foarte mare. în special. la dreapta. Deoarece pentru date de intrare mai mari. Dat fiind faptul cã operaþiile cu pointeri sunt destul de lente. deci ºi a nodurilor deja expandate. timpul de calcul creºte considerabil. Vom încerca sã înlãturãm ºi acest neajuns. Deci operaþiile cele mai frecvente din algoritm sunt cele care manipuleazã o listã. Aceastã secvenþã s-ar reduce doar la schimbarea unei valori din false în true. ªtiind cã 0 îºi poate schimba poziþia cu orice numãr aflat deasupra. ca ºi operaþia de extragere a minimului din lista open va consuma un timp de calcul sporit pentru cã acum este necesarã parcurgerea întregii liste. se cere sã se precizeze ºirul de mutãri prin care se poate ajunge de la o configuraþie iniþialã la o configuraþie finalã. Tehnica ce urmeazã a fi prezentatã porneºte de la o idee . la stânga sau jos. care sã conþinã ºi nodurile expandate ºi cele neexpandate. Douã cãsuþe sunt ocupate cu numãrul 0. O primã idee simplã ar fi sã folosim o singurã listã. Fiecare cãsuþã conþine un numãr între 1 ºi N*N-2. diferit de 0. Dupã cum se cunoaºte. Evident algoritmul rãmâne acelaºi care este cunoscut cititorului din parcurgerea manualului. Rezolvare. Fiecare numãr natural. În acest mod am elimina toate operaþiile de ºtergere din lista open (a nodurilor neexpandate) ºi de inserare în lista close. inserare ºi ºtergere din listã. secvenþa de cod care se executã cel mai frecvent constã în extragerea din lista configuraþiilor a aceleia care minimizeazã efortul parcurs ºi cel estimat (suma g+h) ºi expandarea acestei configuraþii. Reluãm aici problema pãtratului. Cele câteva modificãri în implementare vizeazã. eficientizarea cãutãrii în spaþiul soluþiilor. pentru a familiariza cititorul cu o nouã modalitate de implementare a tehnicii Branch and Bound. în sensul cã trebuie sã se ajungã la configuraþia finalã într-un numãr minim de mutãri. Se cere de asemenea ca acest ºir sã fie optim. în raport cu poziþia în care se aflã numãrul 0. ºi pentru a le distinge sã folosim un nou câmp care sã indice tipul nodului. ajungând sã conþinã chiar zeci de mii de noduri. rezultã cã cea mai mare parte din timpul de calcul este consumatã de operaþiile de parcurgere. apare o singurã datã în cadrul pãtratului. discutatã ºi în manual. Se considerã un pãtrat cu NxN cãsuþe. iar lista fiind atât de mare. dar ºi scurtarea codului.Capitolul 9 Branch and Bound Problema 1 Enunþ. modalitate ce va fi folositã ºi în celelalte rezolvãri. Dar aceastã schimbare nu aduce o îmbunãtãþire substanþialã pentru cã prezintã un dezavantaj esenþial: verificarea existenþei unui nod în listã. Va trebui sã gãsim o cale de reducere a numãrului de astfel de operaþii dar ºi a timpului consumat de ele.

Deci extragerea minimului se va face într-un timp constant indiferent de numãrul de noduri (este necesarã doar o parcurgere a vectorului M). Cãutarea unui nod va necesita parcurgerea unei singure liste v[I]. iar “regula bine stabilitã” este de fapt o funcþie numitã hash. în general. GetMin. care nu este nici injectivã. Totuºi a rãmas o operaþie care este la fel de consumatoare de timp ca ºi înainte. cu raportul dintre numãrul . pe medie. care. pe baza informaþiilor care-l individualizeazã. niºte operaþii în plus: pentru a pãstra proprietatea de minim a nodului referit de M la fiecare inserare se va verifica daca nodul curent are valoarea g+h mai micã decât cea aflatã la M[k]. o valoare cât mai mare (cât încape în reprezentarea internã) care sã depindã de configuraþia respectivã. iar cea de-a doua. nici surjectivã. dar. Trebuie observat cã timpul de calcul necesar ambelor operaþii depinde de hn: prima necesitã exact hn paºi. În general. ºi de care depinde în mare mãsurã reuºita acestei metode. Cãutarea unui nod se va desfãºura în mod analog: se calculeazã din nou k ºi se parcurge lista v[k].de bun-simþ. ºi anume: dacã trebuie sã cãutãm un nod într-o listã foarte mare. Pentru inserarea unui nod se va calcula k=Hash(nod) ºi nodul respectiv se va insera în lista v[k]. anume extragerea minimului care necesitã parcurgerea tuturor elementelor din toate listele. ºi deci sã minimizeze timpul unei cãutãri.o constantã care reprezinzã numãrul listelor “mici” (numite buckets) care vor fi folosite. în multe liste mai mici? Astfel în loc sã cãutãm într-o listã cu 20. Aceasta poate fi optimizatã destul de simplu: vom mai reþine un vector de pointeri M[i] care va indica nodul neexpandat din lista i cu suma g+h minimã. evident. respectiv. Acum sã particularizãm: funcþia hash folositã aici. Aceste operaþii sunt implementate în procedurile AddNode ºi. dupã o regulã bine stabilitã. Aceasta implicã. apoi se însumeazã pãtratele produselor dintre valoarea aflatã pe acea cãsuþã (între 0 ºi N*N-2) ºi numãrul prim corespunzãtor. asociazã fiecãrei cãsuþe din pãtrat un numãr prim care este memorat in matricea np (pot fi folosite ºi numere generate aleator cu condiþia ca în cazul extrem valoarea calculatã sã nu depãºeascã reprezentarea). Valoarea aleasã pentru hn este 4999 care este un numãr prim ºi uºor de reþinut. în final se calculeazã modulo hn. se poate construi destul de uºor o funcþie hash bunã: în primul rând trebuie calculatã.000 de noduri vom cãuta într-una cu 10 de noduri. hn trebuie sã fie un numãr prim pentru a obþine rezultate cât mai bune. datoritã diversitãþii problemelor. folosind o metodã arbitrarã. þinând cont de câteva observaþii. iar la fiecare extragere de minim va trebui parcursã lista v[k] pentru a selecta noul minim ce trebuie referit de M. o valoare întreagã cuprinsã între 0 ºi hn . Cum se efectueazã operaþiile cu lista? Se reþine un vector de pointeri v[i] care indicã primul element din lista i. Pentru a fi eficientã o astfel de funcþie trebuie sã asigure o distribuþie cât mai uniformã a nodurilor în liste. Sã analizãm aceastã funcþie: rolul ei este de a asocia în mod determinist fiecãrui nod. rezultatul funcþiei hash va fi egal cu aceastã valoare modulo hn. nu existã o formulã standard pentru aceastã funcþie. de ce sã nu împãrþim lista cea mare. un numãr de paºi egal. nu trebuie sã aibã mai mult de zece elemente. Aceastã tehnicã se numeºte hash table sau tabele de dispersie1.

total de noduri generate ºi hn. Este clar cã alegerea lui hn determinã eficienþa programului: dacã numãrul de noduri obþinute din expandarea unei configuraþii este mic, atunci într-un timp constant se vor efectua mai multe extrageri de minim ºi ar fi recomandat ca hn sã primeascã o valoare mai micã (pentru a reduce timpul consumat de cãutarea minimului); în caz contrar, se vor efectua multe operaþii de cãutare ºi deci hn va lua o valoare mai mare pentru a minimiza lungimea listelor v[i].

Program Patrat; type ref nod =^nod; =record a n,p g,h e end;

:array[1..6,1..6] of byte; :ref; :integer; :boolean;

const np:array[1..36] of longint= (2,3,5,7,11,13,17,19,23,29,31,37,41,43, 47,53,59,61,67,71,73,79,83,89,97,101,103, 107,109,113,127,131,137,139,149,151); hashno=4999; var fil i,j,n Ci,Cf,minh v,M :text; :integer; :ref; :array[0..4998] of ref;

Function Hash(p:ref):integer; var i,j:integer; t :longint; begin t:=0; for i:=1 to n do for j:=1 to n do t:=t+sqr(p^.a[i,j]*np[37-(i-1)*n-j]); Hash:=t mod hashno end; Procedure H(p:ref); var i,j,x,y:integer; begin p^.h:=0;

for i:=1 to n do for j:=1 to n do if p^.a[i,j]<>Cf^.a[i,j] then for x:=1 to n do for y:=1 to n do if p^.a[i,j]=Cf^.a[x,y] then begin inc(p^.h,abs(i-x)+abs(j-y)); x:=n; y:=n end end; Procedure AddNode(p:ref); var t,t2:integer; z :ref; begin p^.e:=false; t:=Hash(p); if v[t]<>nil then z:=v[t] else z:=nil; v[t]:=p; v[t]^.n:=z; if M[t]=nil then M[t]:=p else if p^.g+p^.h<M[t]^.g+M[t]^.h then M[t]:=p end; Function GetMin:ref; var f,i:integer; t :ref; begin GetMin:=nil; i:=-1; for f:=0 to hashno-1 do if M[f]<>nil then if i=-1 then i:=f else if M[f]^.g+M[f]^.h < M[i]^.g+M[i]^.h then i:=f; GetMin:=M[i]; m[i]:=nil; t:=v[i]; while t<>nil do begin if (not t^.e) then if m[i]=nil then m[i]:=t else if M[i]^.g+M[i]^.h>t^.g+t^.h then M[i]:=t; t:=t^.n

end end; Function Equal(p1,p2:ref):boolean; var i,j:integer; begin Equal:=true; for i:=1 to n do for j:=1 to n do if p1^.a[i,j]<>p2^.a[i,j] then Equal:=false end; Procedure PrintSol(f:ref); var i,j:integer; begin if f^.p<>nil then PrintSol(f^.p); for i:=1 to n do begin for j:=1 to n do write(f^.a[i,j],' '); writeln end; readln end;

Procedure Expand(minh:ref); var i,j,x,y,q :integer; t,f :ref; begin minh^.e:=true; for i:=1 to n do for j:=1 to n do if minh^.a[i,j]=0 then for x:=-1 to 1 do for y:=-1 to 1 do if (x=0) xor (y=0) then if (i+x in [1..n]) and (j+y in [1..n]) then begin new(t); t^:=minh^; inc(t^.g); t^.p:=minh; t^.n:=nil; t^.e:=false; q:=t^.a[i,j]; t^.a[i,j]:=t^.a[i+x,j+y]; t^.a[i+x,j+y]:=q; H(t);

Ci^.a[i.j]). M[i]:=nil end. Ci^.p:=minh. new(Cf). readln(fil. f:=f^. if f=nil then AddNode(t) else if f^.n). Ci^.g then begin f^. .p:=nil.g>t^. readln(fil) end.e then if f^.Cf^.Ci^.t) then break. f^.g:=t^. H(Ci). new(Ci). for i:=1 to n do begin for j:=1 to n do read(fil. begin assign(fil. while (f<>nil) do begin if Equal(f.h then m[q]:=f end else dispose(t) end end. end. close(fil).a[i.IN').q:=Hash(t).g:=0.n end.h < m[q]^.'PATRAT. for i:=0 to hashno-1 do begin v[i]:=nil. readln(fil). f:=v[q].g+m[q]^. for i:=1 to n do begin for j:=1 to n do read(fil. if not f^.g+f^. reset(fil).g.j]).e:=false.

.unde xi este de forma lt sau ct..AddNode(Ci). while minh^.. amn Ieºire: Rezultatele vor fi în fiºierul "semne.')... Se dã un tablou MxN (1 ≤M+N ≤15) cu elemente numere întregi...g. xk .... writeln(minh^. PrintSol(minh) end.in" este de forma: m n a11 a12 .h>0 do begin Expand(minh).. minh:=GetMin. Se numeºte "operaþie" în acest tablou înmulþirea unei linii sau coloane cu -1. Sã se determine cea mai micã secvenþã de operaþii care aduce tabloul la o formã în care toate sumele de linii sau coloane dau numere nenegative. . lt (ct) reprezentând 1 schimbarea semnului pe linia (coloana) t. a1n a21 a22 . Intrare: Fiºierul de intrare. Linia a doua reprezintã secvenþa de operaþii care conduc la rezultat.numãrul minim de operaþii x x2 .... sub forma: k . minh:=GetMin end. a2n . numit "semne... Problema 2 Enunþ..' mutari.out".. am1 am2 ....

În cazul de faþã construirea unei funcþii care sã fie optimistã indiferent de configuraþie este mai dificilã ºi ar necesita un timp de calcul considerabil. dar care dã rezultate bune în practicã: se aproximeazã numãrul de operaþii necesare cu jumãtate din numãrul liniilor ºi coloanelor cu sumã negativã. .Atenþie: se vor scrie întâi operaþiile pe linii. se va lista una din ele. a doua linie este goalã. ordonate crescãtor. Dupã cum se ºtie. Problema constã în faptul cã o singurã operaþie poate schimba semnul mai multor sume. pentru a asigura optimalitatea soluþiei. De aceea a fost aleasã o metodã mai puþin deterministã. vom evidenþia doar câteva aspecte care þin strict de particularitatea problemei. 20-21 iunie 1997. Adrian Atanasiu "Marele premiu PACO". Fiindcã reconstituirea soluþiei va necesita precizarea “operaþiilor” efectuate suntem nevoiþi sã adugãm informaþiei din nod încã douã câmpuri ml ºi mc care vor preciza ce operaþie a fost fãcutã pe linie sau coloanã pentru a ajunge în configuraþia curentã. univ. Observaþie: pentru k=0. Bucureºti. funcþia euristicã trebuie în mod obligatoriu sã fie optimistã adicã sã prezicã un efort mai mic decât cel real. clasa X Rezolvare. Deoarece rezolvarea se bazeazã pe aceeaºi linie cu precedenta. Dacã sunt mai multe soluþii. Exemplu:Pentru intrarea 5 3 4 -2 2 3 -1 15 -22 0 -3 4 1 -3 5 -3 2 ieºirea este: 2 c2 l3 Timp de execuþie: 10 secunde/test “Semne” – prof. ºi deci o configuraþie care are majoritatea sumelor negative poate sã conducã la soluþie într-un numãr de paºi mai mic decât cel al liniilor ºi coloanelor cu sumã mai micã decât zero. dr. urmate de operaþiile pe coloane.

19.b. :byte. var i.h end. :integer.13.m. =4999.7.14] of integer= (2.. :array[0.mc e g. =record a n.nn l.sp const hn p :text. :integer var fil n.4999] of ref.j:integer. begin r:=0.j gn.17. :ref. function Sgn(i:integer):integer..41.31. for i:=1 to m do begin . Function Hash(t:ref):integer.23.Program Semne.a[i.h:=0.p ml.29. :boolean.j])*sqr(p[((i-1)*j) mod 15]). type ref rec =^rec. begin if i>0 then sgn:=1 else sgn:=-1 end.j.11..3. :array[1. :ref.o t. :integer.47.i. begin t^. Hash:=abs(r) mod hn end.37.15] of integer.. :array[-1.15. var i.f. for i:=1 to m do for j:=1 to n do r:=r+sgn(t^. s :longint.r:integer. Procedure H(t:ref).51).1.5.

h < o[i]^. begin r:=nil.h*0. if o[k]=nil then o[k]:=p else if o[k]^. o[i]^.h then i:=f.h<>0 then inc(t^.h>p^. end. if s < 0 then inc(t^. r :ref. end.h). for f:=0 to hn do if o[f]<>nil then begin if i=-1 then i:=f else if o[f]^. t^.j]. if s<0 then inc(t^. Function Getmin:ref. var f.i].g+o[k]^. for j:=1 to n do s:=s+t^.e:=true.I :integer. GetMin:=o[i].h:=round(t^.e=false then begin . for j:=1 to m do s:=s+t^. while (r>>nil) do begin if r^.g+p^.g+o[i]^.a[i. Procedure Add(p:ref). p^. l[k]:=p. i:=-1.a[j.h) end.h) end.n:=l[k]. o[i]:=nil. for i:=1 to n do begin s:=0. k:=hash(p).5) end. if t^.s:=0. begin inc(nn). var k:integer.h then o[k]:=p. r:=l[i].g+o[f]^. if l[k]=nil then inc(gn).

q :ref.p2:ref):boolean.j]:=-t^.a[i. q:=l[k].h then o[i]:=r end.ml:=i. if q<>nil then if q^.g+o[i]^.n end. var i. begin for i:=1 to m do begin new(t). while q<>nil do begin if egal(t. t^:=b^. t^.j :integer. for j:=1 to n do begin .j] then Egal:=false end.p:=b.j].j]<>p2^.a[i.mc:=0. t^. inc(t^.q) then break. t^. for i:=1 to m do for j:=1 to n do if p1^. end.a[i. for j:=1 to n do t^.k:integer.j.a[i.h>r^. r:=r^. t.g then q^:=t^ else dispose(t) else Add(t) end.e:=false. Procedure Expand(b:ref). t^. t^.n:=nil.g+r^. k:=Hash(t). Function Egal(p1.if o[i]=nil then o[i]:=r else if o[i]^. H(t).g>t^. q:=q^.n end. var i. begin Egal:=true.g).

' ').p end. inc(t^. t^.new(t).f :integer.p:=b.j. rewrite(fil). t^.j]. l.a[i. begin for i:=-1 to hn do begin l[i]:=nil.q) then break.c :array[0. q:=b.OUT'). t^.f). close(fil) end. c[q^. q:=q^. while q<>nil do begin l[q^.i.n:=nil. k:=Hash(t).ml]:=1-l[q^. writeln(fil. H(t). for i:=1 to 15 do if l[i]=1 then write(fil.mc:=j.j]:=-t^. o[i]:=nil end. f:=0.a[i..'SEMNE. Procedure Sol(b:ref).mc]. . q:=q^.g). for i:=0 to 15 do begin l[i]:=0.' '). t^. if q<>nil then if q^. for i:=1 to m do t^. begin assign(fil.ml:=0.'C'.g>t^. var i. t^:=b^. for i:=1 to 15 do if c[i]=1 then write(fil.n end.'L'.15] of byte. q :ref.e:=false. c[i]:=0 end.ml]. for i:=1 to 15 do f:=f+l[i]+c[i]. q:=l[k].i.mc]:=1-c[q^. t^.g then q^:=t^ else dispose(t) else Add(t) end end. while q<>nil do begin if egal(t.

t^. Fie urmãtoarea tablã de joc: .p:=nil. readln(fil. sol(b) end. close(fil). t^. repeat b:=getmin.g:=0.h<>0 then expand(b) until b^. readln(fil) end.mc:=0.n). t^. halt end. Problema 3 Enunþ. if b^. t^.m.a[i. for i:=1 to m do begin for j:=1 to n do read(fil. t^. H(t).e:=false.IN'). t^. new(t). t^. if (m+n>15) or (m+n<1) then begin writeln('date invalide. Add(t). reset(fil).h=0.').n:=nil.ml:=0.assign(fil.'SEMNE.j]).

D6. Jocul constã în a schimba între ele locurile ocupate de piesele 'O’ ºi 'X'. D2. Se cere schimbarea între ele a poziþiilor pieselor 'X' cu 'O' într-un numãr cât mai mic de mutãri. în figura de mai sus.constând din douã pãtrate de laturã N=4. Fiºierul de intrare joc. într-un numãr minim de mutãri. aºezate simetric faþã de D4. D4.in conþine poziþia pieselor notate cu 'X' (lucru suficient. sau prin sãritura peste o altã piesã alãturatã dacã se ajunge într-o pãtrãþicã liberã. Pentru configuraþia de mai sus în pãtrãþica D4 se poate ajunge din: D3. C4. F4. . Prin mutare se înþelege deplasarea unei piese o singurã datã într-o altã pãtrãþicã. B4. Nu sunt permise mutãri pe oblicã. Mutãrile se vor vizualiza pe ecran. În acelaºi timp se vor contoriza mutãrile.E4. simulând jocul. D5. care au un pãtrãþel comun. având în vedere aºezarea simetricã a celor notate cu 'O' faþã de D4). O piesã poate fi mutatã într-o pãtrãþicã liberã învecinatã ei. Pe tablã sunt aºezate piese notate cu 'X' ºi 'O'.

j] este egal distanþa Manhattan de la poziþia i. adicã sã supraestimeze numãrul de mutãri necesare.. uses crt.in: D3 D2 E4 E3 E2 F4 F3 F2 Timp de executie 20 sec/test pentru 586 la 133MHz prof..7] of byte. type tabla =array[1.X sau O) pânã la cea mai apropiatã cãsuþã destinaþie pentru piesele de tipul p. . const hn=2999. ca ºi la problema anterioarã. vom folosi o tabelã precalculatã nm cu semnificaþia: nm[p. Pentru a eficientiza procesul de estimare. Deoarece aceste distanþe nu depind de configuraþia pieselor. fapt ce ar conduce la obþinerea unor rezultate neoptime. ps:string=' XO'.j aparþine sau nu tablei de joc.I. matricea va fi de fapt o constantã (iniþializatã în procedura Citire).1. Funcþia euristicã se bazeazã tot pe distanþa Manhattan. cu câteva modificãri necesare. O altã matrice constantã folositã este valid care indicã dacã poziþia i.Exemplu: (pentru figura datã) fiºierul joc. Maria ºi Adrian NIÞÃ Liceul "Emanuil Gojdu" Oradea Rezolvare.7. Program XsiO. pentru a împiedica funcþia sã devinã “pesimistã”.j pe care se aflã o piesã de tipul p (0 sau 1 .

y]=3) then nm[f.a[i.j]:=2.7] of longint.t :integer.8-j]:=1 end..j). n.j]:=abs(x-i)+abs(y-j). reset(Fil).1.3.a[8-i.(abs(i-4)+abs(j-4)) div 2).j]<>0 then inc(nm[f..i.ref nod =^nod.9.. final. i. Procedure Citire. . :byte. :ref. while not eof(fil) do begin readln(fil.i.i.i. :array[-1.7. =record a n..t g.min valid nm i.j]) and (f+final. :tabla.9] of boolean.j. close(fil). var v.j]:=255.1..a[i. final.4999] of ref. i:=byte(Upcase(c1))-64..y..h e end.f.a[8-i.-1.x.7.j]:=1. begin assign(fil.IN').. n.i.y] then if (abs(x-i)+abs(y-j) < nm[f.8-j]:=2. :nod.a[x. :boolean.c1.final z hv :array[0.7] of byte. :ref.'JOC.j]. var fil :text.j] then begin nm[f.j n. if nm[f. :array[1. :array[0.1. c1.c2 :char. for f:=1 to 2 do for i:=1 to 7 do for j:=1 to 7 do if valid[i. :longint. for x:=1 to 7 do for y:=1 to 7 do if valid[x.

j] then begin c:=1.8) end end.e:=true.h) then r:=i. Function GetMin:ref.c. Min[r]^. while Min[r]=nil do inc(r).r:integer.g+Min[i]^.j]:=round(nm[f.h:=p^.g+p^. begin i:=Hash(p).j]*0.g+Min[i]^. begin p^. t:ref. if Min[i]^. for i:=1 to 7 do for j:=1 to 7 do if valid[i. Procedure AdNod(p:ref).h then Min[i]:=p. var i:integer.h:=0.j]. begin t:=0.i. var i. v[i]:=p end.h < Min[r]^. .i. Function Hash(p:ref):integer.h>p^.j] do c:=c*hv[i.t:longint.g+Min[r]^.nm[f.h+nm[3-p^.j. t:=t+c end. var i.n:=v[i]. for i:=1 to 7 do for j:=1 to 7 do if valid[i.j] end.a[i. k:ref. for k:=1 to p^. Hash:=t mod hn end. if Min[i]=nil then Min[i]:=p. p^.j].j] then p^.i. GetMin:=Min[r]. var i.a[i. Procedure H(p:ref).j:integer. for i:=r+1 to hn-1 do if (Min[i]<>nil) and (Min[i]^.k. begin r:=0.

Dispose(w) .h then Min[k]:=f end.g>w^.e then begin if Min[r]=nil then Min[r]:=k.t:=p. k:=v[r]. while k<>nil do begin if not k^. var i. k:=k^. if k^. begin w^. f:=nil.f :ref. while q<>nil do begin if q^. q:=q^. inc(w^. for i:=1 to 7 do for j:=1 to 7 do if valid[i.h then Min[r]:=k end. if Min[k]^.k :integer.a[i.g:=w^. w^. var i. q:=V[k]. if f=nil then AdNod(w) else begin if f^.g then begin f^.t:=p. if Min[k]=nil then Min[k]:=f. f^.g+f^.h=w^. break end.g+k^.j:integer.g+Min[k]^. H(w).n end end. k:=Hash(w).dj.e:=false.a[i.w) then begin f:=q. w.h>f^.j] then if p1^. begin Egal:=false.g. Procedure Expand(p:ref).g). Function Egal(p1. f^. Procedure Proces(w:ref).p2:ref):boolean. Egal:=true end.h < Min[r]^.h then if Egal(q.Min[r]:=nil.j.j]<>p2^.g+Min[r]^.q.n end.j] then exit.di.e:=false.

Procedure Print(p:ref).a[i.a[i+2*di. gotoxy(1.a[i. w^.a[i+2*di.2+2*(8-j)).p^.a[i.a[i. Procedure Sol(p:ref).j]:=0.j+dj] and (p^.tj:integer.end end.j]]) else write(' ') end.24). w^. write('Numãr Mutãri:'.g). if p^.a[i. w^:=p^.a[i.a[i+di. begin ClrScr.j+dj]<>0) and (p^. w^.j] then begin . var i.t<>nil then Print(p^.j].j]<>0 then for di:=-1 to 1 do for dj:=-1 to 1 do if (di=0) xor (dj=0) then begin if Valid[i+di. w^:=p^. begin for i:=1 to 7 do for j:=1 to 7 do if p^.a[i+di.t). w^.ti. fil:text. begin if p^. Proces(w) end.j+2*dj]:=p^.j+dj]=0) then begin new(w).j]:=0.j+dj]:=p^. for i:=1 to 7 do for j:=1 to 7 do if Valid[i.j+2*dj] and (p^.j]<>0 then write(ps[1+p^. if Valid[i+2*di.a[i+di. readln end.a[i. for i:=1 to 7 do for j:=1 to 7 do begin gotoxy(3+4*i. Proces(w) end end end.j+2*dj]=0) then begin new(w).j].j.

H(@n). Inelele au douã biluþe în comun.tj). write('|') randomize. fillchar(v.ti:=3+4*i.e:=false.tj-1). begin write('---'). 11 albe ºi douã gri. for i:=4 to 7 do for j:=1 to 4 do begin valid[i. gotoxy(ti+2.a.sizeof(min). n. fillchar(min.sizeof(valid). n. if z^.sizeof(v). valid[8-i. Citire. Fie douã inele în care sunt aºezate echidistant 13 biluþe.j]:=true. fillchar(final. . for i:=1 to 7 do for j:=1 to 7 do hv[i.h=0. fillchar(valid.0). ºi deci în total vor fi 24 de biluþe din care 11 negre. tj:=2+2*(8-j). gotoxy(ti-1. n.0).a. Problema 4 Enunþ. repeat z:=GetMin. write('|').0). write('---').0).g:=0. fillchar(n. Fiecare inel se poate roti în sens orar cel puþin o poziþie ºi cel mult 12.sizeof(tabla). antrenând cu el cele 13 biluþe din interiorul sãu.tj+1). gotoxy(ti-1.tj). end.8-j]:=true end. Sol(z) end. ca în figurã. AdNod(@n).sizeof(tabla). gotoxy(ti-2.h<>0 then Expand(z) until z^.j]:=1+Random(200)+Random(200). Print(p) end.0).t:=nil.

Problemã datã la selecþia lotului pentru Balcaniadã. Primul inel va fi cel ce conþine în figurã biluþele albe. Primul caracter din ºir corespunde întotdeauna biluþei comune din partea superioarã a figurii. Deci configuraþia din figurã va fi reprezentatã astfel: GAAAAAAAAGAAA GNNNGNNNNNNNN Ieºirea se va face pe ecran. pe fiecare aflându-se un ºir de 13 caractere ce codificã aºezarea biluþelor astel: A. tipãrind configuraþia biluþelor dupã fiecare rotire. Datele de intrare se citesc din fiºierul bilute. neagrã. celelalte urmând parcurgerea inelului în sens orar. Sibiu 1996 enunþ reformulat . respectiv gri.in ºi conþin douã linii.Problema constã în a aduce o configuraþie iniþialã la configuraþia din figurã într-un numãr minim de rotiri.N ºi G semnificã o biluþã de culoare albã.

begin s:=0. :sir. Este bine de ºtiut cã memoria disponibilã pe heap. =record s1.f:integer. if p^. Procedure H(p:ref)..9.h:=s end. type sir ref nod =string[13]. var b.4. în cazul în care nu mai este suficientã memorie liberã. Aceasta poate servi la oprirea programului.s2 n.t g. var fil :text. :integer. var s. apoi putem interpreta acest nou ºir ca pe un numãr în baza 3 pe care-l vom impãrþi modulo la hn.. ºi tipãrirea unui mesaj corespunzãtor. fãrã nici o optimizare suplimentarã. fiecare din ele putând lua trei valori.s2[f]='A') and (f in [2. if (p^.h e end. Programul de mai jos este o implementare simplã a tehnicii Branch and Bound. =^nod.13]) then s:=s+2. .11.s2[5]<>'G' then s:=s+1. Absenþa tabelelor de dispersie nu este motivatã de structura diferitã a informaþiei din noduri: chiar dacã nu mai avem de-a face cu numere. Procedure Citire. :sir. Program Bilute. begin new(B).s1[1]<>'G' then s:=s+1. poate fi aflatã folosind funcþia MemAvail. :integer.6. :ref.k cc min :ref. p^. for f:=1 to 13 do begin if (p^. :boolean.i. end. if p^..13]) then s:=s+2.Rezolvare. cea în care se alocã nodurile.s1[f]='N') and (f in [2. o funcþie hash eficientã poate fi construitã uºor: o idee ar fi sã unim cele douã ºiruri obþinând 26 de elemente..

s2[1]:=z^.s2[5]:=z^. begin n:=length(s).p^. z^.f).'BILUTE.IN'). writeln(p^. begin t:=b. for f:=1 to 12 do for i:=1 to 2 do begin new(z).n-1) end. z^.s1. z^.t:=nil. var f. var z.b^.s1. Procedure Sol(p:ref). z^.g+1.n:=nil.s1[1] .i :integer.t<>nil then Sol(p^.b^.s2:=p^. while t^.s1[10].s1.s2. readln(fil. for f:=1 to p do s:=s[n]+Copy(s.cut :ref.s1:=p^.' '.q. assign(fil.n.t. z^. Procedure Shift(var s:sir.e:=false.n<>nil do t:=t^. z^. readln(fil. Procedure Expand(p:ref). h(b) end.s2) end. z^.b^.n :integer.t:=p.s2).s1).p:integer). close(fil). b^.e:=true. begin if p^. b^. z^.n:=nil. f.1.t). p^. reset(fil).g:=p^.g:=0. if i=1 then begin shift(z^. b^.e:=false.

cut:=t^.h+i^.e) then begin min:=i^. k:=i end.s1) and (q^. Problemã propusã Enunþ. readln.n end. repeat min:=maxint.s1[1]:=z^. cut:=q end. halt end end end.n:=z. if cut^. z^.end else begin shift(z^. q:=q^.s2. writeln(‘Nu exista solutie’) end.t:=p.n end.s1[10]:=z^. expand(k) until memavail<500. begin Citire.n<>nil do begin if (q^.n end else dispose(z).s2=z^. i:=i^.g<min) and (not i^.s1=z^.s2[1] end. cut:=nil. z^.g) then begin q^. h(z). k:=b.g>z^. q:=b.h=0 then begin Sol(cut).n<>nil do begin if (i^.g. while i^. Pe o tablã de ºah nxn se gãsesc p cai ºi k obstacole amplasate într-o configuraþie iniþialã.s2[5].f).h+i^. i:=b. if cut=nil then begin t^. t:=t^.s2) and (q^.n. while q^. .

Problema cere ca dvs. din urmãtoarele n. dacã existã. conþine n caractere (O pentru obstacol. C pentru cal. p=2. Prima linie a fiºierului conþine numãrul n. o linie .txt. Primele n linii vor fi date de configuraþia iniþialã. un set de miºcãri ale cailor care permit ajungerea într-o configuraþie finalã cerutã. Programul va scrie soluþia. Pentru exemplul anterior avem: 3 COC SSS SSS SOS SSS CSC 2. dacã existã. Urmãtoarele n linii reþin configuraþia finalã.txt. sã determinaþi. Fiecare linie. în fiºierul out. Configuraþia iniþialã se citeºte din fiºierul text in. Exemplu: Configuraþie iniþialã (n=3. k=1) c o c Configuraþia finalã: o c c Observaþii: 1. dar nu poate staþiona pe o poziþie ocupatã de un cal sau de un obstacol. S pentru cãsuþã neocupatã) reprezentând configuraþia iniþialã. urmeazã n linii ale primei configuraþii intermediare. Calul poate sãri un obstacol sau un alt cal.Miºcarea calului este cea cunoscutã de la ºah. urmeazã o linie vidã.

Concurs cl. n linii ale configuraþiei finale. 3. prima linie a fiºierului va conþine NU 6. XI-XII. Tudor Sorin. “Miºcarea cailor” – prof. Valorile p ºi k se deduc din configuraþia iniþialã 5. Dacã nu existã soluþie. …. Lugoj 1998 [ Capitolul 8 ] [ Cuprins ] [ Capitolul 10 ] . Timp de execuþie: 1 minut.vidã.

unde este produs.j i.j q Σj∈V fj. sau curent electric de 20 amperi care poate trece printr-un conductor. .j .E). adicã fluxul transportat pe orice arc trebuie sã fie nenegativ ºi subcapacitar. care reprezintã “ritmul” maxim la care materialul poate circula prin conductã. putem de asemenea sã interpretãm un graf orientat ca pe o “reþea de transport” ºi sã-l folosim pentru a rãspunde unor întrebãri despre transportul de material. ∀ i∈V-{s.v)=0. este un graf orientat în care fiecare muchie (u.t}. i. vom considera c(u.i = Σj∈V fi. Fiecare arc dintr-o reþea poate fi imaginat ca o conductã prin care circulã material. Nodurile dintr-o reþea. ca de exemplu 2000 metri cubi de lichid pe orã printr-o conductã. în afarã de s ºi t.v) ∈ E are o capacitate pozitivã c(u. Reþelele de transport pot fi folosite ca model teoretic pentru reþelele de conducte.v) ≥0. Sursa produce materialul într-un anumit ritm ºi destinaþia îl consumã în acelaºi ritm. sunt doar punctele de intersecþie ale conductelor: materialul trece prin ele fãrã a fi colectat. informaþia care circulã prin reþelele de comunicaþie. Aceasta este proprietatea de conservare a fluxului ºi este aceeaºi ca ºi legea lui Kirchhoff pentru cazul în care “materialul” este curentul electric (“flux“ este echivalent cu “ritm”. pãrþi ale liniilor de asamblare. Dacã (u. adicã conservarea fluxului. Transportul de material în oricare punct din sistem este. intuitiv. ritmul în care materialul se miºcã. semnificând o anumitã cantitate într-o anumitã perioadã de timp).Capitolul 10 Teoria grafurilor Reþele de transport Aºa cum putem abstractiza harta strãzilor folosind un graf orientat pentru a gãsi cel mai scurt drum între douã noduri. Imaginaþi-vã traseul materialului printr-un sistem de la o sursã.v) ∉ E. O reþea de transport G=(V. Adicã ritmul în care materialul intrã într-un nod trebuie sã fie egal cu ritmul în care materialul iese din nod. Un flux în G este o funcþie f:VxV→R care are urmãtoarele proprietãþi: q 0 ≤ f ≤ c . Fiecare conductã are o capacitate specificatã. curentul care circulã prin reþelele electrice. unde este consumat. cu sursa s ºi destinaþia t. la o destinaþie.

E). atunci e se numeºte arc invers.t j∈V t.5) ºi (6. ºi e=vivj o muchie a lui P.2.2).6. Se cere sã se determine un flux de valoare maximã. sã considerãm cã drumul P trece prin nodurile 1. (2.Dacã f este un flux în reþeaua G=(V. în graful de mai sus. Fie P un drum oarecare în graful suport al lui G (acelaºi graf în care se ignorã sensurile arcelor). Problema fluxului maxim Se dã reþeaua G=(V.7) sunt arce directe ale drumului P pentru cã au acelaºi sens de parcurgere ca ºi cel din reþea. . De exemplu. ºi funcþia capacitarã c.4).5.6) a drumului P este un arc invers.7. dacã e corespunde arcului vivj al lui G. atunci se numeºte valoarea fluxului f numãrul: v(f) = Σ f -Σ f j∈V j.j V(f) poate fi interpretat ca fiind fluxul net care ajunge în ieºirea reþelei sau fluxul net care intrã în reþea prin s.E) cu sursa s ºi destinaþia t. în schimb muchia (5. dacã e corespunde arcului vjvi al lui G. Fiecare din muchiile (1.4. e se numeºte arc direct al drumului P. (4.

6) = f6. Un astfel de flux admisibil este ºi cel care are flux 0 pe fiecare arc. deci capacitatea rezidualã a drumului este 1. În exemplul nostru. Cum r(4. Primul pas al algoritmului constã în determinarea unui flux iniþial admisibil.6)=3 ºi r(6.6) vom avea: r(5. iar conservarea fluxului este evidentã: în fiecare nod intrã 0 unitãþi ºi ies de asemenea 0.5) va fi egalã cu c4.7 ºi sã vedem dacã este un drum de creºtere: avem r (1.5). capacitatea rezidualã a arcului (4.2)=8. Se numeºte capacitatea rezidualã a lui vivj numãrul: r(ij) = ci. Capacitatea rezidualã a drumului P se noteazã cu r(P) ºi este egalã cu minimul dintre capacitãþile reziduale ale muchiilor din P: r(P) = min e∈P r(e) Fie P={4. un drum de la s la t care are capacitatea rezidualã mai mare ca zero. Se numeºte drum de creºtere a fluxului f în reþeaua G. Aceste douã teoreme stau la baza algoritmului Ford-Fulkerson pentru determinarea unui flux de valoare maximã. Pentru arcul (5.5 = 8 – 3 = 5. dacã ºi numai dacã.5.j – fi. r(4.5 = 4. iar cel de-al doilea.3. atunci existã un flux de valoare maximã cu toate componentele întregi. Astfel.4)=1. fluxul.5)=5 ºi r(5. dacã vivj este arc direct în P j fj. adicã fixarea valorilor lui f pentru fiecare arc astfel încât sã se respecte proprietãþile fluxului (cele din definiþie).3)=5.7)=6.Fie P un drum ºi vivj o muche din P.5 – f4. r(3.2. Se verificã uºor cã cele douã proprietãþi sunt respectate: fluxul este subcapacitar pe orice arc. Teorema 2: Dacã toate capacitãþile sunt întregi.i dacã vivj este arc invers în P În exemplul de mai sus. .4. r(4. nu existã drumuri de creºtere a fluxului f în reþeaua G. primul numãr de pe arc reprezintã capacitatea arcului. sã luãm drumul 1.6}.6)=4 rezultã cã r(P)=4.6. rezultã cã drumul nostru este un drum de creºtere. Teorema 1: Un flux f este de valoare maximã într-o reþea G. r(2.

f5.7. f de valoare mai mare decât f. rezultã cã suma a tot ce “iese” din nod va rãmâne constantã ºi deci fluxul se va conserva.5. mergând întotdeauna doar pe arcele care au capacitatea rezidualã mai mare ca zero. sã vedem dacã va respecta ºi conservarea fluxului. va fi de asemenea verificatã ºi dupã modificarea fluxului: practic vom adãuga aceeaºi cantitate ambilor termeni. b) primul arc este invers.2=3+1=4. iar pentru arcele inverse se poate scãdea r(P) fãrã a obþine un 1 flux negativ. deci pe primul arc fluxul va scãdea. Sã le analizãm pe rând: a) ambele arce sunt arce directe. 1 Pornind de la fluxul anterior ºi folosind drumul de creºtere gãsit se va obþine un nou flux. Deoarece arcele pot fi de douã tipuri. adicã pentru arcele directe se mai poate adãuga r(P) flux fãrã a depãºi capacitatea arcului. astfel: fie r(P) capacitatea rezidualã a drumului gãsit. f4.La pasul doi se va determina un drum de creºtere P. Deci noul flux f va respecta prima proprietate a fluxului. deoarece ambele arce “ies” din nod. Faptul cã r(P) este egal cu minimul dintre capacitãþile reziduale ale arcelor din P. f2. iar de pe celãlalt scade cu aceeaºi cantitate. avem r(e) ≥ r(P).4. iar pentru arcele inverse fluxul va fi scãzut tot cu r(P). iar pe al doilea va creºte. vom întâlni patru cazuri. deci noile valori ale fluxului vor fi: f1.6.4=3+1=4. deci fluxul va creºte pe ambele cu aceeaºi cantitate r(P). Sã dãm un exemplu: pentru drumul de creºtere 1. Acesta se poate gãsi printr-o simplã parcurgere în adâncime. .5=3+1=4. ca în figurã (sensul de parcurgere este de la stânga la dreapta).v) ∈ P. iar cel de-al doilea este direct.2. dacã egalitatea din legea conservãrii fluxului era verificatã.6=4-1=3 ºi f6. ne asigurã cã pentru orice e=(u. avem r(P)=1.7=0+1=1. iar fluxul de pe unul creºte. pentru fiecare arc direct din P se va mãri fluxul cu r(P).

aceeaºi procedurã se ocupã de modificarea fluxului dupã ce a fost gãsit un drum de creºtere. Parcurgerea în adâncime este realizatã recursiv în procedura FindFlow. Finalitatea algoritmului este asiguratã de faptul cã la fiecare iteraþie valoarea fluxului creºte cu o cantitate mai mare decât zero. Acest pas se va repeta atâta timp cât exista un drum de creºtere. =array[1. operaþie care se face la întoarcerea din recursie. :longint. Datele de intrare se citesc din fiºierul flux. cum valoarea fluxului maxim este finitã rezultã cã este necesar un numãr finit de iteraþii ale pasului 2. Program Ford_Fulkerson_Flux. fluxul gãsit va fi maxim. respectiv.j … i j c capacitatea arcului de la i la j . Programul prezentat mai jos urmãreºte întocmai algoritmul descris.i. fluxul pe fiecare arc se va gãsi în matricea f.f n. ºi au formatul: n m numãrul de noduri ºi. de muchii i j c capacitatea arcului de la i la j . d) ambele arce sunt inverse.c) analog cu b). m m I. :boolean.1. de aceastã datã se va scãdea aceeaºi cantitate din ambii termeni ai egalitãþii conservãrii fluxului.in. :set of byte.j m m Se considerã cã sursa reþelei este nodul 1.m. . type matr var c. conform teoremei 1. :text. 1 1 1 1 I.100.j.t fil found seen :^matr. În final valoarea fluxului se calculeazã prin însumarea fluxului de pe arcele care pornesc din sursã. în final..100] of longint.. deci egalitatea se va pãstra. iar destinaþia este nodul N.

maxlongint) until not found.p]+tmp. FindFlow:=min.in'). reset(fil). repeat found:=false. tmp:=FindFlow(p.i]. begin new(c).0). tmp:=FindFlow(p.p]<min then min:=c^[i. FindFlow:=tmp end end. j:=0.p]-f^[i.sizeof(f^). writeln('Flux maxim:'.m).min).i]-tmp.i]<>0 then j:=j+f^[1.p])>0 then begin if c^[i. readln(fil. . new(f). fillchar(f^.j) end. if (f^[p.min:longint):longint. FindFlow:=tmp end end end end.i]:=f^[p. if found then begin f^[i. begin seen:=seen+[i].'flux.p]. seen:=[].i]>0) and (not found) then begin if f^[p. fillchar(c^. FindFlow(1. if found then begin f^[p.min). for t:=1 to m do readln(fil.j. if i=n then found:=true else for p:=1 to n do if (not (p in seen)) and (not found) then begin if (c^[i.j]).sizeof(c^). for i:=1 to n do if c^[1.n. assign(fil.0).p]-f^[i.i.Function FindFlow(i.i].p]:=f^[i. var p.c^[i. close(fil).tmp:longint.i]<min then min:=f^[p.p]-f^[i.

numãrul de muchii din reþea). De exemplu.Sã cercetãm complexitatea algoritmului descris: fiecare iteraþie a pasului doi necesitã O(M) operaþii (M=|E|. Este demonstrat cã.4 ºi. Aceasta constã în simpla înlocuire a parcurgerii în adâncime cu o parcurgere în lãþime. type . în schimb. numãrul de iteraþii al pasului doi nu poate fi mai mare decât M⋅ N/2. fiecare dintre ele având capacitatea rezidualã 1.000. Deoarece algoritmul nu alege drumurile de creºtere în mod preferenþial. iar MM [i] este egal cu capacitatea rezidualã a drumului de la sursã la nodul i. considerãm cã M are o valoare foarte mare. respectiv ultimul element din coadã. acesta este limitat de valoarea fluxului maxim U. Obþinem complexitatea algoritmului O(M⋅ U). iar i1 ºi i2 vor indica primul. în reþeaua de mai sus. Acest dezavantaj poate fi remediat prin îmbunãtãþirea adusã algoritmului Ford-Fulkerson de cãtre Edmonds ºi Karp.2. 1. Vectorul T reþine tatãl nodului de pe poziþia i din coadã. Deci la fiecare iteraþie valoarea fluxului va creºte cu 1. dar trebuie considerat cã.4. respectiv.3.000. asigurând astfel cã la fiecare pas se alege drumul de creºtere care are cel mai mic numãr de muchii. astfel s-ar obþine un timp de calcul inadmisibil). deoarece valoarea fluxului poate fi consideratã infinitã în raport cu dimensiunea problemei N (chiar pentru N=5.3. pentru cazul cel mai defavorabil când la fiecare iteraþie fluxul creºte cu o unitate. Pentru parcurgerea în lãþime este folositã o coadã.2. fiind necesare în total 2M iteraþii. folosind aceastã îmbunãtãþire. Astfel se obþine o complexitate de O(N⋅ M2). Prezentãm mai jos ºi o implementare care se bazeazã pe aceastã modificare. numãrul de iteraþii nu poate fi precizat cu exactitate. putem avea U=100. Program Flux_Edmonds_Karp. Aceasta reprezintã un dezavantaj. se poate întâmpla ca drumurile alese sã fie alternativ 1. aceasta este memoratã în vectorul Q.

for i:=1 to n do if not (i in seen) then begin if (c^[j.. MM[i]:=Min(MM[j].0). new(f). inc(i1). :^matr..m. begin if m1<m2 then min:=m1 else min:=m2 end.100. k:=0.i1.i]>0) then begin inc(i2).i]). :integer.i.. fillchar(c^.matr var fil c.100] of longint.m2:longint):longint.300] of integer. readln(fil. Q[1]:=1.i.m).j. begin assign(fil.j]>0) then begin inc(i2).k. flux:=0.c^[i.100] of longint. i2:=1..i2 Q. .sizeof(c^).T MM seen flux =array[1.sizeof(f^). seen:=seen+[i] end. reset(fil).'flux.c^[j. :longint.f n. Q[i2]:=i. i1:=1. :array[1.i]-f^[j. repeat seen:=[1].j]). T[i]:=j. fillchar(f^. for k:=1 to m do readln(fil. close(fil). T[1]:=0.1. if (f^[i. :set of byte. :array[1.0).i]-f^[j. while (not (n in seen)) and (i1<=i2) do begin j:=Q[i1].j.n. new(c).in'). MM[1]:=maxlongint. :text. Function Min(m1.

i]. astfel încât nodurile sã aibã gradele date. Sã se determine o dispunere a arcelor în graf. Problemã propusã Se considerã un graf orientat cu N noduri. while i<>1 do begin j:=abs(T[i]). writeln('Flux maxim:'. if n in seen then begin flux:=flux+MM[n].f^[i.MM[n]) else dec(f^[i. i:=n.MM[n]). Cititorul este invitat sã testeze ambele variante pentru date de intrare mari (N=100. inc(k) until not (n in seen). M>3000. T[i]:=-j. ºi capacitãþi cât mai apropiate de maxlongint) ºi sã compare timpii de execuþie. if T[i]>0 then inc(f^[j. Se dau gradele interioare ºi gradele exterioare pentru fiecare nod. MM[i]:=Min(MM[j].j]). seen:=seen+[i] end end end.flux) end. Indicaþie: .Q[i2]:=i.j]. i:=j end end.

Se va construi o reþea cu 2*N+2 noduri. astfel încât oricare muchie din M are un capãt în V1 ºi celãlalt capãt în V2. iar fiecare nod i’ va avea un arc spre destinaþie de capacitate di(i) (gradul interior). Iatã un exemplu de graf bipartit: . având la ieºire doar arce de capacitate 1. atunci din fiecare nod i vor pleca exact de(i) arce. Dacã în aceastã reþea se poate determina un flux maxim de valoare m=de(1)+de(2)+ +…+de(n)=di(1)+di(2)+ +…+di(n). iar în fiecare nod i’ vor intra exact di(i) arce (dacã într-un nod i intrã k unitãþi de flux. Deoarece din fiecare nod trebuie sã plece de(i) muchii (gradul exterior al nodului) fiecare nod i va avea un arc de la sursã cu capacitatea de(i). se va distribui pe k arce distincte). cu j≠ i. de la fiecare nod i se va duce un arc de capacitate 1 la fiecare nod j’. astfel: fiecare din cele N noduri va fi reprezentat de douã ori prin nodul i ºi nodul i’. Cuplaj maxim într-un graf bipartit Un graf G(V.M) este bipartit dacã mulþimea vârfurilor sale V poate fi partiþionatã în douã submulþimi V1 ºi V2.

M) este un cuplaj de cardinalitate maximã. . Exemple de cuplaje pe graf general ºi bipartit (muchiile cuplajului sunt desenate cu linia îngroºatã): Un cuplaj maxim într-un graf G(V.  C ≥  C1 . astfel încât oricare douã muchii din C sunt neadiacente (nu au un capãt comun). Un vârf v∈ V este cuplat dacã este incident la una din muchiile cuplajului. unde  C este numãrul de muchii al cuplajului C.M) ºi cuplajul C în acest graf. astfel încât pentru orice cuplaj C1. adicã un cuplaj C.M) este o submulþime C⊆ E. Un lanþ alternant în G este un lanþ ale cãrui muchii sunt alternativ în C ºi în M-C.Un cuplaj într-un graf G(V.  Avem graful G=(V. altfel. Teorema lui Berge: Un cuplaj C este maxim dacã ºi numai dacã nu existã un lanþ alternant între oricare douã vârfuri necuplate. v este necuplat.

atunci. Când nu mai existã un astfel de drum cuplajul este maxim conform teoremei lui Berge.Dacã avem un cuplaj C într-un graf G ºi un lanþ alternant P între douã vârfuri necuplate: P={n1. unde nk ∉ C ºi ck ∈ C. c2. Algoritmul nostru pentru determinarea cuplajului maxim într-un graf bipartit se bazeazã pe gãsirea drumurilor de creºtere ºi “creºterea” cuplajului pe aceste drumuri.c1. prin scoaterea muchiilor ck din cuplaj ºi introducerea în cuplaj a muchiilor nk se obþine un cuplaj C1 cu o muchie în plus faþã de C. Pentru eficienþa implementãrii vom gãsi iniþial un cuplaj oarecare printr-o metodã Greedy pe care îl vom îmbunãtãþi ulterior pânã când nu mai existã drumuri de creºtere..n}.n2.. determinãm iniþial printr-o metodã Greedy oarecare un cuplaj cu 2 muchii: . Din acest motiv un lanþ alternant între douã vârfuri necuplate se mai numeºte ºi drum de creºtere relativ la C. Exemplu: În graful din figurã. adicã cuplajul este maxim...

În cazul în care graful nu este conex. ºi NrV. Dacã nu se gãseºte un nod necuplat sau un drum de creºtere în componenta conexã curentã. Nodul i are NrV[i] vecini memoraþi în Graf[i].. atunci cuplajul asociat acestei componente este maxim. Vectorul Comp reþine componenta conexã din care face parte fiecare nod. Procedura care cautã acest drum de creºtere este DFS2. adicã “partea stângã” a grafului. Procedura Greedy determinã un cuplaj iniþial oarecare. Dacã toate cuplajele componentelor sunt maxime variabila Maxim devine TRUE ºi programul se terminã cu afiºarea cuplajului.k formeazã prima partiþie. . Numai când nu se mai poate creºte cuplajul în nici o componentã conexã. C este numãrul de componente conexe al grafului. Urmeazã determinarea componentelor conexe printr-o parcurgere în adâncime a fiecãrei componente conexe. se încearcã gãsirea unui drum de creºtere în fiecare componentã. Apoi.. trebuie repetatã operaþia de creºtere a cuplajului pentru fiecare componentã conexã în parte. avem un cuplaj maxim. n este numãrul de noduri al grafului. n.Gãsim drumul de creºtere 5-3-6-2-4-1 ºi incrementãm cuplajul pe acest drum obþinând un cuplaj maxim: Determinarea unui drum de creºtere se face printr-o cãutare în lãþime sau în adâncime. ºi nodurile k+1. Detalii de implementare a algoritmului: Graful va fi memorat sub formã de listã de noduri cu ajutorul variabilelor Graf. atâta timp cât cuplajul nu este maxim. Pornim cãutarea de la un nod necuplat ºi mergem alternativ pe muchii din cuplaj ºi din afara lui pânã gãsim un alt nod necuplat. Vectorul Max este de tip boolean. Prima operaþie a programului este citirea grafului prin procedura CitireGraf. Nodul de plecare pentru procedurã trebuie sã fie un nod necuplat. Nodurile 1. Procedura care face aceastã parcurgere în adâncime este DFS1. Max[i] reþine dacã cuplajul asociat componentei conexe i este maxim sau nu.n “partea dreaptã”.

cu 2-3 muchii mai puþin decât cuplajul maxim. k. Cuplat Parcurs Primul.. type Graf=array [1. :boolean. numãrul de muchii al cuplajului maxim ºi cu M1.1. Numãrul de muchii din cuplaj este reþinut în variabila M. M. Sã analizãm complexitatea acestui algoritm. i. n. În cazul cel mai defavorabil. GasitDrum Gasit. q q q Adunând. rezultã o complexitate totalã O((M-M1)(m+n)+(m+n)+m+m). :array [1. Maxim Max procedure CitireGraf. :array [1. numãrul de noduri. În practicã însã. caz în care timpul de calcul va fi O(n⋅ (m+n)). Sã vedem acum complexitatea fiecãrei operaþii efectuate de program: q operaþia de citirea a grafului este O(m) determinarea componentelor conexe este O(m+n). operaþia de cãutare a unui drum de creºtere se va efectua de M-M1 ori. Termenul dominant este (M-M1) ⋅ (m+n). :Graf.100] of boolean. :boolean. numãrul de muchii al grafului ºi cu n.. C.100] of integer. numãrul de muchii al cuplajului iniþial. Chiar dacã se face o parcurgere în adâncime pentru fiecare componentã conexã.100] of integer.100] of boolean. dacã se foloseºte un algoritm greedy bun.Vectorul Cuplat reþine cuplajul.. cuplajul determinat de acesta este foarte aproape de cuplajul maxim. j NrV. Dupã cum observãm timpul de calcul al algoritmului depinde de cât de bun este cuplajul iniþial. cuplajul iniþial va avea 0 muchii ºi cel final. program Cuplaj_Maxim. Timpul de calcul în acest caz se apropie de O(m+n). Comp. la un graf cu 10000 de noduri.. complexitatea adunatã a parcurgerilor este tot O(m+n). O parcurgere în adâncime într-un graf este O(m+n). . Notãm cu m. fiind vorba de o parcurgere în adâncime a fiecãrei componente procedura Greedy este O(m) fiecare cãutare a unui drum de creºtere este O(m+n). Dacã notãm cu M. astfel. var G n. De exemplu.. :integer. Cuplaj[i] este nodul cu care este cuplat nodul i. :array [1.100.

j]).Tip:integer). procedure DFS2(Nod. close(F).txt').i]). end. readln(F).k).n). readln(F. i :integer. begin assign(F. end else if Cuplat[Nod]=0 then begin GasitDrum:=true. j :integer. NrV[i]:=nrvec.'input. procedure DFS1(Nod:integer). reset(F). i. readln(F. i:=0. end. nrvec.var F :text. M:=M+1 end. begin Comp[Nod]:=C.i]]=0 then DFS1(G[Nod.G[i. begin Parcurs[Nod]:=True. for i:=1 to NrV[Nod] do if Comp[G[Nod.i]. if Tip=0 then if not Parcurs[Vec] and (Cuplat[Vec]<>Nod) then . for i:=1 to n do begin read(F. var i :integer.nrvec). while (i<NrV[Nod]) and not GasitDrum do begin i:=i+1. GasitDrum:=false. if Primul then begin Primul:=false. var Vec. end. Vec:=G[Nod. for j:=1 to nrvec do read(F.

0). if Tip=1 then if not Parcurs[Vec] and (Cuplat[Vec]=Nod) then DFS2(Vec. Cuplat[Vec]:=Nod. while (j<NrV[i]) and not Cup do begin j:=j+1. j :integer. begin for i:=1 to k do begin j:=0. FillChar(Comp. while i<=n do begin C:=C+1. Cup:=false. if NrV[i]=0 then Max[C]:=true. begin CitireGraf. Cup :boolean. var i. M:=M+1.1).j]]=0 then begin Cup:=true.0). Cuplat[G[i. FillChar(Max.0).2*n.j]]:=i end end end end. DFS1(i). procedure Greedy. end.DFS2(Vec.2*n. M:=0. FillChar(Cuplat. C:=0. end.j]. end. i:=1. if GasitDrum and (Tip=0) then begin Cuplat[Nod]:=Vec.2*n.0). Cuplat[i]:=G[i. if Cuplat[G[i. .

if not Gasit then Max[i]:=true. care decid sã organizeze. capodopere?". simultan ºi separat. de fapt. for i:=1 to k do if Cuplat[i]>0 then writeln(i.0). end. fiecare. while not Maxim do begin Maxim:=true.M). talk-show-uri având ca invitaþi câte doi critici. Opþiunile lor au stârnit discuþii aprinse în masele largi de cititori.while (i<=n) and (Comp[i]>0) do i:=i+1.' '. writeln('Muchiile din cuplaj sunt: '). Primul:=true.2*n. writeln('Numarul de muchii din cuplaj este '. pãrerea în chestiunea: "Care din cele r romane cu succes de public sunt. end. while (j<n) and not Gasit do begin j:=j+1.0). Momentul este speculat de cele p=c div 2 posturi de televiziune. end. if (Comp[j]=i) and (Cuplat[j]=0) then begin Gasit:=true. Gasit:=false. DFS2(j. end.Cuplat[i]) end. end. for i:=1 to C do if not Max[i] then begin Maxim:=false. j:=0. Aplicaþie Enunþ. FillChar(Parcurs. . Greedy. Un numãr de c critici literari ºi-au spus. if not GasitDrum then Max[i]:=true.

conþinând: q pe prima linie. cuprinse între 1 ºi r ºi separate prin spaþiu.. lista capodoperelor propuse de criticul i. numerele de ordine ale celor doi critici invitaþi de câte un post de televiziune dintre cele ce pot organiza talk-show în soluþia datã. Cum însã sponsorii ºi clienþii la spaþiile publicitare ale televiziunilor cer insistent ca înainte ºi dupã reclamele lor. q Ieºire: Fiºierul CRITICI. q Exemplu: CRITICI. cu i=1. încât niciuna nu-ºi permite sã "deþinã".Televiziunile plãtesc criticilor exclusivitatea .IN conþinând: q pe prima linie. r ºi lista de capodopere propuse de fiecare critic sã se determine numãrul maxim de emisiuni care se pot realiza ºi criticii care apar în aceste emisiuni. dar nu identice.deci nici un critic nu poate apãrea la douã posturi diferite -. în emisiuni sã nu mai existe certuri sau momente tensionate. separate prin spaþiu. fiecare post de televiziune e obligat sã invite critici cu pãreri apropiate. numãrul de critici c ºi numãrul de romane r. opþiunile sã difere pentru exact un roman. dacã poate.OUT 2 . asupra romanelor-capodoperã. decât exact doi critici.c. pe linia numãrul i+1. dar cu sume atât de mari. Dându-se c.OUT. Condiþia este ca pentru oricare doi critici invitaþi la acelaºi post. pe fiecare linie din urmãtoarele t.IN 4 1 3 4 2 4 1 4 1 1 CRITICI. Intrare: Fiºierul CRITICI. numãrul t de posturi de televiziune care pot realiza talk-show-uri în soluþia descrisã de fiºier. sub forma unui ºir de cel mult r numere naturale diferite între ele.

Rezultã cã între douã noduri din M1 sau douã noduri din M2 nu poate exista muchie. M1 ºi M2. Condiþia pentru ca doi critici sã poatã realiza o emisiune împreunã este ca opþiunile lor sã difere exact asupra unui roman. Construim un graf G cu c noduri în care existã muchie între nodurile i ºi j dacã criticul i poate realiza o emisiune împreunã cu criticul j.1 4 2 3 Olimpiada Naþionalã de Informaticã 1998 Clasa a XI-a Rezolvare. [ Capitolul 9] [ Cuprins ] . prima conþinând criticii ale cãror liste de capodopere au un numãr par de romane ºi cea de-a doua pe cei cu numãr impar. Rezultã cã unul dintre critici trebuie sã aibã cu un roman în plus în lista de capodopere faþã de celãlalt ºi restul listei sã fie comun. deci graful este bipartit. este clar cã problema se reduce la problema cuplajului bipartit maxim. Acum. iar aceste muchii nu trebuie sã aibã vreun capãt comun (sã nu existe un critic care apare în douã emisiuni). Avem un graf bipartit G cu c noduri. Doi critici din aceeaºi mulþime nu pot face o emisiune împreunã pentru cã diferenþa dintre numãrul de capodopere din listele lor este parã ºi dupã cum am stabilit mai sus aceastã diferenþã trebuie sã fie 1. Exemplu: criticul 1: 3 4 8 7 2 criticul 2: 9 4 8 3 2 7 Opþiunile lor diferã numai asupra romanului 9. Împãrþim cei t critici în douã mulþimi. Trebuie sã alegem un numãr maxim de muchii (emisiuni) din acest graf.

. Inc. 1992. 1996. The MIT Press. Addison-Wesley. E. Introduction to Algorithms. 1998. Swamy. Ronald L.Bibliografie 1. Leiserson. L&S Infomat. M. K. Donald. volume 2 of The Art of Computer Programming. 3. Seminumerical Algorithms. Third edition. Thomas H.N. 2. Tehnici de programare. Graphs: Theory and Algorithms. Charles E. Cormen. Tudor Sorin. Knuth. Rivest. 1990. Thulasiraman. 4. John Wiley & Sons.S. .

Sign up to vote on this title
UsefulNot useful