Sunteți pe pagina 1din 180

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.

Capitolul 1

Backtracking

Problema 1

Enunþ. Avem la dispoziþie 6 culori: alb, galben, roºu, verde, albastru ºi negru. Sã se precizeze toate drapelele tricolore care se pot proiecta, ºtiind cã trebuie respectate urmãtoarele reguli:

orice drapel are culoarea din mijloc galben sau verde;

cele trei culori de pe drapel sunt distincte.

Exemple: "alb galben roºu", "roºu verde galben"

Rezolvare. Folosim o stivã cu 3 nivele, reprezentând cele trei culori de pe drapel ºi codificãm culorile prin numere:

1 – alb

2 – galben

3 – roºu

4 – verde

5 – albastru

6 - negru

Folosim un vector auxiliar fol de tip boolean:

fol[i]= TRUE, dacã culoarea i a fost folositã în drapel deja;

FALSE, dacã nu a fost folositã.

Condiþiile care trebuie îndeplinite pentru aºezarea unei anumite culori c pe un nivel al stivei sunt:

fol[c]=FALSE, adicã sã nu fi folosit deja culoarea respectivã în drapel;

dacã nivelul stivei este 2 (adicã culoarea din mijloc), atunci culoarea

trebuie sã fie 2 sau 4 (galben sau verde).

program Drapel;

const Culoare:array [1

6]

of string[10]=('alb','galben',

'rosu','verde', 'albastru','negru');

var ST

:array [1

3]

of integer;

Fol

:array [1

6]

of boolean;

k, i

:integer;

procedure Printsol; var i:integer; begin for i:=1 to 3 do write(Culoare[ST[i]],' '); writeln end;

function Valid:boolean; begin if Fol[ST[k]] then Valid:=false else if k<>2 then Valid:=true else Valid:=(ST[k] in [2,4])

end;

begin for i:=1 to 6 do Fol[i]:=false;

k:=1;

ST[k]:=0;

while k>0 do begin repeat

ST[k]:=ST[k]+1

until (ST[k]>6) or Valid; if ST[k]<=6 then if k=3 then Printsol else begin Fol[ST[k]]:=true;

k:=k+1;

ST[k]:=0

end

else

begin

k:=k-1;

if k>0 then Fol[ST[k]]:=false end end

end.

Problema 2

Enunþ. Sã se descompunã un numãr natural N, în toate modurile posibile, ca sumã de P numere naturale

(P N).

Rezolvare. Folosim o stivã cu P nivele, unde fiecare nivel ia valoarea unui termen din sumã. 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. Condiþia de validitate devine: S+ST[k] N.

Pentru a evita afiºarea descompunerilor identice, cu ordinea termenilor schimbatã, forþãm ordinea crescãtoare (nu strict crescãtoare!) a termenilor din sumã, prin iniþializarea unui nivel din stivã cu valoarea nivelului anterior.

program Descompuneri;

var n, p, k, S, i

:integer;

ST

:array [0

1000]

of integer;

Procedure Printsol; var i:integer; begin for i:=1 to p do write(ST[i],' '); writeln end;

begin write('N= '); readln(n); write('P= '); readln(p);

S:=0;

k:=1;

fillchar(ST,sizeof(ST),0);

while k>0 do begin

ST[k]:=ST[k]+1;

if S+ST[k]<=N then if (k=p) and (S+ST[k]=N) then Printsol else begin S:=S+ST[k];

k:=k+1;

ST[k]:=ST[k-1]-1

end else begin

k:=k-1;

S:=S-ST[k]

end

end

end.

Problema 3

Enunþ. Dintr-un grup de N persoane, dintre care P femei, trebuie formatã o delegaþie de C persoane, dintre care L femei. Sã se precizeze toate delegaþiile care se pot forma.

Rezolvare. Vom nota femeile cu numerele de la 1 la P, ºi bãrbaþii de la P+1 la N. Pentru rezolvarea

problemei folosim o stivã cu C nivele, unde nivelele 1

nivelele L+1

L

conþin indicii femeilor din delegaþia curentã, ºi

C

indicii bãrbaþilor.

st[i] poate lua valori între st[i-1] ºi MAX, unde:

MAX=p, pentru i<=l;

MAX=n, pentru i>l.

st[l] poate lua valori între p ºi n.

Motivul pentru care st[i] se iniþializeazã cu st[i-1] este evitarea duplicãrii delegaþiilor, prin forþarea ordinii strict crescãtoare în fiecare delegaþie.

Limita pânã la care se pot majora elementele de pe un nivel este P pentru nivelele 1

L (femeile) ºi N

st[l] nu se iniþializeazã cu st[l-1], ci cu P, pentru cã este primul dintre bãrbaþi ºi bãrbaþii au indicii între P+1 ºi N.

program Delegatie;

var st

:array [1

100]

of integer;

MAX, k, N, P, C, L

:integer;

procedure Printsol; var i:integer; begin for i:=1 to C do write(st[i],' '); writeln end;

begin write('N, P, C, L: '); readln(N,P,C,L);

k:=1;

st[1]:=0;

MAX:=P; while k>0 do begin

st[k]:=st[k]+1;

if st[k]<=MAX then if k=C then Printsol else begin

k:=k+1;

if k<>L+1 then st[k]:=st[k-1] else begin st[k]:=P; MAX:=N end end else begin

k:=k-1;

if k=l then MAX:=P end end

end.

Problema 4

Enunþ. 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. Persoanei i se pun la dispoziþie N obiecte. Pentru fiecare obiect se cunoaºte greutatea (mai micã decât capacitatea rucsacului) ºi câºtigul obþinut în urma transportului sãu.

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

Datele de intrare se vor citi din fiºierul input.txt. Pe primele douã linii ale fiºierului sunt N ºi G, numãrul de obiecte ºi capacitatea rucsacului. Pe fiecare din urmãtoarele N linii se aflã douã numere întregi reprezentând greutatea ºi câºtigul asociat obiectului respectiv.

Rezolvare. În vectorii gr ºi c avem greutatea ºi câºtigul obþinut în urma transportãrii fiecãrui obiect. CG este câºtigul obþinut în urma transportãrii obiectelor aºezate în stivã pânã la nivelul curent ºi S, greutatea acestor obiecte.

Condiþiile care trebuie îndeplinite pentru aºezarea unui obiect i pe nivelul k al stivei sunt:

1. Pus[i]=FALSE, adicã sã nu fi aºezat deja obiectul respectiv în stivã ºi

2. S+gr[ST[k]]<=G, adicã greutatea obiectelor sã fie mai micã sau cel mult egalã cu greutatea maximã care poate fi transportatã cu rucsacul.

În variabilele Cmax, Gmax ºi STmax (câºtigul, greutatea ºi obiectele) reþinem pe timpul execuþiei programului cea mai bunã soluþie gãsitã.

Notã: Rezolvarea optimã a acestei probleme poate fi gãsitã în manualul “Tehnici de programare” de Tudor Sorin la capitolul “Programare dinamicã”.

program Rucsac;

var i,k,n,G,CG,S,Cmax,Gmax,NOb :integer;

c,gr,ST,STmax

:array [1

100]

of integer;

Pus

:array [1

100]

of boolean;

procedure Citire;

var f

:text;

i

:integer;

begin

assign(f,'input.txt');

reset(f);

readln(f,N);

readln(f,G); for i:=1 to n do readln(f,gr[i],c[i]); close(f) end;

function Valid:boolean; begin if Pus[ST[k]] then Valid:=false else if S+gr[ST[k]]<=G then begin S:=S+gr[ST[k]]; CG:=CG+c[ST[k]]; Valid:=true end else Valid:=false

end;

begin Citire;

k:=1;

for i:=1 to n do Pus[i]:=false;

ST[k]:=0;

CG:=0;

S:=0;

Cmax:=0;

while k>0 do begin repeat

ST[k]:=ST[k]+1

until (ST[k]>n) or Valid; if ST[k]<=n then begin if CG>Cmax then begin Cmax:=CG; NOb:=k; for i:=1 to k do STmax[i]:=ST[i]; Gmax:=S; end; Pus[ST[k]]:=true;

k:=k+1;

ST[k]:=0

end else begin

k:=k-1;

if k>0 then

begin

S:=S-gr[ST[k]];

CG:=CG-c[ST[k]];

Pus[ST[k]]:=false

end;

end

end

writeln('Cistigul maxim: ',Cmax); writeln('Greutate transportata: ',Gmax); write('Obiectele transportate: '); for i:=1 to NOb do write(STmax[i],' '); writeln end.

Problema 5

Enunþ. Fiind dat un numãr natural N, se cere sã se afiºeze toate descom-punerile sale ca sumã de numere prime.

