P. 1
Batog, Culegere de probleme de informatica [RO]

Batog, Culegere de probleme de informatica [RO]

|Views: 855|Likes:
Published by Lorena Strechie

More info:

Published by: Lorena Strechie on Mar 08, 2011
Copyright:Attribution Non-commercial

Availability:

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

02/14/2013

pdf

text

original

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.

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. Dan Grigoriu. . Daniela Oprescu si Rodica Pintea.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.

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

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

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

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

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

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

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

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

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

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

[ Cuprins ] [ Capitolul 2] .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

var N. dintre care L femei.1000] of integer. n. write('P=')..s-i) end end. Back(k+1.s:integer). Problema 3 Enunþ. S. 1. begin if (S=0) and (k=p+1) then begin for i:=1 to k-1 do write(ST[i].100] of integer. var i:integer. Back(1. 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. i ST :integer. Sã se precizeze toate delegaþiile care se pot forma. Procedure Back(k. l:integer. c. Dintr-un grup de N persoane. begin write('N='). :array[1. . K. p. P. dintre care P femei. writeln end else for i:=1 to S do begin ST[k]:=i. var ST:array [0. readln(N). am ajuns la o soluþie.' '). 3). care se tipãreºte. program Delegatie. Rezolvare.program Descompuneri.. Pr.N) end. Variabilele Min ºi Max. La trecerea pe nivelul c+1. k. readln(P).

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

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

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

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

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

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

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

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

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

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

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

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

0. Exemplu: n=8 V=<7.Se parcurg apoi vectorii A ºi V în paralel.1> i=3. 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.0.1. cu i indicele de parcurgere al lui A ºi j indicele de parcurgere al lui V: i=1 j=1 A[i]=A[1]=1 ⇒ V[1]:=1 i=2 j=2 A[i]=A[2]=1 ⇒ V[2]:=2 i=3 j=3 A[i]=A[3]=0 i=4 j=3 A[i]=A[4]=2 ⇒ V[3]:=4.4.0. V[6]:=5.4.0.0.0.0.2.2.4.0.5. V[4]:=4 i=5 j=5 A[i]=A[5]=3 ⇒ V[5]:=5.3.1. se dã valoarea i urmãtoarelor A[i] poziþii din V. A=<1.1> Procedeul continuã pânã la i=8. incrementãm A[V[3]]=A[4] ⇒ A=<0.2.0.5.1.5.1> i=2.0.1.5.0. incrementãm A[V[1]]=A[7] ⇒ A=<0. Parcurgem apoi A ºi V în paralel.0.5> M=7 Prima parcurgere: i=1. începând cu poziþia 1. Când se întâlneºte un element A[i] >0.4.1.0. incrementãm A[V[4]]=A[5] ⇒ A=<0.1> i=4.2. incrementãm A[V[2]]=A[5] ⇒ A=<0.1>.7> .1. În final.0.0.0.0.5.

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

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

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

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

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

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

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

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

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

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

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

49. end.… ºi aºa mai departe. orice putere a unui numãr terminat în cifra 0 se va termina cu cifra 0. begin Citire. . 9. C:=1. . end. De exemplu. 3. La fel ºi pentru numerele terminate în 1.']= ')... readln(n).. Toate cifrele au perioade de lungime maxim 4. 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ã.a:=a mod 10.7. .63. writeln(C).49. .21. . Deci perioada lui 7 este 7.. readln(x). O:=1. end. 5 sau 6. 1. Sã vedem ce se întâmplã pentru 7: 7. O:=O*x. for i:=1 to O do C:=C*a mod 10. for i:=1 to n do begin write('x['. 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 .63. write('n= ').i..

begin write('a= ').2).0.4.0).1. write('n= '). a:=a mod 10. :longint. (1.7. 4K+1..4.4). readln(n). program Cifra_2.8.9)).3).9. n :integer. (1. considerând ca operaþie de bazã înmulþirea. (6.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. const Perioada:array [0.3] of integer= ((0. readln(a).6).5).1.2. procedure Citire.0. Complexitatea acestei metode este O(n). (5.1. var a O :integer. (1.8). pentru a ºti cifra în care se terminã puterea respectivã ⋅ a lui a.4.9. x. (6.7). (6.9.3. . (1.5. 4K+2 sau 4K+3..6.9.0. var i.1).6.6. (6.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

s2) and (q^.n<>nil do begin if (q^. i:=i^.s2=z^.n<>nil do begin if (i^. cut:=q end. repeat min:=maxint.n. k:=i end.s1) and (q^.s2.n:=z.n end else dispose(z).s2[1] end. readln.g<min) and (not i^. i:=b. if cut=nil then begin t^.s1[1]:=z^. cut:=t^.n end. q:=b.e) then begin min:=i^.n end. cut:=nil.f). begin Citire. q:=q^. while q^.g. t:=t^.g>z^. expand(k) until memavail<500. h(z). halt end end end. k:=b.end else begin shift(z^.g) then begin q^.s2[5].h+i^. while i^.s1=z^. writeln(‘Nu exista solutie’) end. . z^. if cut^.t:=p.s1[10]:=z^. 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).h+i^. z^. Problemã propusã Enunþ.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

You're Reading a Free Preview

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