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.

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). Mihai Boicu. . Daniela Oprescu si Rodica Pintea.

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

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

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

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

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

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

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

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

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

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

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

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

ilustratã ºi în figurã: .....12} plasatã pe muchia i..12. i=2.. astfel încât suma numerelor aflate pe muchiile unei feþe sã fie aceeaºi pentru toate feþele.end else begin U[ST[k]]:=1. Problema 8 Enunþ. O soluþie corectã este cea de mai jos.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 . Considerãm muchiile numerotate ca în figurã: Ieºirea va fi într-un fiºier text având numele solcub. iar muchia 1 conþine valoarea 1 în toate soluþiile. k:=k+1 end end end. 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.

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

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

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

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

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

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

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.

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

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

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

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

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

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

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

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

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

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

[ Cuprins ] [ Capitolul 2] .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Problema 8 Enunþ. dar nu conþin un altul d. Rezolvare. Ls.j) end. K.' ').. Q. conþin exact un obiect dat. conþin cel puþin un obiect din p obiecte date f. conþin exact un obiect din p obiecte date e. if l=k+1 then begin sol:=sol+1. begin if st[l-1]<Li[l] then b:=Li[l] else b:=st[l-1].i. u N. procedure Back(l:integer). conþin r obiecte din p obiecte date. for i:=1 to K do write(ST[i]. nu conþin nici unul din q obiecte date c. Se considerã o mulþime de N elemente ºi un numãr natural K nenul. . :longint. Li. i sol ch :array[0. dar nu conþin alte q obiecte date.Back(1. R.1000] of integer. program Submultimi.b:integer. conþin p obiecte date. P. var ST. var i. :integer. 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. :char. b.

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

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

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

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

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

În final.5.1.4. începând cu poziþia 1.2.5.1> i=2.4.1. Când se întâlneºte un element A[i] >0.1> Procedeul continuã pânã la i=8.0.0.0.0.0. V[4]:=4 i=5 j=5 A[i]=A[5]=3 ⇒ V[5]:=5.4.0.0.0. se dã valoarea i urmãtoarelor A[i] poziþii din V.1.0.0. Parcurgem apoi A ºi V în paralel.0. incrementãm A[V[2]]=A[5] ⇒ A=<0. incrementãm A[V[4]]=A[5] ⇒ A=<0. Exemplu: n=8 V=<7.0. 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.2.4.7> .5. A=<1.2. 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.1> i=4.0.1. V[6]:=5.0.1> i=3.5.1. incrementãm A[V[3]]=A[4] ⇒ A=<0. incrementãm A[V[1]]=A[7] ⇒ A=<0.1.0.5> M=7 Prima parcurgere: i=1.0.Se parcurg apoi vectorii A ºi V în paralel.3.1>.5.2.0.

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

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

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

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

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

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

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

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

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

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

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

. 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ã. readln(x). Sã vedem ce se întâmplã pentru 7: 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.… ºi aºa mai departe. end.a:=a mod 10. for i:=1 to n do begin write('x['.. end.7. 5 sau 6. .. for i:=1 to O do C:=C*a mod 10. end. readln(n).']= ').. C:=1. Toate cifrele au perioade de lungime maxim 4. write('n= '). 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 ..21.49. .i.. 3. O:=O*x.63. writeln(C).63. . 1. 49. . O:=1. 9. De exemplu. Deci perioada lui 7 este 7. begin Citire.

9)).6.1.4..8.1). :longint. (1.8). considerând ca operaþie de bazã înmulþirea.3. var i. (5.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.9. (6.6. program Cifra_2. (1. const Perioada:array [0. 4K+2 sau 4K+3.7). (1. (6. x.5. pentru a ºti cifra în care se terminã puterea respectivã ⋅ a lui a.3] of integer= ((0. readln(n).0). readln(a).5. n :integer.4..0. begin write('a= '). var a O :integer.9.9.1. (6. (6.0.2. (1.2).4). Complexitatea acestei metode este O(n).9.5).0.7. a:=a mod 10.6).4.3). write('n= '). . 4K+1.1.6. procedure Citire.

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

Sã se gãseascã elementul din X sau Y.Indicaþie: Comparaþi timpii de calcul pe cazul defavorabil ºi favorabil. care are proprietatea cã este mai mare decât exact i-1 elemente din cele 2n conþinute de cei doi vectori. [ Capitolul 3] [ Cuprins ] [ Capitolul 5] . a cãror sumã este x..n] ºi Y[1. 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.. datã fiind o mulþime M cu n numere reale ºi un alt numãr real x. 5.n] ºi 1≤ i≤ 2n. determinã dacã existã sau nu 2 elemente în M.

adicã V[2]=8.. Procedeul continuã recursiv pentru restul vectorului.5]. anume 3. subarborele drept este arborele cartezian al subvectorului drept. subarborele stâng este arborele cartezian al subvectorului stâng (faþã de poziþia elementului din rãdãcinã). Subarborele stâng este la rândul lui un arbore cartezian al subvectorului V[1. 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. Iatã arborele rezultat: . alegem elementul minim din V[1. iar cel drept al lui V[7.. Ca rãdãcinã a subarborelui stâng (adicã fiu stânga al rãdãcinii). 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.5].Capitolul 5 Divide et Impera Problema 1 Enunþ.9]..

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

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

pentru cazul al treilea vom urmãri sã reducem problema tot la una din situaþiile 1 sau 2. var cx. 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ã.x1. acest lucru nu este posibil. împãrþind pãtratul în alte patru mai mici. Aplicând strategia generalã Divide et Impera. Function Inside(var x. Forward.y2 :double.Primele douã cazuri se pot rezolva banal.cy.y:double):byte. .y1. analiza matematicã ne spune cã aceastã metodã nu va calcula aria exactã. 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. begin if (sqr(x-cx)+sqr(y-cy)<=r*r) then Inside:=1 else Inside:=0 end.r. Program Cerc_si_Patrat. vom calcula aria comunã dintre pãtrat ºi cerc.x2.y2:double):double.y1. Evident. (în program 1e-3=0. Function Arie(var x1. pentru care vom rezolva aceeaºi problemã. Totuºi.001). decât dupã un numãr infinit de apeluri recursive.x2.

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

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

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

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

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

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

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

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

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

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

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

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

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

Matricea rarã O matrice rarã este o matrice.6) (6. Astfel. de cele mai multe ori este ineficient sã folosim alocarea staticã (se consumã prea multã memorie).(8. dacã apare nevoia modificãrii structurii telefon (de exemplu adãugarea unui câmp pentru memorarea adresei). dupã ce aceastã aplicaþie a fost în totalitate scrisã. în care majoritatea elementelor au valoarea 0.7) Cele douã metode care implementeazã operaþiile fundamentale pentru matrice (Citeste ºi Scrie) .1). 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.9). 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.2). Obiectul MatriceRara prezentat mai jos.4). Deoarece informaþia utilã propriu-zisã este de dimensiuni mult mai mici decât ale matricei.pentru informaþia dintr-o înregistrare.(4.(8.4) (1.(5. în general de dimensiuni mari.5) (3.4).(7. 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.

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

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

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

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

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

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

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

Iatã o aºezare a pieselor prin care putem umple perfect un pãtrat de 6x6. 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.' lei'). writeln('Am folosit: '). while S>0 do begin i:=i+1. S:=S-M[i]*F[i] end. Prof. FillChar(F. Gheorghe Petrov – Universitatea Timiºoara Concursul Naþional de Informaticã Lugoj 1998 Rezolvare. 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.Citire. QuickSort(1. end.' monede de '.M[i]. F[i]:=S div M[i]. folosind cel puþin o piesã de fiecare tip: .0). Rezultã cã pentru a putea face acoperirea n trebuie sã fie par.n). Problema 3 Enunþ. for i:=1 to n do writeln(F[i].2*n.

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

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

dacã este posibil.mare decât cele k-1 elemente de pe linia sa.k].. cã suma minimã a elementelor de pe coloana k este ordine.j]:=(i-1)*k+j-k+T[n. k: '). Problema 5 Enunþ. j :array [1.. . var T n. Pentru ca un segment (Pi. un poligon convex care sã aibã ca vârfuri aceste puncte. .k] . în program Tablou. for i:=1 to n do for j:=k+1 to n do T[i.100] of integer. begin write('n.n. Se dau n puncte în plan. Sã se construiascã. writeln end end.1..j]. Numerele de la 1 la nk se aºeazã în A[1.1. readln(n. for i:=1 to n do begin for j:=1 to n do write(T[i. k. Rezultã.j]:=(i-1)*k+j.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. :integer.100.k).. for i:=1 to n do for j:=1 to k do T[i.' '). Rezolvare. i.

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

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

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

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

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

C[1]:=2. Vom nota cu A[i] numãrul de cuvinte de lungime i care se terminã în a sau b. 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. dacã cuvântul se terminã cu a litera b. var N. 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.c sau d.. 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.c.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. care respectã condiþiile problemei. Câte cuvinte de lungime N se pot forma cu litere din alfabetul {a. readln(N).C begin write('N=').Capitolul 8 Programare dinamicã Problema 1 Enunþ.c sau d. A[1]:=2. Sã considerãm cã am construit deja un cuvânt de lungime N-1.100] of comp. ºi cu C[i] numãrul de cuvinte care se terminã în c sau d. :integer. :array[0.b. .i A.

ºi anume numarul i de partide câºtigate de prima echipã ºi numãrul j de partide câºtigate de a doua echipã.5000 0. Pentru desemnarea campioanei. Fiºierul de intrare conþine pe fiecare linie câte un set de date sub formã de triplete "n i j". i numãrul de partide câºtigate de echipa 1.for i:=2 to N do begin C[i]:=2*C[i-1]+2*A[i-1].0000 0. Analiºtii sportivi sunt interesaþi în estimarea ºansei pe care o are una dintre echipe la un moment dat oarecare al turneului. Problema 2 Enunþ. A[i]:=A[i-1] +2*C[i-1] end. n partide. sã se calculeze probabilitatea ca echipa 1 sã câºtige turneul ºi sã devinã campioanã. În orice partidã.7500 . 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. Titlul de "Campioanã Absolutã" al unei discipline sportive este disputat de douã echipe cu forþã de joc egalã. 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ã. cu patru zecimale exacte. writeln(A[N]+C[N]:20:0) end. Pentru fiecare set de date trebuie afiºatã probabilitatea ca echipa 1 sã devinã campioanã. unde n reprezintã numãrul de partide ce trebuie câºtigate pentru a deveni campioanã (n≤45). cele douã echipe au ºanse egale de câºtig indiferent de rezultatele din partidele anterioare. de a deveni campioanã. Echipele sunt desemnate cu numerele 1 ºi 2. echipa care câºtigã prima. ªtiindu-se situaþia turneului la un moment dat. iar j numãrul de partide câºtigate de echipa 2. Orice partidã se terminã cu victoria uneia dintre echipe.0000 0.

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

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

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

j-1] Max ( A[i-1.100] of integer. :array[0.2 cu subºirul comun 1.X2. În cazul în care se adaugã un singur element la sfârºitul unuia din ºiruri se poate întâmpla.8.3.Xn ºi Y1. dacã se adaugã acelaºi element la sfârºitul ambelor ºiruri.4.9. Elementele care se ºterg nu trebuie sã fie neapãrat pe poziþii consecutive în ºir.Ym un subºir comun a celor douã ºiruri este un ºir care este subºir ºi pentru primul ºir ºi pentru al doilea.3. altfel Program SubsirComun.2.Y A N. Un subºir al unui ºir X1. Sã studiem ce se întâmplã cu acesta în cazul în care se mai adaugã elemente celor douã ºiruri. ca lungimea subºirului comun sã creascã cu 1: Exemple: 1) 1.3 ºi 1. noua soluþie va fi 4.7. var X. obþinem urmãtoarea relaþie de recurenþã: A[i.0.….100.j. Rezolvare.2..9.Xn este un ºir care se obþine ºtergând zero sau mai multe elemente din ºirul iniþial.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.9. :integer. sau nu.2.4.4 cu subºirul comun 1.4. dacã avem ºirurile 4.7.2..1 este un subºir al ºirului 2.X2. Dacã notãm cu A[i.…. De exemplu: 2. 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).2 ºi 1.3 2) 1. În cel mai simplu caz.100] of integer. Întotdeauna. Dându-se douã ºiruri X1. obþinând astfel douã ºiruri ce au acelaºi ultim element.….2 Orice element am adãuga.1.3.Enunþ. subºirul comun va pãstra aceeaºi lungime. Dacã adãugãm un 8 în finalul ambelor ºiruri.5. De exemplu.j].Y2. Sã presupunem cã ºtim deja cel mai lung subºir comun al celor douã ºiruri de lungime N ºi respectiv M.. 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).2. la oricare ºir.7 ºi 1.k :array[1.j-1] ) dacã X[i]=Y[j]. .j] = = 1 + A[i-1. A[i.6 cel mai lung subºir comun va fi 4.i.M.1 el obþinându-se prin ºtergerea lui 4 ºi a primei apariþii a lui 1 în ºirul iniþial.2.9.2 Dacã adãugãm un 3 la ºirul al doilea noul subºir comun va deveni 1. Problema constã în a gãsi un subºir de lungime maximã a douã ºiruri date.

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

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.7} ºi {1.i.'OUTPUT. dacã pânã la etapa curentã avem S[5]=3 ºi S[9]=8.6} USACO.sizeof(S). deci N*(N-1)/4.k S begin fillchar(S.7} ºi {1. Problema este analogã cu problema rucsacului. 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.Ieºirea se va face în output.4. assign(fil. doar S[0] va primi valoarea 1.7} ºi {2.5. :array[0.6} {1.5.5.6. ºi vom obþine încã 3 soluþii cu suma 9.2. :text.N). 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ã. i care va spori numãrul de soluþii obþinute folosindu-le doar pe primele i-1. Exemplu: Pentru N=7.'INPUT. deci numãrul de soluþii va fi 0).4. assign(fil.3.j.4.txt ºi va consta în numãrul de partiþionãri.0). Spring Open 1998 Rezolvare.5} {2. Program Submultimi. la fiecare etapã considerându-se un nou numãr. close(fil). .1000] of comp. reset(fil). :integer.TXT').7} ºi {3.4.3. sunt patru soluþii: {1.TXT'). Vectorul S este iniþializat cu 0.6} {3. De exemplu.2. var fil N. Vectorul va fi completat progresiv. 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]. este imposibil sã formãm douã submulþimi cu sume egale.. readln(fil.

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

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

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

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

TXT'). while not seekeoln(fil) do begin inc(nf).si. nf:=0.N).s[i]]^[0]:=0 :text. readln(fil. while c=' ' do read(fil.'DICTION. type vec=array[0.200.'z'] of ^vec. cuvântul face parte din limbaj. :char..f+1).fo N.s[i]]. if (f=1) and (tmp[1]=0) then A[j. r:=length(s). reset(fil). read(fil..c). :array[1. . s:=s+c end. while not seekeoln(fil) do begin read(fil..si).j.i. 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.r. :string.s2 begin assign(fil. getmem(A[j. read(fil. readln(fil).'a'.0). :vec.A[j.f s c A sf. readln(fil.200] of byte.c). var fil. :integer.tmp[f]) end.s[i]]^. Program Limbaj. for i:=1 to r do for j:=1 to n do begin f:=0. move(tmp.sizeof(A).nf.pentru a putea efectua aceleaºi operaþii ºi la etapa urmãtoare. while not eoln(fil) do begin inc(f). Dupã ce s-a parcurs întreg cuvântul.s1. fillchar(A.f+1).sf[nf]) end.tmp.

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

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

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

care sã conþinã ºi nodurile expandate ºi cele neexpandate. Dupã cum se cunoaºte. ajungând sã conþinã chiar zeci de mii de noduri. pentru a familiariza cititorul cu o nouã modalitate de implementare a tehnicii Branch and Bound. Douã cãsuþe sunt ocupate cu numãrul 0. Se cere de asemenea ca acest ºir sã fie optim. eficientizarea cãutãrii în spaþiul soluþiilor. Dat fiind faptul cã operaþiile cu pointeri sunt destul de lente. Vom încerca sã înlãturãm ºi acest neajuns. aceastã listã a configuraþiilor devine ºi ea foarte mare. 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. apare o singurã datã în cadrul pãtratului. inserare ºi ºtergere din listã. deci ºi a nodurilor deja expandate. Evident algoritmul rãmâne acelaºi care este cunoscut cititorului din parcurgerea manualului. discutatã ºi în manual. la dreapta. O primã idee simplã ar fi sã folosim o singurã listã. ªtiind cã 0 îºi poate schimba poziþia cu orice numãr aflat deasupra. 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 operaþiile cele mai frecvente din algoritm sunt cele care manipuleazã o listã. 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ã. dar ºi scurtarea codului. Aceastã secvenþã s-ar reduce doar la schimbarea unei valori din false în true. În acest mod am elimina toate operaþiile de ºtergere din lista open (a nodurilor neexpandate) ºi de inserare în lista close. la stânga sau jos. Fiecare numãr natural. modalitate ce va fi folositã ºi în celelalte rezolvãri. Reluãm aici problema pãtratului. în special. Tehnica ce urmeazã a fi prezentatã porneºte de la o idee . Rezolvare. Fiecare cãsuþã conþine un numãr între 1 ºi N*N-2. reducerea timpului de calcul. în sensul cã trebuie sã se ajungã la configuraþia finalã într-un numãr minim de mutãri. Se considerã un pãtrat cu NxN cãsuþe. 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. ºi pentru a le distinge sã folosim un nou câmp care sã indice tipul nodului. Deoarece pentru date de intrare mai mari. rezultã cã cea mai mare parte din timpul de calcul este consumatã de operaþiile de parcurgere. 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ã. în raport cu poziþia în care se aflã numãrul 0. diferit de 0. timpul de calcul creºte considerabil. iar lista fiind atât de mare.Capitolul 9 Branch and Bound Problema 1 Enunþ. Cele câteva modificãri în implementare vizeazã.

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

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

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

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

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ã. Bucureºti. vom evidenþia doar câteva aspecte care þin strict de particularitatea problemei. . 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. Dupã cum se ºtie. Î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.Atenþie: se vor scrie întâi operaþiile pe linii. funcþia euristicã trebuie în mod obligatoriu sã fie optimistã adicã sã prezicã un efort mai mic decât cel real. univ. Observaþie: pentru k=0. se va lista una din ele. Adrian Atanasiu "Marele premiu PACO". pentru a asigura optimalitatea soluþiei. Deoarece rezolvarea se bazeazã pe aceeaºi linie cu precedenta. Problema constã în faptul cã o singurã operaþie poate schimba semnul mai multor sume. 20-21 iunie 1997. ordonate crescãtor. De aceea a fost aleasã o metodã mai puþin deterministã. clasa X Rezolvare. a doua linie este goalã. dr. º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. 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ã. urmate de operaþiile pe coloane. Dacã sunt mai multe soluþii.

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

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

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

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

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

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

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

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

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

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

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

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

Primul caracter din ºir corespunde întotdeauna biluþei comune din partea superioarã a figurii. respectiv gri. Sibiu 1996 enunþ reformulat . Primul inel va fi cel ce conþine în figurã biluþele albe. Deci configuraþia din figurã va fi reprezentatã astfel: GAAAAAAAAGAAA GNNNGNNNNNNNN Ieºirea se va face pe ecran. tipãrind configuraþia biluþelor dupã fiecare rotire. celelalte urmând parcurgerea inelului în sens orar. Problemã datã la selecþia lotului pentru Balcaniadã.in ºi conþin douã linii. pe fiecare aflându-se un ºir de 13 caractere ce codificã aºezarea biluþelor astel: A. Datele de intrare se citesc din fiºierul bilute.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ã. neagrã.

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

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

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

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

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

ritmul în care materialul se miºcã. Reþelele de transport pot fi folosite ca model teoretic pentru reþelele de conducte.j . Fiecare arc dintr-o reþea poate fi imaginat ca o conductã prin care circulã material. Transportul de material în oricare punct din sistem este.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. unde este consumat. semnificând o anumitã cantitate într-o anumitã perioadã de timp). 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. unde este produs. pãrþi ale liniilor de asamblare.v)=0. cu sursa s ºi destinaþia t. . Imaginaþi-vã traseul materialului printr-un sistem de la o sursã.i = Σj∈V fi. ∀ i∈V-{s.v) ≥0. sunt doar punctele de intersecþie ale conductelor: materialul trece prin ele fãrã a fi colectat. intuitiv.t}. care reprezintã “ritmul” maxim la care materialul poate circula prin conductã.v) ∉ E. i. informaþia care circulã prin reþelele de comunicaþie. în afarã de s ºi t. sau curent electric de 20 amperi care poate trece printr-un conductor. este un graf orientat în care fiecare muchie (u. adicã fluxul transportat pe orice arc trebuie sã fie nenegativ ºi subcapacitar.E). Fiecare conductã are o capacitate specificatã. adicã conservarea fluxului. Nodurile dintr-o reþea. ca de exemplu 2000 metri cubi de lichid pe orã printr-o conductã. Dacã (u. la o destinaþie. vom considera c(u. Un flux în G este o funcþie f:VxV→R care are urmãtoarele proprietãþi: q 0 ≤ f ≤ c . 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”. curentul care circulã prin reþelele electrice. 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. Sursa produce materialul într-un anumit ritm ºi destinaþia îl consumã în acelaºi ritm.v) ∈ E are o capacitate pozitivã c(u.j q Σj∈V fj.j i.

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

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

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

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

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

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

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

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

M) este bipartit dacã mulþimea vârfurilor sale V poate fi partiþionatã în douã submulþimi V1 ºi V2. de la fiecare nod i se va duce un arc de capacitate 1 la fiecare nod j’. astfel încât oricare muchie din M are un capãt în V1 ºi celãlalt capãt în V2. atunci din fiecare nod i vor pleca exact de(i) arce. iar fiecare nod i’ va avea un arc spre destinaþie de capacitate di(i) (gradul interior). se va distribui pe k arce distincte). 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. având la ieºire doar arce de capacitate 1. 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.Se va construi o reþea cu 2*N+2 noduri. 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). cu j≠ i. Iatã un exemplu de graf bipartit: .

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

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. c2.n}. determinãm iniþial printr-o metodã Greedy oarecare un cuplaj cu 2 muchii: .Dacã avem un cuplaj C într-un graf G ºi un lanþ alternant P între douã vârfuri necuplate: P={n1.. atunci. 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ã. Când nu mai existã un astfel de drum cuplajul este maxim conform teoremei lui Berge.. 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. unde nk ∉ C ºi ck ∈ C.c1..n2. adicã cuplajul este maxim.

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

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

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

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

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

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

Trebuie sã alegem un numãr maxim de muchii (emisiuni) din acest graf. M1 ºi M2. Exemplu: criticul 1: 3 4 8 7 2 criticul 2: 9 4 8 3 2 7 Opþiunile lor diferã numai asupra romanului 9. 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. Împãrþim cei t critici în douã mulþimi. 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. iar aceste muchii nu trebuie sã aibã vreun capãt comun (sã nu existe un critic care apare în douã emisiuni). Rezultã cã între douã noduri din M1 sau douã noduri din M2 nu poate exista muchie.1 4 2 3 Olimpiada Naþionalã de Informaticã 1998 Clasa a XI-a Rezolvare. [ Capitolul 9] [ Cuprins ] . deci graful este bipartit. Acum. Condiþia pentru ca doi critici sã poatã realiza o emisiune împreunã este ca opþiunile lor sã difere exact asupra unui roman. este clar cã problema se reduce la problema cuplajului bipartit maxim. 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. Avem un graf bipartit G cu c noduri.

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

Sign up to vote on this title
UsefulNot useful