Rezolvare. Pentru eficienþã, vom calcula mai întâi toate numerele prime mai mici ca N; acestea vor fi memorate în vectorul Numar.

Rutina de backtracking este similarã cu cea din problema 2, cu excepþia faptului cã termenii pot lua valori

din Numar[1

P]

ºi cã descompunerea nu trebuie sã aibã un numãr fix de termeni.

Program SumaPrime;

var ST, Numar

:array[0

1000]

of integer;

n, K, P, S, i

:integer;

Function Prim( X : integer):boolean; var i:integer; begin Prim:=true;

i:=2;

while i*i<=X do begin if X mod i=0 then begin Prim:=false; i:=X

end

end;

end;

i:=i+1

procedure Printsol; var i:integer; begin for i:=1 to k do write(Numar[ST[i]],' '); writeln end;

begin

fillchar(ST,sizeof(ST),0);

fillchar(Numar,sizeof(Numar),0);

write('N='); readln(N);

P:=0;

for i:=2 to N do if Prim(i) then begin inc(P); Numar[P]:=i end;

k:=1;

S:=0;

while k>0 do begin

ST[k]:=ST[k]+1;

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

k:=k+1;

ST[k]:=ST[k-1]-1

end else begin

k:=k-1;

S:=S-Numar[St[k]]

end

end

end.

Enunþ. ("Attila ºi regele") Un cal ºi un rege se aflã pe o tablã de ºah. Unele câmpuri ale tablei sunt "arse", poziþiile lor fiind cunoscute. Calul nu poate cãlca pe câmpuri "arse", iar orice miºcare a calului face ca respectivul câmp sã devinã "ars". Sã se afle dacã existã o succesiune de mutãri permise (cu restricþiile de mai sus), prin care calul sã poatã ajunge la rege ºi sã revinã la poziþia iniþialã. Poziþia iniþialã a calului, precum ºi poziþia regelui sunt considerate "nearse".

Rezolvare. Vom folosi o stivã triplã cu elemente de tip pozitie: l,c - linia ºi coloana poziþiei curente, mut - mutarea fãcutã din aceastã poziþie.

Vectorul Mutari (constant) conþine cele 8 mutãri pe care le poate efectua un cal (modificãrile coordonatelor calului prin mutarea respectivã; vezi ºi problema 4).

Pentru început vom citi datele de intrare cu procedura Citire. Fiºierul de intrare input.txt trebuie sã aibã forma:

N // pe prima linie, n - numãrul de linii (coloane) al tablei

lCal cCal // pe urmãtoarele douã linii, poziþia iniþialã a

lRege cRege // calului ºi a regelui

a 11 a 12 a 13

a 1n // pe urmãtoarele n linii, tabla de ºah

// a ij = 0, dacã pãtrãþelul respectiv e ars

a n1 a n2 a n3

a nn // = 1, dacã nu este ars

Vom face o "bordare" a tablei de ºah cu douã rânduri de pãtrãþele "arse". 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.

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

:integer;

T

:array [-1

22] 22,-1

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

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

1234567892 = 246913578 ; 1234567894 = 493827156

1234567897 = 864197523 ; 1234567898 = 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).

program Numeric;

var ST,U

:array[0

10]

of integer;

K,i,f

:integer;

e

:double;

t

:set of char;

s

:string;

begin

k:=1;

fillchar(ST,sizeof(ST),0);

fillchar(U,sizeof(U),0);

ST[9]:=9;

U[9]:=1;

while k>0 do if k=9 then begin

e:=0;

for f:=1 to 9 do e:=e*10+ST[f];

{ se valideazã o posibilã soluþie }

e:=e*3;

{ se înmulþeºte numãrul cu 3

}

str(e:0:0,s);

{ ºi se verificã dacã cifrele

}

t:=[];

{ rezultatului sunt distincte

}

i:=0;

if length(s)=10 then exit; for f:=1 to length(s) do if not (s[f] in t) then begin

i:=i+1;

t:=t+[s[f]] end; if i=9 then begin for f:=1 to 9 do write(ST[f]); writeln end;

k:=k-1

end else begin

U[ST[k]]:=0;

repeat

ST[k]:=ST[k]+1

until (U[ST[k]]=0) or (ST[k]>8); if ST[k]>8 then begin

ST[k]:=0;

k:=k-1

end else begin

U[ST[k]]:=1;

k:=k+1

end

end

end.

Problema 8

Enunþ. Sã se dispunã pe cele 12 muchii ale unui cub toate numerele de la 1 la 12, astfel încât suma numerelor aflate pe muchiile unei feþe sã fie aceeaºi pentru toate feþele.

Considerãm muchiile numerotate ca în figurã:

feþele. Considerãm muchiile numerotate ca în figurã: Ieºirea va fi într-un fiºier text având numele

Ieºirea va fi într-un fiºier text având numele solcub.txt care va conþine câte o soluþie pe fiecare linie sub forma:

1 m 2 m 3 m 4 m 5 m 6 m 7 m 8 m 9 m 10 m 11 m 12

1 m 2 m 3 m 4 m 5 m 6 m 7 m 8 m 9 m 10 m 11 m 12

unde m i este valoarea din mulþimea {2,

valoarea 1 în toate soluþiile.

,12}

plasatã pe muchia i, i=2

12,

iar muchia 1 conþine

O soluþie corectã este cea de mai jos, ilustratã ºi în figurã:

1 12 9 6 8 5 7 4 11 3 2 10 Rezolvare. Notãm cu

1

12 9

6

8

5 7

4

11 3 2 10

Rezolvare. Notãm cu F 1 ,

,F 6 suma numerelor de pe muchiile feþelor cubului.

Baraj Sibiu, 1996

F 1 = F 2 =

= F 6 =

F

F 1 +F 2 +F 3 +F 4 +F 5 +F 6 = 2(m 1 +m 2 +

Deci, 6*F=2*(m 1 +m 2 +

+m

12 ),

+m

12 ), pentru cã fiecare muchie apare în douã feþe ale cubului.

dar m 1 +m 2 +

+m

12 = 1+2+…+12 =78 => 6*F=2*78=156 => F=26.

Deci suma muchiilor de pe fiecare faþã trebuie sã fie 26.

Observãm cã este suficient sã folosim doar 6 nivele ale stivei, celelalte valori deducându-se din acestea 6. Astfel, m 1 =1 întotdeauna. Dupã ce aºezãm m 2 ºi m 3 , m 8 se deduce ca fiind 26-m 1 -m 2 -m 3 . Analog, dupã ce

aºezãm m 4 ºi m 5 se deduce m 9 =26-m 4 -m 5 . La fel, se deduc m 10 =26-m 2 -m 4 , m 11 =26-m 5 -m 7 -m 3 ºi m 12 =26-

m 9 -m 10 -m 11 .

Deci numai 6 muchii variazã independent una de celelalte: m 2 ,

pentru a ºti ce numere au fost deja aºezate în stivã.

,m

7 . Vectorul auxiliar Pus este folosit

În concurs, la aceastã problemã a fost impus un timp de execuþie de maxim 10 secunde. Programul face

un timp de execuþie de maxim 10 secunde. Programul face aranjamente de 11 luate câte 6,

aranjamente de 11 luate câte 6, timpul de calcul fiind O(

utiliza soluþia banalã, adicã permutãri de 11, timpul de calcul era O(11!)ª 39 milioane ºi timpul de execuþie era de aproximativ 30 de secunde.

)ª 55000, adicã sub o secundã. Dacã se

program cub_magic;

var ST

:array [2

12]

of integer;

Pus

:array [1

26]

of boolean;

k, i

:integer;

function Valid:boolean; begin if Pus[ST[k]] then Valid:=false else case k of

2:begin

Pus[ST[2]]:=true;

Valid:=true end;

3:begin

Pus[ST[3]]:=true;

ST[8]:=26-ST[2]-ST[3]-1;

if not Pus[ST[8]] then begin

Pus[ST[8]]:=true;

Valid:=true end else begin

Pus[ST[3]]:=false;

Valid:=false

end;

end

4:begin

Pus[ST[4]]:=true;

Valid:=true end;

5:begin

Pus[ST[5]]:=true;

ST[9]:=26-ST[5]-ST[4]-1;

if not Pus[ST[9]] then begin

Pus[ST[9]]:=true;

Valid:=true end else begin

Pus[ST[5]]:=false;

Valid:=false

end;

end

6:begin

Pus[ST[6]]:=true;

ST[10]:=26-ST[4]-ST[6]-ST[2];

if (ST[10]>0) and (not Pus[ST[10]]) then begin

Pus[ST[10]]:=true;

Valid:=true end else begin

Pus[ST[6]]:=false;

Valid:=false

end;

end;

7:begin

Pus[ST[7]]:=true;

ST[11]:=26-ST[5]-ST[7]-ST[3];

ST[12]:=26-ST[6]-ST[7]-ST[8];

if (ST[11]>0) and (ST[12]>0) and (ST[11]<>ST[12]) and (not Pus[ST[11]]) and (not Pus[ST[12]]) and (ST[9]+ST[10]+ST[11]+ST[12]=26) then begin

Pus[ST[7]]:=false;

Valid:=true end else begin

Pus[ST[7]]:=false;

Valid:=false

end;

end;

end;

end;

procedure Printsol; begin write('1 '); for i:=2 to 12 do write(ST[i],' '); writeln end;

begin k:=2; ST[k]:=0;

Pus[1]:=true;

for i:=2 to 26 do Pus[i]:=false; while k>1 do begin repeat

ST[k]:=ST[k]+1

until (ST[k]>12) or Valid; if ST[k]<=12 then if k=7 then Printsol else begin

k:=k+1;

ST[k]:=0

end else begin

k:=k-1;

case k of

2:Pus[ST[2]]:=false;

3:begin

Pus[ST[3]]:=false;

Pus[ST[8]]:=false

end;

4:Pus[ST[4]]:=false;

5:begin

Pus[ST[5]]:=false;

Pus[ST[9]]:=false

end;

6:begin

Pus[ST[6]]:=false;

Pus[ST[10]]:=false

end.

end

end

end

end

Problema 9

Enunþ. Fie A m,n o matrice binarã. Se cunosc coordonatele (i,j) ale unui element, i aparþinând 1

j aparþinând 1

elementele cu valoarea 1.

n,

care are valoarea 1. Sã se gãseascã toate ieºirile din matrice mergând numai pe

m,

Rezolvare. Vom proceda ca la problema 4: reþinem un vector cu deplasãrile relative ale miºcãrilor posibile; se subînþelege cã dintr-o cãsuþã se poate merge doar în cele patru vecine cu ea:

(i-1,j) (i+1,j) (i,j+1) (i,j-1)

Condiþia pentru o soluþie o reprezintã verificarea atingerii marginilor matricei; deci avem patru cazuri, pentru fiecare din cele patru margini: i=1 sau i=M sau j=1 sau j=N.

O mutare este consideratã validã dacã:

este în matrice;

poziþia respectivã este marcatã cu 1.

Poziþiile deja parcurse sunt marcate cu 2 pentru a nu fi considerate încã o datã în soluþie.

Program Matrice;

const

Muta

: array[0

4,1

2]

of integer=

( (0,0), (-1,0), (1,0), (0,1), (0,-1) );

var

N,M,k,i,j,oi,oj

:integer;

A

:array[0

100,0

100]

of integer;

ST

:array[0

1000]

of integer;

Procedure Sol; var x,y,f :integer; begin x:=oi; y:=oj; for f:=1 to k do begin write('(',x,',',y,') ');

x:=x+Muta[ST[f],1];

y:=y+Muta[ST[f],2]

end;

writeln;

dec(k)

end;

Function Valid:boolean; var pi,pj :integer; begin

pi:=i+Muta[ST[k],1];

pj:=j+Muta[ST[k],2];

Valid:=(pi>0) and (pj>0) and (pi<=M) and (pj<=N) and (A[pi,pj]=1)

end;

begin write('M,N:'); readln(M,N); for i:=1 to M do

for j:=1 to N do begin write('A[',i,',',j,']='); readln(A[i,j]) end; write('i,j:'); readln(i,j); oj:=j; oi:=i;

A[i,j]:=-1;

fillchar(ST,sizeof(ST),0);

k:=1;

while k>0 do begin if (i=1) or (j=1) or (i=M) or (j=N) then Sol; if A[i,j]=2 then A[i,j]:=1;

i:=i-Muta[ST[k],1];

j:=j-Muta[ST[k],2];

repeat inc(ST[k]) until (ST[k]>4) or valid; if ST[k]=5 then begin

ST[k]:=0;

dec(k) end else begin

A[i,j]:=2;

i:=i+Muta[ST[k],1];

j:=j+Muta[ST[k],2];

inc(k)

end

end

end.

Problema 10

Enunþ. Se dã un numãr natural par N. Sã se afiºeze toate ºirurile de N paranteze care se închid corect.

Rezolvare. Un ºir de N paranteze care se închid corect, este un ºir în care fiecãrei paranteze deschise îi corespunde o parantezã închisã la o poziþie ulterioarã în ºir.

Exemple de ºiruri corecte: (())() ; ((())) ; ()()()

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 N d (k) ºi N i (k) numãrul de paranteze deschise, respectiv

închise, pânã la poziþia k a ºirului, inclusiv. Cele douã condiþii sunt:

1. N d (k) N/2, 1

k

n

2. N d (k) N i (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 N d ºi N i 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ã N d <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ã N d >Ni.

La urcarea ºi coborârea în stivã se modificã N d sau N i în funcþie de tipul parantezei de pe nivelul respectiv.

program Paranteze;

var ST

:array [0

100]

of integer;

k, N, Nd, Ni

: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 {L i ,L i+1 ,L i+2 ,

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

,L

S }. Anume: pentru

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,

rãmase (p+1,p+2, [i]=i.

,p ºi apoi încã oricare k-p din cele

, N). Pentru a forþa alegerea primelor p elemente vom considera Li[i]=Ls

Deci vectorii vor arãta astfel: I 1 2 3 … p p+1 p+2 … N
Deci vectorii vor arãta astfel:
I
1
2
3
p
p+1
p+2
N
Li
1
2
3
p
p+1
p+1
p+1
Ls
1
2
3
p
N
N
N

b) aici considerãm p=0: deci obiectele care nu trebuie selectate vor fi 1,2,3

ºi Ls[i]=N pentru oricare i.

, q. Avem Li[i]=Q+1

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.

d)

Li[1]=1 ºi Ls[1]=p alegem un obiect între 1 ºi p;

Li[i]=p+1 ºi Ls[i]=N, pentru i>1 ºi restul între p+1 ºi N.

e) Li[1]=1 ºi Ls[1]=p un obiect între 1 ºi p;

Li[i]=1 ºi Ls[i]=N, pentru i>1 restul pot lua orice valori.

f) Li[i]=1 ºi Ls[i]=p, pentru 1 i R pe primele r

poziþii alegem obiecte din primele p;

Li[i]=p+q+1 ºi Ls[i]=N, pentru R<i K restul trebuie sã nu fie printre cele p sau q.

Deci vom scrie o singurã rutinã backtracking, care, în funcþie de iniþializãrile vectorilor Li ºi Ls, va furniza rãspunsul la oricare din cele ºase cerinþe. 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. Pentru cã variabila k face parte din datele de intrare, nivelul curent în stivã va fi memorat în variabila i.

program submultimi;

var

ST, Li, Ls, u

:array[0

1000]

of integer;

N, P, Q, R, K, i, f

:integer;

sol

:longint;

ch

:char;

begin

write('N,K,P,Q:');

 

readln(N,K,P,Q);

write('subpunctul (A,B,C,D,E sau F):'); readln(ch); Case UpCase(ch) of 'A':begin

 
 

for i:=

1 to P do

begin Li[i]:=i;

Ls[i]:=i

end;

for i:=P+1 to K do begin Li[i]:=P+1; Ls[i]:=N

end

end;

'B':

for i:=

1 to K do

begin Li[i]:=Q+1; Ls[i]:=N

end;

'C':begin for i:=

2 to K do

 

begin Li[i]:=3; Li[1]:=1; Ls[1]:=1 end;

Ls[i]:=N

end;

'D':begin

for i:=

2 to K do

begin Li[i]:=P+1; Ls[i]:=N

end;

Li[1]:=1; Ls[1]:=p end; 'E':begin

 

for i:=

2 to K do

begin Li[i]:=1;

Ls[i]:=N

end;

Li[1]:=1; Ls[1]:=p end; 'F':begin write('R='); readln(R);

 

for i:=

1 to R do

begin Li[i]:=i;

Ls[i]:=P

end;

for i:=R+1 to K do begin Li[i]:=P+Q+1;Ls[i]:=N

end

end;

i:=1;

end

fillchar(ST,sizeof(ST),0);

fillchar(u,sizeof(u),0);

ST[1]:=Li[1]-1;

sol:=0;

while i>0 do if i=k+1 then begin

sol:=sol+1;

for f:=1 to K do write(ST[f],' '); writeln;

i:=i-1

end else begin

U[ST[i]]:=0;

repeat

ST[i]:=ST[i]+1

until (ST[i]>LS[i]) or (U[ST[i]]=0); if ST[i]>LS[i] then begin

ST[i]:=0;

i:=i-1

end else begin

U[ST[i]]:=1;

i:=i+1;

if ST[i-1]<Li[i] then ST[i]:=Li[i]-1 else ST[i]:=ST[i-1]-1

end

end;

writeln('Numar solutii= ',sol) end.

Problema 12

Enunþ. Sã se genereze toate permutãrile de N cu proprietatea cã oricare ar fi

2 i N, existã 1 j i astfel încât |V(i)-V(j)|=1.

Exemplu: pentru N=4, permutãrile cu proprietatea de mai sus sunt:

2134, 2314, 3214, 3241, 3421, 4321, 1234

Rezolvare. Pentru generarea permutãrilor folosim o stivã cu N nivele ºi un vector auxiliar:

Pus[i]=TRUE, dacã numãrul i a fost aºezat deja în permutare;

FALSE, altfel.

Condiþiile care trebuie îndeplinite pentru aºezarea unui numãr c pe nivelul k al stivei sunt:

Pus[c]=FALSE, adicã sã nu fi aºezat deja numãrul c în permutare;

cel puþin unul din numerele aºezate pe poziþiile 1 diferenþa absolutã faþã de c egalã cu 1.

program Permutari;

k-1

,respectiv st[1]

st[k-1],

sã aibã

var ST

:array [1

20]

of integer;

k, i, N

:integer;

Pus

:array [1

20]

of boolean;

function Valid:boolean; var tmp:boolean; begin if k=1 then Valid:=true else begin tmp:=false; if not Pus[ST[k]] then for i:=1 to k-1 do if abs(ST[k]-ST[i])=1 then tmp:=true;

Valid:=tmp

end

end;

procedure Printsol; var i:integer; begin for i:=1 to N do write(st[i],' '); writeln end;

begin write('N= '); readln(N); for i:=1 to N do Pus[i]:=false;

k:=1;

ST[k]:=0;

while k>0 do begin repeat

ST[k]:=ST[k]+1

until (ST[k]>N) or Valid; if ST[k]<=N then if k=N then Printsol else begin Pus[ST[k]]:=true;

k:=k+1;

ST[k]:=0

end else begin

k:=k-1;

Pus[ST[k]]:=false

end.

end

end

Problema 13

Enunþ. Se dau N puncte albe ºi N puncte negre în plan, de coordonate întregi. Fiecare punct alb se uneºte cu câte un punct negru, astfel încât din fiecare punct, fie el alb sau negru, pleacã exact un segment. Sã se determine o astfel de configuraþie de segmente încât oricare douã segmente sã nu se intersecteze. Se citesc 2N perechi de coordonate corespunzând punctelor.

Rezolvare. Vom reþine în vectorii X ºi Y coordonatele punctelor, aºezând pe primele N poziþii punctele

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

:integer;

X,Y

:array[1

1000]

of real;

ST,u

: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

end.

k:=k-1;

U[ST[k]]:=0

end

end

Problema 14

Enunþ: Se considerã n puncte în plan, de coordonate reale, (X 1 ,Y 1 ), (X 2 ,Y 2 ), (X 3 ,Y 3 ),

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.

, (X n ,Y n ).

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:

pe prima linie n, numãrul de puncte

pe urmãtoarele n linii coordonatele punctelor.

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 (d 12 ,d 23 ,d 13 ). 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

10 -5 . Acest test evitã erorile de aproximaþie (în programul nostru acestea pot apãrea, de exemplu, la

operaþia

). Din acest motiv, am folosit funcþia booleanã Egal care face testul de egalitate prezentat

). Din acest motiv, am folosit funcþia booleanã Egal care face testul de egalitate prezentat

mai sus.

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

distanþele de la P[2] la aceste douã puncte.

Funcþia Dist(p1,p2) întoarce distanþa între Pct[p1] ºi Pct[p2].

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, altfel. Pentru a fi inclus în pãtrat, punctul Pct[i] trebuie sã se afle între dreptele suport ale segmentelor P[1]P[2] ºi P[4]P[3], ºi de asemenea între dreptele suport ale lui P[2]P[3] ºi P[1]P [4]. Pentru a se afla între douã drepte paralele, de acelaºi sens, un punct trebuie sã se afle în semiplane de semne opuse faþã de acele drepte.

program puncte;

type punct=record x,y:real end;

var n, k, colt, max, i

:integer;

Pct

:array [1

20]

of punct;

ST

:array [1

4]

of integer;

P,maxp

:array [1

4]

of punct;

procedure Citire;

var i

:integer;

f

:text;

begin assign(f,'input.txt'); reset(f); readln(f, n); for i:=1 to n do readln(f, Pct[i].x, Pct[i].y); close(f) end;

function Egal(val1,val2:real):boolean; begin

Egal:=(abs(val1-val2)<=1e-5)

end;

function Dist(p1,p2:integer):real; begin Dist:=sqrt(sqr(pct[p1].x-pct[p2].x)+sqr(pct[p1].y pct[p2].y)) end;

function Valid:boolean; var d12,d23,d13,d1,d2,d3:real; begin if k<=2 then valid:=true

else if k=3 then begin

d12:=Dist(ST[1],ST[2]);

d23:=Dist(ST[2],ST[3]);

d13:=Dist(ST[1],ST[3]);

if Egal(d12,d23) then if Egal(d13,d12*sqrt(2)) then begin

colt:=2;

P[1]:=Pct[ST[1]];P[2]:=Pct[ST[2]];

P[3]:=Pct[ST[3]];

Valid:=true end else Valid:=false else if Egal(d12,d13) then if Egal(d23,d12*sqrt(2)) then begin

colt:=1;

P[1]:=Pct[ST[2]]; P[2]:=Pct[ST[1]];

P[3]:=Pct[ST[3]];

Valid:=true end else Valid:=false else if Egal(d13,d23) and Egal(d12,d23*sqrt(2)) then begin

colt:=3;

P[1]:=Pct[ST[1]]; P[2]:=Pct[ST[3]];

P[3]:=Pct[ST[2]];

Valid:=true end else Valid:=false end else begin case colt of

1:begin

d1:=Dist(ST[2],ST[4]);

d2:=Dist(ST[3],ST[4]);

d3:=Dist(ST[1],ST[2])

end;

2:begin

d1:=Dist(ST[1],ST[4]);

d2:=Dist(ST[3],ST[4]);

d3:=Dist(ST[1],ST[2])

end;

3:begin

d1:=Dist(ST[2],ST[4]);

d2:=Dist(ST[1],ST[4]);

d3:=Dist(ST[3],ST[2])

end;

end; if Egal(d1,d3) and Egal(d2,d3) then begin Valid:=true;

P[4]:=Pct[ST[4]]

end else Valid:=false end end;

function Inclus(i:integer):boolean; var tmp:boolean; begin tmp:=false; if ((Pct[i].x-P[3].x)*(P[2].y-P[3].y)-(Pct[i].y- P[3].y)* (P[2].x-P[3].x))*

((Pct[i].x-P[4].x)*(P[1].y-P[4].y)-(Pct[i].y-P[4].y)*(P[1].x-P[4].x))<=0

then if ((Pct[i].x-P[3].x)*(P[4].y-P[3].y)-(Pct[i].y-P[3].y)*(P[4].x-P[3].x))*

((Pct[i].x-P[2].x)*(P[1].y-P[2].y)-(Pct[i].y-P[2].y)*(P[1].x-P[2].x))<=0

then tmp:=true inclus:=tmp end;

procedure Numara; var c,i:integer; begin

c:=0;

for i:=1 to n do if Inclus(i) then c:=c+1; if c>Max then begin Max:=c; for i:=1 to 4 do Maxp[i]:=P[i] end

end;

begin Citire;

k:=1;

ST[k]:=0;

Max:=0;

while k>0 do begin repeat

ST[k]:=ST[k]+1

until (ST[k]>n) or Valid; if ST[k]<=n then if k=4 then Numara

else begin

k:=k+1;

ST[k]:=ST[k-1]

end else k:=k-1 end; if Max>0 then for i:=1 to 4 do writeln(Maxp[i].x:0:3,' ',Maxp[i].y:0:3); writeln(Max) end.

Problema 15

Enunþ. Fiind dat un numãr natural N ºi un vector V cu N componente întregi, se cer urmãtoarele:

sã se determine toate subºirurile crescãtoare de lungime [N/5];

sã se calculeze p(1)+p(2)+ lungime k.

+p(k),

unde p(k) reprezintã numãrul subºirurilor crescãtoare de

Vom scrie o singurã rutinã de backtracking, care va genera subºirurile crescãtoare de lungime L. Pentru a afiºa soluþiile doar pentru L=[N/5] folosim variabila booleanã scrie.

Punctul doi se realizeazã prin însumarea numãrului de soluþii generate de aceeaºi procedurã, cu L luând valori între 1 ºi k.

În stivã vom reþine indicele din vectorul iniþial al elementului aºezat pe poziþia i în subºir. În procedura de backtracking, 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]].

S-a considerat "mai mare sau egal" pentru cã în cerinþã se cer subºirurile crescãtoare ºi nu strict- crescãtoare.

program Subsiruri;

var ST,V

:array[0

1000]

of integer;

N,k,i

:integer;

sol

:longint;

scrie

:boolean;

procedure Printsol(l:integer);

var i:integer; begin if scrie then begin for i:=1 to l do write(V[ST[i]],' '); writeln end;

sol:=sol+1

end;

procedure Bkt(l:integer); var f,i:integer; begin

i:=1;

ST[1]:=0;

while i>0 do begin repeat

ST[i]:=ST[i]+1

until ((ST[i]>ST[i-1]) and (V[ST[i]]>=V[ST[i-1]])) or (ST[i]>N); if ST[i]<=N then if i=l then Printsol(l) else begin

i:=i+1;

ST[i]:=0

end else i:=i-1 end

end;

begin write('N,k:'); readln(N,k); for i:=1 to N do begin write('V[',i,']='); readln(V[i]) end;

scrie:=true; writeln('Subsirurile crescatoare de lungime ',N div 5,' sunt: '); Bkt(N div 5);

sol:=0;

scrie:=false; for i:=1 to k do Bkt(i); writeln('Numarul subsirurilor crescatore de lungime <= ',K,' este: ',sol) end.

[ Cuprins ]

[ Capitolul

2 ]

Capitolul 2

Recursivitate

Problema 1

Enunþ. Calculaþi recursiv suma a n numere naturale citite.

Rezolvare. Funcþia recursivã Suma calculeazã suma celor n numere. Funcþia primeºte ca parametru k, numãrul de numere care au mai rãmas de citit. Când k=0 se iese din funcþie. În caz contrar, se citeºte de la tastaturã A, un numãr din ºir ºi se adunã la sumã.

program sum;

var n :integer;

function Suma(k:integer):integer; var A:integer; begin if k>0 then begin write('A[',n-k+1,']= '); readln(A);

Suma:=A+Suma(k-1)

end else Suma:=0; end;

begin write('n= '); readln(n); writeln(Suma(n)); end.

Problema 2

Enunþ. Fiind datã o mulþime cu n elemente, 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,k).

Acestea se definesc recursiv astfel:

S(n,1)=

=S(n,n)=1

S(n+1,k)=S(n,k-1)+kS(n,k)

Sã se calculeze numãrul partiþiilor unei mulþimi cu n elemente.

Rezolvare. Trebuie precizat cã numãrul partiþiilor unei mulþimi este egal cu suma numãrului de partiþii în k clase, dând lui k valori între 1 ºi numãrul de elemente din mulþime.

Funcþia recursivã este întocmai transcrierea formulei recurente.

program Partitii;

var

N,k :integer;

:longint;

R

function Part(n,k:integer):longint; begin if (k=1) or (k=n) then Part:=1 else Part:=Part(n-1,k-1) + k*Part(n-1,k)

end;

begin write('N='); readln(N);

R:=0;

for k:=1 to N do R:=R+Part(N,k); writeln(R) end.

Problema 3

Enunþ. Un numãr natural n, se poate descompune ca sumã unicã de numere naturale. De exemplu, pentru numãrul 4 se scrie descompunerea 2+1+1 (secvenþã descrescãtoare), nu ºi 1+2+1. Prin P(n, m) notãm numãrul de împãrþiri ale lui n ca sumã (unicã) de m numere.

Exemplu:

P(4,2)=2 (4=3+1, 4=2+2).

Numerele P(n,m) verificã relaþia de recurenþã:

P(n,1)+P(n,2)+…+P(n,k)=P(n+k,k);

P(n,1)=P(n,n)=1

Sã se calculeze numãrul total de descompuneri ale numãrului natural n.

Rezolvare.

program Suma;

var N,k

:integer;

R

:longint;

function Min(m1,m2:integer):integer; begin if m1<m2 then Min:=m1 else Min:=m2

end;

function P(n,k:integer):longint; var i:integer; r:longint; begin

r:=0;

if (k=n) or (k=1) then R:=1 else for i:=1 to Min(k,N-k) do R:=R+P(n-k,i); P:=R end;

begin

write('N=');

readln(N);

R:=0;

for k:=1 to N do R:=R+P(N,k); writeln(R) end.

Problema 4

Enunþ. Se citesc n ºi k (numere naturale n>k). Calculaþi recursiv

n ºi k (numere naturale n>k ). Calculaþi recursiv . Este eficient? utilizând formula de recurenþã:

. Este eficient?

naturale n>k ). Calculaþi recursiv . Este eficient? utilizând formula de recurenþã: Rezolvare. Dupã cum se

utilizând formula de recurenþã:

Rezolvare. Dupã cum se vede, programul reprezintã întocmai implementarea formulei de recurenþã la

care s-au adãugat douã ramuri:

formulei de recurenþã la care s-au adãugat douã ramuri: = n ºi = 0 , dacã

=n ºi

de recurenþã la care s-au adãugat douã ramuri: = n ºi = 0 , dacã k>n

=0, dacã k>n.

Pentru a funcþiona corect, orice program (recursiv) bazat pe o relaþie recurentã trebuie sã porneascã de la câteva rezultate cunoscute pentru valori mici ale datelor de intrare. Apoi prin aplicarea succesivã a formulei de recurenþã, orice instanþã a intrãrii trebuie redusã la unul din rezultatele cunoscute. Astfel, orice pereche (n,k) trece în (n-1,k) ºi (n-1,k-1); este evident cã în final perechea (n,k) se va încadra într-una din cele douã ramuri de iniþializare ale formulei de recurenþã (fie n<k, fie k=1).

Acum sã rãspundem la întrebarea dacã programul este eficient. Succesiunii apelurilor recursive i se poate asocia un arbore binar în care fiecare nod este etichetat cu perechea (n,k) corespunzãtoare. Este necesar sã construim doar douã nivele ale arborelui pentru a observa cã etichetele nodurilor nu

sunt distincte (perechea (n-2,k-1) apare de douã ori). Deci pentru a calcula

( n-2 , k-1 ) apare de douã ori). Deci pentru a calcula programul va calcula

programul va calcula

de douã ori

Deci pentru a calcula programul va calcula de douã ori . Dacã mai construim un nivel

. Dacã mai construim un nivel din arbore vom observa cã

. Dacã mai construim un nivel din arbore vom observa cã este calculat de ºase ori.

este calculat de

ºase ori. Acest coeficient va creºte exponenþial cu numãrul de nivele. Pe cazul general,

calculat de

cu numãrul de nivele. Pe cazul general, calculat de este Deci programul nostru în nici un
cu numãrul de nivele. Pe cazul general, calculat de este Deci programul nostru în nici un

este

Deci programul nostru în nici un caz nu este eficient: pentru o pereche (n,k) de pe parcursul calculului, el va efectua de un numãr de ori exponenþial aceeaºi secvenþã de operaþii.

program Combinari_recursiv; var N,k :integer; function Comb(n,k:integer):longint; begin if k=1 then Comb:=n else if

program Combinari_recursiv;

var

N,k

:integer;

function Comb(n,k:integer):longint; begin if k=1 then Comb:=n else if k>n then Comb:=0 else Comb:=Comb(n-1,k)+Comb(n-1,k-1)

end;

begin

write('N,K=');

readln(N,K);

writeln(Comb(N,K))

end.

Problema 5

Enunþ. Scrieþi un program iterativ care rezolvã problema anterioarã utilizând aceeaºi formulã.

Rezolvare. Programul de mai jos rezolvã neajunsurile celui dinaintea sa, ºi anume evitã calcularea de mai multe ori a aceloraºi rezultate. Aceasta se face folosind matricea Comb[n,k] în care se va reþine

valoarea

.
.

Modificarea esenþialã faþã de programul precedent constã în faptul cã apelurile recursive sunt înlocuite cu citiri în matrice. 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).

Ca ºi la varianta recursivã, aici combinãrile se calculeazã pe baza aceluiaºi arbore, care acum este parcurs de "jos în sus". Anume, se pleacã de la valorile cunoscute Comb[i,1]; apoi se aplicã relaþia de recurenþã ºi se obþin valorile pentru Comb[i,2]; cunoscând valorile pentru un anumit N, cele pentru N+1 se obþin prin simpla aplicare a formulei de recurenþã.

program Combinari_iterativ;

var N,K,i,j

:integer;

Comb

:array[0

50,0

50]

of longint;

begin write('N,K='); readln(N,K); for i:=0 to N do Comb[i,0]:=1; for i:=1 to N do for j:=1 to k do

Comb[i,j]:=Comb[i-1,j-1]+Comb[i-1,j];

writeln(Comb[N,k])

end.

Problema 6

Enunþ. Gãsiþi o formulã de recurenþã care sã rezolve eficient problema 4 (recursiv).

Rezolvare.

Program Combinari_recursiv2;

var

N,k

:integer;

Function Comb(n,k:integer):longint; begin if k=1 then Comb:=N else Comb:=(n-k+1)*Comb(n,k-1) div k

end;

begin

write('N,k=');

readln(N,k);

writeln(Comb(n,k))

end.

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, notat cmmdc(u,v), este cel mai mare întreg care îl divide atât pe u, cât ºi pe v.

Algoritmul se bazeazã pe urmãtoarea formulã recursivã pentru cmmdc:

bazeazã pe urmãtoarea formulã recursivã pentru cmmdc : Sã demonstrãm acum corectitudinea ecuaþiei

Sã demonstrãm acum corectitudinea ecuaþiei cmmdc(u,v)=cmmdc(v,

u mod v). Considerãm cã u=qv+r (unde q este întreg). Atunci, r=u-qv ºi cum

r=u mod v => u mod v=u-qv. Ecuaþia devine: cmmdc(u,v)=cmmdc(v,

u-q*v).

Dacã x este un divizor comun al lui u ºi v, atunci x/(u-qv). Deci orice divizor comun al lui u ºi v este ºi divizor al lui u-qv. Analog, orice divizor comun al lui v ºi u-q*v, este divizor al lui u. Rezultã cã cele douã perechi de numere (u,v) ºi (v,u-q*v) au aceiaºi divizori comuni, deci implicit ºi acelaºi cmmdc.

Varianta iterativã a algoritmului se scrie astfel:

while v<>0 do

r:=u mod v (u mod v = restul impartirii lui u la v)

u:=v

v:=r

(la sfârºitul execuþiei programului, valoarea cmmdc se va afla în u)

Vom prezenta acum implementãrile celor douã variante ale algoritmului.

program euclid_iterativ;

var u,v,r:integer;

begin write('u, v: '); readln(u,v); while v<>0 do begin r:=u mod v; u:=v; v:=r end; writeln(u) end.

program euclid_recursiv;

var u,v:integer;

function cmmdc(u,v:integer):integer; begin if v=0 then cmmdc:=u else cmmdc:=cmmdc(v,u mod v);

end;

begin write('u, v: '); readln(u,v);

writeln(cmmdc(u,v));

end.

Algoritmul binar

Vom prezenta acum un alt algoritm pentru aflarea cmmdc descoperit de Josef Stein în 1961.

Algoritmul binar se bazeazã pe urmãtoarele patru afirmaþii despre douã întregi pozitive u ºi v:

a. Dacã u ºi v sunt pare, atunci cmmdc(u,v)=2cmmdc(u/2,v/2).

b. Dacã u este par ºi v este impar, atunci cmmdc(u,v)=cmmdc(u/2,v).

c. cmmdc(u,v)=cmmdc(u-v,v).

d. Dacã u ºi v sunt impare, atunci u-v este par, ºi |u-v|<max(u,v).

Lãsãm demonstraþia acestor propoziþii în seama cititorului.

Vom schiþa algoritmul iterativ în pseudocod:

k:=0;

while u-par ºi v-par do

u:=u/2; v:=v/2;

k:=k+1

dupã acest prim pas al algoritmului, cmmdc=2 unul dintre u ºi v este impar.

if v-par then interschimba(u,v)

k *cmmdc(u,v), conform afirmaþiei a) ºi cel puþin

dacã unul dintre numere este par, atunci acesta trebuie sã fie u.

repeat

while u-par do

u=u/2

în urma acestui pas valoarea cmmdc se pãstreazã pentru cã, conform afirmaþiei b), dacã u-par ºi v-impar, cmmdc(u,v)=cmmdc(u/2,v).

if v>u then interschimba(u,v);

u trebuie sã fie întotdeauna cel mai mare dintre cele douã numere.

u:=u-v

conform afirmaþiei c), cmmdc(u,v)=cmmdc(u-v,v).

until u=0;

writeln(2 k *v)

Vom prezenta acum programele care implementeazã algoritmul binar, în cele douã variante, iterativã ºi recursivã.

Notã: O implementare mai eficientã, ar fi folosit în locul operaþiei x mod 2, operaþia x and 1 ºi în locul operaþiei x div 2, x shr 1. Aceste perechi de operaþii sunt echivalente, dar operaþiile binare "and" ºi "shr" sunt mult mai rapide.

program binar_iterativ;

var u,v,k,r,i:integer;

procedure intersch(var u,v:integer); var aux:integer; begin aux:=u; u:=v; v:=aux end;

begin write('u, v: ');

readln(u,v);

k:=0;

while (u mod 2=0) and (v mod 2=0) do begin v:=v div 2; u:=u div 2;

k:=k+1;

end; if v mod 2=0 then intersch(u,v); repeat while u mod 2=0 do u:=u div 2; if v>u then intersch(u,v); u:=u-v until u=0; for i:=1 to k do v:=v*2; writeln(v) end.

program binar_recursiv;

var u,v,k,r,i:integer;

procedure intersch(var u,v:integer); var aux:integer; begin aux:=u; u:=v; v:=aux end;

function cmmdc(u,v:integer):integer; begin if u=0 then cmmdc:=v else if u mod 2=0 then cmmdc:=cmmdc(u div 2,v) else if v>u then cmmdc:=cmmdc(v-u,u) else cmmdc:=cmmdc(u-v,v)

end;

begin write('u, v: '); readln(u,v);

k:=0;

while (u mod 2=0) and (v mod 2=0) do begin v:=v div 2;

u:=u div 2;

k:=k+1;

end; if v mod 2=0 then intersch(u,v);

r:=1;

for i:=1 to k do r:=r*2; writeln(cmmdc(u,v)*r) end.

[ Capitolul

1 ]

[ Cuprins ]

[ Capitolul

3 ]

Capitolul 3

Backtracking recursiv

Problema 1

Enunþ. Avem la dispoziþie 6 culori: alb, galben, roºu, verde, albastru ºi negru. Sã se precizeze toate drapelele tricolore care se pot proiecta, ºtiind cã trebuie respectate urmãtoarele reguli:

orice drapel are culoarea din mijloc galben sau verde;

cele trei culori de pe drapel sunt distincte.

Exemple: "alb galben roºu", "roºu verde galben"

Rezolvare. Procedura Back primeºte ca parametru k, nivelul curent al stivei (poziþia în drapel). Dacã se depãºeºte nivelul 3, avem o soluþie ºi aceasta este tipãritã.

Se încearcã aºezarea unei culori pe nivelul curent, dacã este validã. 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, i. În cazul în care culoarea este "validã", se apeleazã recursiv procedura Back pentru nivelul urmãtor. Înainte ºi dupã apelare se seteazã corespunzãtor Fol [i].

program drapel;

const Culoare:array [1

6]

of string[10]=('alb','galben',

'rosu', 'verde','albastru','negru');

var ST:array [1 Fol:array [1 k, i:integer;

3]

6]

of integer; of boolean;

procedure Printsol; var i:integer; begin for i:=1 to 3 do write(Culoare[ST[i]],' ');

writeln;

end;

function Valid(niv,val:integer):boolean; begin if Fol[val] then Valid:=false else if niv<>2 then Valid:=true else if (val=2) or (val=4) then Valid:=true else Valid:=false;

end;

procedure Back(k:integer); var i:integer; begin if k>3 then Printsol else for i:=1 to 6 do if Valid(k,i) then begin ST[k]:=i; Fol[i]:=true;

Back(k+1);

Fol[i]:=false;

end;

end;

begin for i:=1 to 6 do Fol[i]:=false;

Back(1);

end.

Problema 2

Enunþ. Sã se descompunã un numãr natural N, în toate modurile posibile, ca sumã de P numere naturale (P N).

Rezolvare. Procedura recursivã rezolvã problema pentru o sumã s, reþinând termenii în vectorul solutie începând de pe pozitia k.

Se considerã pe rând toþi termenii între 1 ºi S, care se scad din suma iniþialã, ºi apoi se apeleazã recursiv pentru a rezolva aceeaºi problemã, dar de dimensiuni mai mici.

program Descompuneri;

var N, P, K, S, i

:integer;

ST

:array[1

1000]

of integer;

Procedure Back(k,s:integer); var i:integer; begin if (S=0) and (k=p+1) then begin for i:=1 to k-1 do write(ST[i],' '); writeln end else for i:=1 to S do begin ST[k]:=i;

Back(k+1,s-i)

end

end;

begin write('N='); readln(N); write('P='); readln(P);

Back(1,N)

end.

Problema 3

Enunþ. Dintr-un grup de N persoane, dintre care P femei, trebuie formatã o delegaþie de C persoane, dintre care L femei. Sã se precizeze toate delegaþiile care se pot forma.

Rezolvare. Variabilele Min ºi Max, sunt limitele între care poate lua valori nivelul curent al stivei (explicaþia se gãseºte în Cap. 1, Pr. 3). La trecerea pe nivelul c+1, am ajuns la o soluþie, care se tipãreºte.

program Delegatie;

var ST:array [0

100]

of integer;

k, c, n, p, l:integer;

procedure Printsol; var i:integer; begin for i:=1 to c do write(ST[i],' '); writeln; end;

procedure Back(k:integer); var i,Min,Max:integer; begin if k>c then Printsol else begin if k<>l+1 then Min:=ST[k-1]+1 else Min:=p+1; if k<=l then Max:=p else Max:=n; for i:=Min to Max do begin ST[k]:=i;

end;

end;

Back(k+1);

end;

begin write('n, p, c, l: '); readln(n,p,c,l);

ST[0]:=1;

Back(1);

end.

Problema 4

Enunþ. Se dã un numãr natural par N. Sã se afiºeze toate ºirurile de N paranteze care se închid corect.

Rezolvare. Procedura recursivã Back are trei parametri:

k, nivelul curent al stivei.

Nd ºi Ni, numãrul de paranteze deschise, respectiv închise, pânã la poziþia curentã.

program Paranteze;

var ST:array [1 N:integer;

20]

of integer;

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

writeln;

end;

procedure Back(k,Nd,Ni:integer); begin if k>N then Printsol else begin if Nd<N div 2 then begin

ST[k]:=1;

Back(k+1,Nd+1,Ni);

end; if Ni<Nd then begin

ST[k]:=2;

Back(k+1,Nd,Ni+1);

end;

end

end

begin write('N= '); readln(N);

Back(1,0,0);

end.

Enunþ. Fiind dat un numãr natural N, se cere sã se afiºeze toate descompunerile sale ca sumã de numere prime.

Rezolvare.

program Prime;

var ST, Numar

:array[0

1000]

of integer;

N, P, i

:integer;

function Prim( X : integer):boolean; var f:integer; begin Prim:=true;

f:=2;

while f*f<=X do begin if X mod f=0 then begin Prim:=false; exit end;

f:=f+1;

end;

end;

procedure Back(k,s:integer); var i:integer; begin if S=0 then begin for i:=1 to k-1 do write(ST[i],' '); writeln end else begin

i:=1;

while (Numar[i]<=s) and (i<=p) do begin ST[k]:=Numar[i];

Back(k+1,s-Numar[i]);

i:=i+1;

end

end

end;

begin

write('N=');

readln(N);

P:=0;

for i:=2 to N do if Prim(i) then begin P:=P+1; Numar[P]:=i end;

Back(1,N)

end.

Problema 6

Enunþ. Sã se genereze toate permutãrile de N cu proprietatea cã oricare ar fi

2 i N, existã 1 j i astfel încât |V(i)-V(j)|=1.

Rezolvare.

program Permutari;

var ST

:array [1

20]

of integer;

n, i

:integer;

Pus

:array [1

20]

of boolean;

function Valid(k,v:integer):boolean; var tmp:boolean; i:integer; begin if k=1 then Valid:=true else begin tmp:=false; for i:=1 to k-1 do if abs(V-ST[i])=1 then tmp:=true; Valid:=tmp end

end;

procedure Printsol; var i:integer; begin for i:=1 to n do write(ST[i],' '); writeln end;

procedure Back(k:integer); var i:integer; begin if k>n then Printsol else for i:=1 to n do if not Pus[i] and Valid(k,i) then begin ST[k]:=i; Pus[i]:=true;

Back(k+1);

Pus[i]:=false;

end;

end;

begin write('N= '); readln(n); for i:=1 to n do Pus[i]:=false;

Back(1);

end.

Problema 7

Enunþ. Fie A m,n o matrice binarã. Se cunosc coordonatele (i,j) ale unui element, i aparþinând

1

numai pe elementele cu valoarea 1.

m,

j aparþinând 1

n,

care are valoarea 1. Sã se gãseascã toate ieºirile din matrice mergând

Rezolvare.

program Matrice;

const Muta

: array[0

4,1

2]

of integer=

( (0,0), (-1,0), (1,0), (0,1), (0,-1) );

var N,M,i,j,oi,oj

:integer;

A

:array[0

100,0

100]

of integer;

ST

:array[0

1000]

of integer;

procedure Sol(k:integer); var x,y,f :integer;

begin x:=oi; y:=oj; for f:=1 to k do begin write('(',x,',',y,') ');

x:=x+Muta[ST[f],1];

y:=y+Muta[ST[f],2]

end;

writeln

end;

function Valid(k,pi,pj:integer):boolean; var i,j :integer; begin

i:=pi+Muta[ST[k],1];

j:=pj+Muta[ST[k],2];

Valid:=(i>0) and (j>0) and (i<=M) and (j<=N) and (A[i,j]=1) end;

procedure Back(k,pi,pj:integer); var i:integer; begin

A[pi,pj]:=2;

if (pi=1) or (pj=1) or (pi=M) or (pj=N) then Sol(k); for i:=1 to 4 do begin ST[k]:=i; if Valid(k,pi,pj) then

Back(k+1,pi+Muta[i,1],pj+Muta[i,2])

end;

A[pi,pj]:=1

end;

begin write('M,N:'); readln(M,N); for i:=1 to M do for j:=1 to N do begin write('A[',i,',',j,']='); readln(A[i,j]) end; write('i,j:'); readln(i,j); oj:=j; oi:=i;

A[i,j]:=1;

Back(1,i,j)

end.

Problema 8

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.

program Submultimi;

var ST, Li, Ls, u

:array[0

1000]

of integer;

N, P, Q, R, K, i

:integer;

sol

:longint;

ch

:char;

procedure Back(l:integer); var i,b:integer; begin if st[l-1]<Li[l] then b:=Li[l] else b:=st[l-1]; if l=k+1 then begin

sol:=sol+1;

for i:=1 to K do write(ST[i],' ');

writeln end else for i:=b to Ls[l] do if U[i]=0 then begin ST[l]:=i;

U[i]:=1;

Back(l+1);

U[i]:=0

end

end;

begin write('N,K,P,Q:'); readln(N,K,P,Q); write('subpunctul (A,B,C,D,E sau F):'); readln(ch); Case UpCase(ch) of 'A':begin for i:=1 to P do begin Li[i]:=i; Ls[i]:=i end; for i:=P+1 to K do begin Li[i]:=P+1; Ls[i]:=N end

end; for i:=1 to K do

'B':

begin Li[i]:=Q+1; Ls[i]:=N end; 'C':begin for i:=2 to K do begin

Ls[i]:=N

end; Li[1]:=1; Ls[1]:=1 end; 'D':begin for i:=2 to K do begin Li[i]:=P+1; Ls[i]:=N end; Li[1]:=1; Ls[1]:=p end; 'E':begin

Li[i]:=3;

for i:=2 to K do

begin

Li[i]:=1;

Ls[i]:=N

end; Li[1]:=1; Ls[1]:=p end; 'F':begin for i:=1 to R do

begin

Li[i]:=i;

Ls[i]:=P

end; for i:=R+1 to K do begin

Li[i]:=P+Q+1;Ls[i]:=N

end

end

end;

Back(1);

writeln('Numar solutii= ',sol) end.

[ Capitolul

2 ]

[ Cuprins ]

[ Capitolul

4 ]

Capitolul 4

Analiza timpului de calcul necesar

algoritmilor

Cãutare binarã

Definim problema cãutãrii:

Se dã un vector A[1

n]

ºi o valoare v de acelaºi tip cu elementele din A.

Sã se afiºeze p astfel încât v=A[p] sau 0 dacã nu existã un element cu valoarea v în A.

Cel mai simplu algoritm care rezolvã aceastã problemã este cãutarea liniarã, care testeazã elementele vectorului unul dupã altul, începând cu primul, în cãutarea lui v. Iatã implementarea acestui algoritm:

Gasit_v:=False;

p:=0;

while not Gasit_v do begin

p:=p+1;

if v=A[p] then Gasit_v:=True; end; if Gasit_v then writeln(p) else writeln('0');

Sã analizãm complexitatea acestui algoritm pe cazul cel mai defavorabil, acela în care v nu se gãseºte în A. În acest caz se va face o parcurgere completã a lui A. Considerând ca operaþie de bazã testul v=A[p], numãrul de astfel de operaþii de bazã efectuate va fi egal cu numãrul de elemente al lui A, adicã n. Deci complexitatea algoritmului pe cazul cel mai defavorabil este O(n).

Un alt algoritm, mai eficient, este cãutarea binarã. Acest algoritm are însã neajunsul cã necesitã ca vectorul A sã fie sortat crescãtor. Iatã procedura recursivã care implementeazã algoritmul:

procedure caut(i,j:integer); begin m=(i+j) div 2; if v=A[m] then writeln('Valoarea gasita la pozitia: ',m) else if i<j then if v<A[m] then caut(i,m-1) else caut(m+1,j)

end;

Procedura primeºte ca parametri i ºi j, limitele porþiunii din vector în care cãutãm. Iniþial procedura este apelatã cu i=1 ºi j=n. m primeºte valoarea (i+j) div 2, adicã indicele elementului din mijlocul

Dacã elementul din mijlocul acestei porþiuni, A[m], este chiar valoarea cãutatã, atunci

programul se terminã cu afiºarea indicelui acestui element. Dacã v<A[m] înseamnã cã poziþia lui v, dacã se gãseºte în vector, este în stânga lui m, pentru cã vectorul este sortat crescãtor. În acest caz

procedura se apeleazã recursiv cu noua porþiune de vector (i

v>A[m], procedura se apeleazã recursiv pentru (m+1

de vector de un element, ºi acest element este diferit de v, înseamnã cã v nu se gãseºte în A ºi apelarea recursivã înceteazã datoritã neîndeplinirii condiþiei i<j.

lui A[i

j].

m-1)

ca parametru. Analog, dacã

j).

Dacã se ajunge la i=j, adicã o porþiune

Exemplu:

i<j . lui A[i j] . m-1) ca parametru. Analog, dacã j) . Dacã se ajunge

Sã analizãm complexitatea acestui algoritm pe cel mai defavorabil caz, care este ca ºi la cãutarea liniarã acela în care valoarea cãutatã nu se gãseºte în vector. Considerãm ca operaþie elementarã , o execuþie a procedurii caut. Pentru a determina complexitatea algoritmului, trebuie sã vedem de câte ori se executã procedura caut. Procedura se va apela recursiv pânã când se va ajunge la o porþiune de vector de un element, adicã pânã când i=j. Iniþial procedura este apelatã cu i=1 ºi j=n, mãrimea porþiunii de vector fiind , j-i+1=n. La fiecare apel recursiv, porþiunea de vector se înjumãtãþeºte. La ultimul apel, i=j, deci mãrimea porþiunii va fi i-j+1=1.

Problema este deci, de câte ori se poate împãrþi un numãr natural n la 2, pânã ce câtul împãrþirii va fi 1. Presupunem cã n=2 k . La fiecare împãrþire a lui n la 2 exponentul acestuia scade cu o unitate, pânã când câtul împãrþirii va fi 1, adicã n=2 1 . Deci n se poate împãrþi la 2 de k ori. Se demonstreazã uºor

cã acelaºi lucru este valabil ºi dacã 2 k <n<2 k+1 . Dacã n=2 k sau 2 k <n<2 k+ 1 , atunci k=[log 2 n]. Deci

numãrul maxim de execuþii ale procedurii caut ºi implicit, complexitatea algoritmului, este O(log 2 n).

Logaritmul este o funcþie care creºte mult mai încet decât funcþiile polinomiale. De exemplu, pentru un vector cu n=100000 de elemente, în cazul în care valoarea cãutatã nu se gãseºte în vector, cãutarea liniarã va face n=100000 de operaþii elementare, în timp ce cãutarea binarã va face numai log 2 n=log 2 100000=16 operaþii. Dacã n=2 1000 , care are aproximativ 300 de cifre, log 2 n=1000.

În concluzie, cãutarea binarã este mult mai eficientã decât cea liniarã, dar necesitã ca vectorul sã fie sortat, iar sortarea nu poate fi fãcutã (prin metodele care folosesc comparaþii) într-o complexitate mai bunã de O(n lg n).

Sortare prin numãrare

Este demonstrat cã metodele de sortare care folosesc ca operaþie de bazã pentru determinarea ordinii elementelor comparaþia (ex. quicksort, heapsort, sortarea prin interclasare, sortarea prin inserþie) nu pot avea o complexitate, pe cel mai defavorabil caz, mai bunã de O(n lg n). Existã totuºi metode de sortare O(n) care nu folosesc comparaþia elementelor ca operaþie de bazã, o astfel de metodã fiind sortarea prin numãrare.

Ne propunem sã sortãm crescãtor vectorul V, care are n elemente naturale. Definim M ca fiind

valoarea maximã din V. Folosim de asemenea, un vector auxiliar A[1

L], unde LM.

Se iniþializeazã A cu 0 ºi apoi se face o parcurgere a vectorului V incrementându-se la fiecare pas A[V [i]], adica elementul din A corespunzãtor valorii elementului curent din V. În urma acestei parcurgeri A[i] va fi numãrul de elemente cu valoarea i din V (respectiv 0, dacã nu existã nici un element cu valoarea i în V).

Se parcurg apoi vectorii A ºi V în paralel, începând cu poziþia 1. Când se întâlneºte un element A[i] >0, se dã valoarea i urmãtoarelor A[i] poziþii din V.

Exemplu:

n=8 V=<7,5,4,5,1,2,4,5> M=7

Prima parcurgere:

i=1, incrementãm A[V[1]]=A[7] A=<0,0,0,0,0,0,1>

i=2, incrementãm A[V[2]]=A[5] A=<0,0,0,0,1,0,1>

i=3, incrementãm A[V[3]]=A[4] A=<0,0,0,1,1,0,1>

i=4, incrementãm A[V[4]]=A[5] A=<0,0,0,1,2,0,1>

Procedeul continuã pânã la i=8. În final, A=<1,1,0,2,3,0,1>.

Parcurgem apoi A ºi V în paralel, 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, V[4]:=4

i=5 j=5 A[i]=A[5]=3 V[5]:=5, V[6]:=5, V[7]:=5

i=6 j=8 A[i]=A[6]=0

i=7 j=8 A[i]=A[7]=1 V[8]:=7

Rezultã vectorul V sortat: V=<1,2,4,4,5,5,5,7>

Programul constã din douã parcurgeri (nu considerãm ºi parcurgerea pentru iniþializarea vectorului A):

una de lungime n ºi una de lungime M, deci complexitatea algoritmului este O(max(n,M)). Spaþiul de memorie folosit pentru vectorii A ºi V este O(M+n). Se observã cã algoritmul nu este practic decât pentru valori mari ale lui n ºi valori relativ mici ale lui M. Altfel, preferãm un algoritm clasic, ca heapsort sau quicksort, a cãrui complexitate este O(n lg n) ºi spaþiu de memorie O(n). În plus, sortarea prin numãrare are neajunsul cã nu este aplicabilã decât pe vectori cu elemente naturale (sau eventual întregi).

program Sortare_prin_numarare;

const L=200;

var V

:array [1

100]

of integer;

A

:array [1

L]

of integer;

M, n, i, j, k

:integer;

procedure Citire; var i:integer; begin write('n= '); readln(n); for i:=1 to n do begin write('V[',i,']= '); readln(V[i]) end

end;

begin Citire;

FillChar(A,2*L,0);

M:=1;

for i:=1 to n do begin if V[i]>V[M] then M:=i;

A[V[i]]:=A[V[i]]+1;

end; M:=V[M];

j:=0;

for i:=1 to M do if A[i]>0 then for k:=1 to A[i] do begin

j:=j+1;

V[j]:=i; end; for i:=1 to n do write(V[i],' '); writeln; end.

Ciurul lui Eratostene

Ciurul lui Eratostene este o metodã de determinare a numerelor prime mai mici decât n. Pentru a determina dacã un numãr i este prim sau nu, se testeazã divizibilitatea acestuia cu numerele prime deja determinate (þinute în vectorul Prime