Sei sulla pagina 1di 158

Decan Sef de catedra

VIZAT Prof. Univ. Dr. Rodica TRANDAFIR Conf. Univ. Dr. Rodica IOAN

FISA DISCIPLINEI
I.
UNIVERSITATEA SPIRU HARET
FACULTATEA MATEMATICA-INFORMATICA
DOMENIUL DE LICENTA INFORMATICA
SPECIALIZAREA INFORMATICA
Anul universitar 2008 - 2009
Forma de invatamant ZI
II.
DENUMIRE DISCIPLINA TEHNICI AVANSATE DE PROGRAMARE

III.
CODUL DISCIPLINEI
IV.
Statut disciplina Obligatorie Optionala Facultativa
(se marcheaza cu X) X
V. Structura disciplinei (nr. ore)
Semestrul Curs Seminar Laborator Lucrari practice Proiecte
(numar de (nr. ore/sapt. (nr. ore/sapt. (nr. ore/sapt. (nr. ore/sapt. (nr. ore/sapt.
la 1 la 6) si total si total si total si total si total
nr.ore/sem.) nr.ore/sem.) nr.ore/sem.) nr.ore/sem.) nr.ore/sem.)
4 2/28 - 2/28 - -
VI.(ETCS)
Numar credite 6
VII.
OBIECTIVELE DISCIPLINEI
1. Valorificarea cunostintelor acumulate in cadrul cursurilor de Programare Procedurala,
Algoritmi si Structuri de date, Proiectare si Programare Orientata Obiect in vederea
programarii unor algoritmi avansati elaborati cu metode specifice.
2. Crearea deprinderilor de a gandi si programa folosind sabloane (templates)
3. Insusirea unor algoritmi avansati in domeniul prelucrarii sirurilor.
4. Cresterea capabilitatii in utilizarea in programarea generica a limbajelor C++ si Java.
5. Asigurarea compatibilitatii cu invatamantul de excelenta : University of Cambridge
(http://www.cl.cam.ac.uk/DeptInfo/CST05/node30.html); Imperial College London
(http://www3.imperial.ac.uk/computing/teaching/undergraduate/computing/lectures);
Princeton University (http://www.cs.princeton.edu/academics/catalog/ugrad)
VIII.
CONTINUT TEMATIC
I. Structuri de date fundamentale (Recapitulare)
II. Metode avansate de proiectarea algoritmilor:
1. Recursivitate
2. Metoda Greedy
3. Metoda Backtracking
4. Metoda Divide et impera
5. Metoda Programării dinamice
6. Metoda Branch&Bound.
7. Procesarea sirurilor
IX.
TEME SEMINAR
-

X.
LUCRARI DE LABORATOR
1. Metode de elaborarea algoritmilor (14 ore)
2. Biblioteca Standard de Sabloane (STL) a limbajului C++ (6 ore)
3. Cautare in siruri (2 ore)
4. Programare generica in Java (6 ore).
XI.
LUCRARI PRACTICE
-
XII.
PROIECTE
-

XIII. Forma de evaluare (procent din nota finala)


Examen Colocviu Verificare pe Lucrari Laborator Proiecte
parcurs practice
- - 70% - 30% -

XIV. Bibliografie
Obligatorie minimala Suplimentara Facultativa
1. Albeanu G., Algoritmi si 1. Java, Using and 1. N.M. Josuttis, The C++
limbaje de programare, Programming Generics Standard Library, Addison-
Editura FRM, 2000 (pag. 207- in J2SE 5.0, Wesley, 1999.
233) http://java.sun.com/devel 2. A. Drozdek, Data structures
2. Albeanu G. (coord), L. oper/technicalArticles/J2 and algorithms in C++,
Rapeanu, L. Radu, A. SE/generics/ Brooks/Cole, 2001.
Averian, Tehnici de 2. C. Galatan, 3. Roberge J., Brandle S.,
programare, Editura FRM, Introducere in C++ Whittington D., A laboratory
2003. Standard Template course in C++ data structures
3.Doina Logofatu, Algoritmi Library, Ed. All, 2008. (ed. 2), Jones and Bartlett
fundamentali in Java/C++. 3. R. Neapolitan, K. Publishers, 2003.
Aplicatii. Polirom, 2007 Naimipour ,
(integral). Foundations of
4. Valeriu Iorga, Cristian Algorithms Using C++
Opincaru, Corina Stratan, Pseudocode, Jones and
Alexandru Chirita, Structuri Bartlett Publishers, 2004.
de date si algoritmi. Aplicatii
in C++ folosind STL,
Polirom, 2005.

XV.
Metode didactice (clasice/moderne)
1. Prelegerea - proiecţie in amfiteatru, programe demonstrative;
2. Recomandarea, pentru studiul individual, a unor paragrafe din bibliografia indicată, în
vederea aprofundării sau extinderii cunoştinţelor căpătate la curs/laborator ;
3. Prezentarea unor exemple şi a unor probleme aplicative în cadrul cursului pentru sporirea
interesului cursantilor.
4. Evaluare folosind platforma Blackboard.

Data Titular disciplina


Titlul didactic, Numele si Prof. Univ. Dr. GRIGORE
prenumele ALBEANU
_____________________ Semnatura

_______________________
2. Structuri de date
O structură de date este o mulţime de date organizate într-un anumit
mod împreună cu relaţiile dintre acestea.
În funcţie de modul de alocare, structurile de date se clasifică în:
• structuri de date statice : tabloul, înregistrarea, mulţimea, fişierul.
Structurile de date statice ocupă o zonă de memorie constantă pe
toată durata de executare a blocului în care apare declaraţia şi
elementele ocupă poziţii fixe. Fişierele sunt structuri de date
externe (vezi capitolul 1).
• structuri de date semistatice: stiva alocată static, coada alocată
static. Structurile de date semistatice ocupă o zonă de memorie de
dimensiune constată, alocată pe toată durata executării blocului în
care apare declaraţia de structură, iar elementele ocupă poziţii
variabile pe parcursul executării programului.
• structuri de date dinamice: lista înlănţuită, structuri arborescente.
Structurile de date dinamice ocupă o zonă de memorie de
dimensiune variabilă, alocată dinamic. Alocarea dinamică permite
gestionarea memoriei în timpul executării programului. Ele sunt
mult mai flexibile decât cele statice şi din această cauză sunt
extrem de avantajoase.
Liste: O listă este o colecţie de elemente de informaţie (noduri)
aranjate într-o anumită ordine. Lungimea unei liste este numărul de
noduri din listă. Structura corespunzătoare de date trebuie să ne
permită să determinăm eficient care este primul/ultimul nod în
structură şi care este predecesorul/succesorul (dacă există) unui nod
dat. Limbajele Pascal sau C(++) oferă posibilităţi de implementare a
acestor structuri atât static cât şi dinamic. Operaţiile curente care se
fac în liste sunt: inserarea unui nod, ştergerea (extragerea) unui nod,
concatenarea unor liste, numărarea elementelor unei liste etc.
Implementarea unei liste se poate face în principal în două moduri:
• Implementarea secvenţială, în locaţii succesive de memorie,
conform ordinii nodurilor în listă. Avantajele acestei tehnici
sunt accesul rapid la predecesorul/succesorul unui nod şi
găsirea rapidă a primului/ultimului nod. Dezavantajele sunt
inserarea/ştergerea relativ complicată a unui nod şi faptul că
nu se foloseşte întreaga memorie alocată listei.
• Implementarea înlănţuită. Fiecare nod conţine două părţi:
informaţia propriu-zisă şi adresa nodului succesor. Alocarea
31
memoriei fiecărui nod se poate face în mod dinamic, în timpul
rulării programului. Accesul la un nod necesită parcurgerea
tuturor predecesorilor săi, ceea ce poate lua ceva mai mult
timp. Inserarea/ştergerea unui nod este, în schimb, foarte
rapidă.
Listele se pot clasifica după numărul de legături în:
• liste simple
• liste duble
• liste circulare
O listă simplă are o singură legătură, legătura ultimului element este
adresa NIL/NULL. Pentru a accesa lista avem nevoie de o variabilă
care să păstreze adresa primului element (cap sau prim). O listă
dublă are două legături: legătura de tip următor şi legătura de tip
precedent sau anterior. O lista circulară este o lista în care, după
ultimul nod, urmează primul, deci fiecare nod are succesor şi
predecesor.
Listele se pot clasifica după locul în care se fac operaţiile de
adăugare/eliminare astfel:
• stive
• cozi

2.1. Liste simplu înlănţuite

Între nodurile unei liste simplu înlănţuite este definită o singură relaţie
de ordonare. Fiecare nod conţine un pointer a cărui valoare reprezintă
adresa nodului următor din listă.
Limbajul Pascal
Limbajul C(++)
type lista = ^nod typedef struct nod {
nod=record tip inf;
inf:tip ; struct nod *urm;
urm: lista; }lista;
end;
unde urm este adresa următorului nod (pointer către următorul nod),
iar inf este un câmp de informaţie utilă.
Operaţii asupra listei simplu înlănţuite
Crearea unei liste simplu înlănţuite:
1. Se iniţializează pointerul prim cu Nil/NULL, deoarece lista la
început este goală;
2. Se rezervă zonă de memorie în memoria heap pentru nodul curent;
32
3. Se încarcă nodul curent cu date;
4. Se determină adresa ultimului nod şi se realizează legătura cu
nodul creat;
5. Se reia cu pasul 2 dacă se introduce un nod nou.
Inserarea unui element x al cărui câmp cheie a fost iniţializat în
faţa unei liste înlănţuite
LISTA-INSEREAZA(p,x)
1: urm(x) ←p
2: dacă p<>NIL atunci p←x
Căutarea într-o listă înlănţuită p a elementului x se realizează
prin subprogramul LISTA-CAUTA şi returnează pointerul la acest
obiect.
LISTA-CAUTA(p,x)
1. q←p
2. cât timp q<>NIL şi cheie(q)<>x execută q←urm(q)
3. returnează q

Probleme rezolvate
1. Fiind dată o listă simplu înlănţuită cu elemente numere întregi să se
realizeze un program care să execute următoarele operaţii: crearea,
parcurgerea, adăugarea unui nod la începutul listei, adăugarea unui nod
la sfârşitul listei, ştergerea unui nod de pe o poziţie dată.

Observaţii:
Limbajul Pascal: Limbajul C(++)
Procedura NEW(pointer)- alocarea Funcţia MALLOC se foloseşte
dinamică a memoriei pentru pentru a rezerva octeţi din
variabila dinamică pointer. memoria heap. Trebuie inclus
Procedura DISPOSE(pointer)- fişierul antet: stdlib.h sau
eliberarea memoriei ocupate de alloc.h
către variabila dinamică pointer.

Rezolvare:
Parcurgerea unei liste liniare simplu înlănţuite se realizează cu un
pointer care pleacă de la capul listei şi va referi pe rând fiecare nod,
prelucrând informaţiile din nod apoi se trece la următorul nod, prelu-
crăm informaţiile din nod etc.

33
Ştergerea unui nod de pe o poziţie dată p din interiorul listei se
realizează astfel: se parcurge lista până la pozitia p-1, se păstrează
nodul de pe poziţia p, se realizează legătura între nodul p-1 şi p+1 şi,
apoi se eliberează memoria ocupată de nodul p.
Implementare în Pascal:

type lista=^nod;
nod=record inf:integer; urm:lista end;
var p,x:integer;cap:lista;{adresa primului nod al listei}
procedure adaug(var cap:lista;x:integer); {adaugarea la sfarsitul listei}
var nou,q:lista;
begin
new(nou);nou^.inf:=x;nou^.urm:=nil;
if cap=nil then cap:=nou
else begin
q:=cap;
while q^.urm<> nil do q:=q^.urm;
q^.urm:=nou;
end;
end;
procedure adaug_inc(var cap:lista;x:integer); {adaugarea la inceput}
var nou:lista;
begin
new(nou);nou^.inf:=x;nou^.urm:=nil; {crearea nodului nou}
if cap=nil then cap:=nou
else begin
nou^.urm:=cap;cap:=nou;
{realizarea legaturii si primul nod devine nodul creat}
end;
end;
procedure listare(cap:lista);{listarea listei}
var t:lista;
begin
t:=cap;
while t<>nil do begin
write(t^.inf,' '); {prelucrarea informatiei}
t:=t^.urm; {trecerea la urmatorul nod}
end;
end;
procedure sterge(var cap:lista; p:integer);
34
{stergerea nodului de pe pozitia p}
var q,w,t:lista;i:integer;
begin
if cap=nil then writeln('Lista vida !!! ')
else if p=1 then begin
{stergere la inceputul listei}
q:=cap; cap:=cap^.urm; dispose(q);
end
else if (cap<> nil) then begin
t:=cap; i:=1;
while (t^.urm<> nil) and (i+1<p) do
begin inc(i) ;t:=t^.urm end;
if t^.urm=nil then write('nu exista suficiente pozitii')
else begin
w:=t^.urm; t^.urm:=t^.urm^.urm;
dispose(w)
end
end
end;
begin
read(x);
while x<>0 do begin adaug_inc(cap,x); read(x); end;
listare(cap);
write('pozitia');read(p);sterge(cap,p);
listare(cap);
write('informatia adaugata la sfarsit');read(x);adaug(cap,x);
listare(cap)
end.

Implementare în C:
#include <stdio.h>
#include<stdlib.h>
#include<alloc.h>
typedef struct nod {
int inf;
struct nod*urm;
}lista;
lista *prim;
lista *creare(void);
void parcurgere(lista *p);
35
lista* sterg_inc(lista *prim){
lista *p;
p=prim;
prim=prim->urm;
free(p);
return prim;
}
void adauga(lista*prim) {
/*adauga un nod la o lista simplu inlantuita
si returneaza pointerul spre nodul adaugat sau zero daca nu s-a realizat
adaugarea*/
lista *p,*q;
for (p=prim;p->urm!=NULL;p=p->urm)
q=(lista*) malloc(sizeof(q));
scanf("%d",&q->inf);
q->urm=NULL;
p->urm=q;
}
void main(){
lista *prim;
prim=creare(); parcurgere(prim); prim=sterg_inc(prim);
printf("\n");
parcurgere(prim); adauga(prim); parcurgere(prim);
}
lista *creare(){
int n,i,inf; lista *prim,*p,*q;
printf("nr. de elemente");scanf("%d",&n);
printf("informatia primului nod"); scanf("%d",&inf);
prim=(lista*)malloc(sizeof(prim)); prim->inf=inf;prim->urm=NULL;
for(q=prim,i=2;i<=n;i++){
printf("inf . %d:",i);scanf("%d",&inf);
p=(lista*)malloc(sizeof(p));
p->inf=inf;p->urm=NULL; q->urm=p; q=p;
}
return(prim);
}
void parcurgere(lista *p){
lista *q;
for (q=p;q;q=q->urm) printf("%d ",q->inf);
}

36
2. Inversarea legăturilor într-o listă simplu înlănţuită.

Rezolvare:
Se parcurge lista iniţială folosind trei variabile dinamice p1, p2, p3
care vor face referire la elementele consecutive din listă. p1 va fi
capul listei modificate.

Implementare Pascal:
program invers;
type lista=^nod;
nod=record inf:integer; urm:lista end;
var i,n,x:integer; p:lista;
procedure creare(var p:lista; x:integer);
var q:lista;
begin
if p=nil then begin new(p); p^.inf:=x; p^.urm:=nil end
else begin
q:=p; while q^.urm<>nil do q:=q^.urm;
new(q^.urm); q^.urm^.inf:=x; q^.urm^.urm:=nil;
end;
end;
procedure listare(p:lista);
var q:lista;
begin
q:=p; while q<>nil do begin write(q^.inf,' '); q:=q^.urm end;
end;
function inversare(p:lista):lista; Subprogramul de inversare în C:
var p1,p2,p3:lista; lista* invers(lista*p){
begin lista *p1,*p2,*p3;
p1:=p; p1=p;
p2=p->urm;
p2:=p^.urm;
p->urm=NULL;
p^.urm:=nil;
while (p2){
while p2<>nil do begin
p3=p2->urm;
p3:=p2^.urm;
p2->urm=p1;
p2^.urm:=p1;
p1=p2;
p1:=p2; p2=p3;
p2:=p3; }
end; return p1;
inversare:=p1; }
end;

37
begin
read(n);
for i:=1 to n do begin read(x); creare(p,x) end;
listare(p);writeln;
p:=inversare(p);
listare(p)
end.

3. Să se efectueze suma a două polinoame rare (polinom cu foarte


mulţi coeficienţi egali cu zero) folosind liste simplu înlănţuite.

Rezolvare:
Lista are ca informaţie gradul şi coeficientul fiecărui termen de
coeficient nenul. Pentru a calcula suma este necesar să parcurgem
listele celor două polinoame şi să adăugăm corespunzător în cea de-a
treia listă.

Implementare Pascal:
type lista=^nod;
nod=record grad:1..5000; coef:integer; urm:lista end;
var a,b,p,q,r:lista;
i,n,m:integer;
procedure creare(var p:lista);
begin
write('cati coeficienti nenuli are polinomul');readln(n);
new(p);readln(p^.coef,p^.grad);
p^.urm:=nil;b:=p; {b este adresa ultimului nod}
for i:=2 to n do begin
new(a);
write('coef ',i,':');readln(a^.coef); write('grad ',i,':');readln(a^.grad);
b^.urm:=a; b:=a; b^.urm:=nil
end
end;
procedure listare(p:lista);
var a:lista;
begin
a:=p;
while a<>nil do begin write(a^.coef,'x^', a^.grad,' +'); a:=a^.urm end;
writeln(#8,' ');
end;

38
procedure aduna(p,q:lista;var r:lista);
var a,b,c,d:lista;
begin
a:=p;b:=q; {c este adresa ultimului nod pentru lista suma}
while (a<>nil) and (b<>nil) do
if a^.grad=b^.grad then begin
if r=nil then begin
new(c);c^.grad:=a^.grad;
c^.coef:=a^.coef +b^.coef;
r:=c; r^.urm:=nil;
end
else begin
new(d); d^.grad:=a^.grad;
d^.coef:=a^.coef+b^.coef;
c^.urm:=d;c:=d;c^.urm:=nil
end;
a:=a^.urm;b:=b^.urm;
end
else if a^.grad<b^.grad then begin
if r=nil then begin
new(c); c^.grad:=a^.grad;
c^.coef:=a^.coef ; r:=c; r^.urm:=nil;
end
else begin
new(d); d^.grad:=a^.grad; d^.coef:=a^.coef;
c^.urm:=d;c:=d;c^.urm:=nil;
end;
a:=a^.urm;
end
else begin
if r=nil then begin
new(c);c^.grad:=b^.grad;
c^.coef:=b^.coef;r:=c;r^.urm:=nil;
end
else begin
new(d); d^.grad:=b^.grad; d^.coef:=b^.coef;
c^.urm:=d;c:=d;c^.urm:=nil;
end;
b:=b^.urm;
end;

39
if a<>nil then c^.urm:=a;
if b<>nil then c^.urm:=b;
end;
begin
creare(p);creare(q);listare(p);listare(q);
r:=nil;
aduna(p,q,r);
listare(r);
readln;
end.
Exerciţii:
1. Să se implementeze în C aplicaţia de mai sus.
2. Să se scrie un subprogram pentru calcularea produsului a două
polinoame rare.
3. Să se calculeze valoarea unui polinom într-un punct dat.
Indicaţie:
Calcularea produsului se va face prin parcurgerea tuturor perechilor de
termeni astfel:
-fiecare pereche generează un nod în polinomul produs
-gradul noului nod va fi suma gradelor nodurilor din pereche
-coeficientul noului nod va fi produsul coeficienţilor termenilor din
pereche
-se elimină nodurile din lista de termeni prin păstrarea fiecărui grad o
singură dată astfel: dacă există două noduri cu acelaşi grad unul din
ele va fi eliminat, iar coeficientul celuilalt va avea valoarea sumei
coeficienţilor celor doi termeni.
Valoarea unui polinom se va calcula prin parcurgerea listei şi
adăugând la valoare produsul dintre coeficient şi valoarea dată la
puterea gradului din nod.

4.Dându-se două liste simplu înlănţuite cu elemente numere intregi


distincte, să se afiseze diferenţa celor două liste şi elementele comune
celor două liste.
Rezolvare:
Diferenţa celor două liste conţine elementele primeia care nu
sunt în cea de-a doua. Se parcurge prima listă şi pentru fiecare nod se
verifică dacă este în cea de-a doua listă, dacă nu se află atunci se
adaugă listei trei. Intersecţia celor două liste se determină parcurgând

40
prima listă şi pentru fiecare nod se verifică dacă elementul se află şi în
cea de-a doua listă, dacă da atunci se adaugă în cea de-a treia listă.

Implementare Pascal:
type lista=^nod;
nod = record inf:integer; urm:lista end;
var q,cap1,cap2,cap3:lista;
gasit:boolean;
procedure creare(var cap:lista);
var n,i:integer;
nou,q:lista;
begin
write('nr elem'); read(n); new(cap); read(cap^.inf);
cap^.urm:=nil;
for i:=2 to n do begin
new(nou); read(nou^.inf); nou^.urm:=nil; q:=cap;
while q^.urm<>nil do q:=q^.urm;
q^.urm:=nou
end
end;
procedure diferenta(cap1,cap2:lista);
var x:integer;
q2:lista;
begin
q:=cap1;
while q<>nil do begin
x:=q^.inf; gasit:=false; q2:=cap2;
while q2<>nil do begin
if q2^.inf=x then gasit:=true; q2:=q2^.urm
end;
if not gasit then write(x, ' '); q:=q^.urm
end
end;
procedure afisare(cap:lista);
var q:lista;
begin
q:=cap;
while q<>nil do begin write(q^.inf,' '); q:=q^.urm end;
writeln;
end;

41
procedure intersectie(cap1,cap2:lista; var cap3:lista);
var q,q2,nou,q3:lista;
x:integer;
begin
q:=cap1;
while q<>nil do begin
x:=q^.inf; gasit:=false; q2:=cap2;
while q2<>nil do begin
if q2^.inf=x then gasit:=true;
q2:=q2^.urm;
end;
if gasit then if cap3=nil then
begin new(cap3); cap3^.inf:=x; cap3^.urm:=nil end
else begin
new(nou); nou^.inf:=x ; nou^.urm:=nil; q3:=cap3;
while q3^.urm<> nil do q3:=q3^.urm;
q3^.urm:=nou
end;
q:=q^.urm
end
end;
begin
creare(cap1); creare(cap2); afisare(cap1); afisare(cap2);
diferenta(cap1,cap2);
writeln; diferenta(cap2, cap1);
writeln('intersectie'); intersectie(cap1,cap2,cap3); afisare(cap3);
end.

Exerciţiu: Să se implementeze aplicaţia de mai sus in C.

5. Să se creeze o listă simplu înlănţuită, cu informaţie numerică astfel


încât la orice moment al inserării lista să fie ordonată crescător după
informaţie.

Rezolvare:
Paşii: -crearea unui nod nou
-dacă informaţia care se adaugă este mai mică decât informaţia
din capul listei atunci se inserează în faţa primului nod
-altfel se parcurge lista până la primul nod a cărei informaţie este
mai mare decât informaţia noului nod şi se inserează.

42
Implementare C:

#include <stdio.h>
#include <stdlib.h>
#include <alloc.h>
typedef struct nod { int inf; struct nod*urm;} lista;
lista *prim;
lista *creare(void);
void parcurgere(lista *p);
void main(){
lista *prim;
prim=creare();
parcurgere(prim);
}
lista *creare(){
int n,inf; lista *prim,*p,*q,*nou;
printf("nr. de elemente");scanf("%d",&n);
prim=NULL;
for(int i=1;i<=n;i++){
printf("informatia nod");scanf("%d",&inf);
nou=(lista *)malloc(sizeof(lista));
nou->inf=inf;
if (prim==NULL){
prim=nou;prim->urm=NULL;
}
else if (prim->inf>inf){
nou->urm=prim;prim=nou;
}
else {
p=q=prim;
while(p&&p->inf<inf){q=p; p=p->urm;}
if (p) {q->urm=nou; nou->urm=p;}
else{q->urm=nou; nou->urm=NULL;}
}
}
return prim;
}
void parcurgere(lista *p){ lista *q;
for (q=p;q;q=q->urm) printf("%d ",q->inf);
}

43
Exerciţiu: Să se implementeze aplicaţia de mai sus în Pascal.

6. Să se scrie un program pentru interclasarea a două liste ordonate


simplu înlănţuite.

Rezolvare:
Se va parcurge simultan cele două liste urmând ca introducerea unui
nod în lista finală să fie făcută din lista care are valoarea informaţiei
din nod mai mică.

Implementare C:
#include <stdio.h>
#include <stdlib.h>
#include <alloc.h>
typedef struct nod {int inf; struct nod*urm;}lista;
lista *inter(lista *prim1, lista*prim2){
lista *prim,*ultim;
if (prim1->inf>prim2->inf){
prim=prim2;prim2=prim2->urm;
}
else {
prim=prim1;
prim1=prim1->urm;
}
ultim=prim;
while(prim1&&prim2)
if (prim1->inf>prim2->inf){
ultim->urm=prim2;
ultim=prim2;
prim2=prim2->urm;
}
else {ultim->urm=prim1;
ultim=prim1;
prim1=prim1->urm;
}
if (prim1) ultim->urm=prim1; else ultim->urm=prim2;
return prim;
}
lista *creare(void);
void parcurgere(lista *p);

44
void main(){
lista *prim1,*prim2,*prim;
prim1=creare();
prim2=creare();
/* parcurgere(prim1) */;
prim=inter(prim1,prim2);
parcurgere(prim1);
}
lista *creare(){
int n,inf; lista *prim,*p,*q,*nou;
printf("nr. de elemente");scanf("%d",&n);
prim=NULL;
for(int i=1;i<=n;i++){
printf("informatia nod");scanf("%d",&inf);;
nou=(lista *)malloc(sizeof(lista));
nou->inf=inf;
if (prim==NULL){
prim=nou;
prim->urm=NULL;
}
else if (prim->inf>inf){
nou->urm=prim;prim=nou;
}
else {
p=q=prim;
while(p&&p->inf<inf){q=p;p=p->urm;}
if(p) {q->urm=nou;nou->urm=p;}
else {
q->urm=nou;nou->urm=NULL;
}
}
}
return prim;
}
void parcurgere(lista *p){ lista *q;
for (q=p;q;q=q->urm) printf("%d ",q->inf);
}

Exerciţiu: Să se implementeze aplicaţia de mai sus în limbajul Pascal.

45
2.2. Liste dublu înlănţuite

Pentru liste duble create dinamic modul de definire a unui nod este:
Limbajul Pascal typedef struct nod{
type lista=^nod; inf tip;
nod=record struct nod *urm;
inf:tip; struct nod *ant;
urm, ant:lista; }lista;
end;

Operaţiile care se pot defini asupra listelor dublu înlănţuite sunt


aceleaşi ca şi în cazul listelor simplu înlănţuite:
- crearea unei liste dublu înlănţuite;
- accesul la un element al unei liste dublu înlănţuite;
- inserarea unui nod într-o listă dublu înlănţuită;
- ştergerea unui nod dintr-o listă dublu înlănţuită;
- ştergerea unei liste dublu înlănţuite.

Probleme rezolvate

1. Să se scrie un program care va conţine un meniu cu principale


operaţii asupra listei dublu înlănţuite.

Implementarea Pascal a soluţiei:


type lista=^nod;
nod=record inf:integer; urm,ant:lista end;
var cap:lista;
x:integer;
procedure creare(var cap:lista);
begin
new(cap); write('inf=');readln(cap^.inf); cap^.urm:=nil;cap^.ant:=nil;
end;
procedure adaugare(var cap:lista);
var q,nou:lista;
begin
new(nou);readln(nou^.inf);nou^.urm:=nil;
q:=cap; while q^.urm <> nil do q:=q^.urm; q^.urm:=nou;nou^.ant:=q;
end;
46
procedure inserare(var cap:lista);
var nou,q:lista;
k,i:integer;
begin
writeln('unde inserezi? ');
read(k);new(nou);
write('ce? ');readln(nou^.inf);
if k=1 then begin
cap^.ant:=nou; nou^.urm:=cap; nou^.ant:=nil; cap:=nou;
end
else begin
q:=cap;
i:=1;
while (q^.urm<>nil) and (i<k) do begin
q:=q^.urm; inc(i)
end;
if i=k then begin
nou^.ant:=q^.ant; q^.ant^.urm:=nou;
nou^.urm:=q; q^.ant:=nou;
end
else begin write('nu exista');
readln
end
end
end;
procedure stergere(var cap:lista);
var q,p:lista;
k,i:integer;
begin
write('unde stergi? '); readln(k);
if cap<>nil then
if k=1 then begin q:=cap; cap:=cap^.urm;
dispose(q);
cap^.ant:=nil;
end
else begin
p:=cap;i:=1;
while (p^.urm<>nil) and(i<k) do begin
p:=p^.urm; inc(i);
end;

47
if i=k then begin
q:=p; p^.ant^.urm:=p^.urm;
p^.urm^.ant:=p^.ant; dispose(q);
end
else begin write('nu exista'); readln end
end
end;
procedure parcurgere(var cap:lista);
var q:lista;
begin
q:=cap;
while q<>nil do begin write(q^.inf, ' '); q:=q^.urm end;
readln
end;
procedure parcurgere_inv(var cap:lista);
var q:lista;
begin
q:=cap;
while q^.urm<>nil do q:=q^.urm;
while q<>nil do begin write(q^.inf, ' '); q:=q^.ant end;
readln;
end;
begin
while x<> 7 do begin
writeln('1.creare'); writeln('2.adaugare');
writeln('3.inserare'); writeln('4.stergere');
writeln('5.parcurgere'); writeln('6.parcurgere_inv');
writeln('7.iesire');
readln(x);
case x of
1:creare(cap);
2:adaugare(cap);
3:inserare(cap);
4:stergere(cap);
5:parcurgere(cap);
6:parcurgere_inv(cap);
end
end
end.

48
2. Crearea şi parcurgerea listei dublu înlănţuite în limbajul C:

#include<stdio.h>
#include<stdlib.h>
typedef struct nod{int inf; struct nod*urm,*ant; } lista;
lista*prim ,*ultim;
void creare(int n);
void parcurg(lista*prim);
void parcurg1(lista*ultim);
void main(){
int n;
printf("numarul de elemente:");scanf("%d",&n);
creare(n);
puts("\n Parcurgere directa");parcurg(prim);
puts("\n Parcurgere indirecta");parcurg1(ultim);
}
void creare(int n){
int i;
lista *p;
prim=(lista*)malloc(sizeof(lista));
printf("informatia primului nod"); scanf("%d",&prim->inf);
prim->urm=prim->ant=NULL;
ultim=prim;
for(i=2;i<=n;i++){
p=(lista*)malloc(sizeof(lista));
printf("inf=");scanf("%d",&p->inf);
p->ant=ultim;ultim->urm=p;
p->urm=NULL;
ultim=p;
}
}
void parcurg(lista*p){
if (p){
printf("%d ",p->inf); parcurg(p->urm);
}
}
void parcurg1(lista *p){
lista *q;
for (q=p;q;q=q->ant) printf("%d ",q->inf);
}

49
2.3. Liste circulare
După numărul de legături, listele circulare se împart în: liste simple şi
liste duble. Listele circulare simple sunt liste simple care au în plus
propietatea că valoarea câmpului următor a ultimului nod este adresa
capului listei. Listele circulare duble sunt liste duble care au
propietatea că legătura următor a celui de-al doilea cap este primul
cap şi legătura anterior a primului cap este al doilea cap.

Crearea şi parcurgerea listei circulare simplu înlănţuite în limbajul


C:

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
typedef struct nod{ int inf; struct nod *urm;}lista;
void main(){
lista *prim,*p,*q;
int i,n;
/*crearea listei circulare*/
printf("inf primului nod");
prim=(lista*)malloc(sizeof(lista));
scanf("%d",&prim->inf);prim->urm=NULL;
p=prim;
printf("nr de elemente");scanf("%d",&n);
for (i=2;i<=n;i++){
q=(lista*)malloc(sizeof(lista));
printf("inf=");scanf("%d",&q->inf);
p->urm=q;p=q;
}
p->urm=prim;
/*parcurgerea listei*/
printf("%d ",prim->inf);
for (q=prim->urm;q!=prim;q=q->urm)
printf("%d ",q->inf);
}

Problemă rezolvată: Se dă un grup de n copii aşezaţi în cerc, care


sunt număraţi din m în m. Copilul care a fost numărat cu valoarea m
este eliminat. Dându-se pasul de eliminare m se cere să se precizeze
ordinea ieşirii din cerc.
50
Rezolvare: Simularea eliminării copiilor se realizează cu o listă
circulară la care se realizează eliminarea nodului care are ca
informaţie numărul copilului scos din joc. Numărarea se va face
parcurgând m elemente de la poziţia de unde s-a efectuat ultima
ştergere.

Implementarea Pascal (Lista circulară este simplă.)


type lista=^nod;
nod=record inf:integer; urm:lista end;
var i,n,m,j:integer;
prim,q,p:lista;
procedure creare(var prim:lista;x:integer);
begin new(prim); prim^.inf:=x; prim^.urm:=prim end;
procedure adaugare(var prim:lista;x:integer);
var q,p:lista;
begin
new(q);
q:=prim;
while q^.urm<>prim do q:=q^.urm;
new(p); p^.inf:=x;
q^.urm:=p;p^.urm:=prim;
end;
procedure listare(prim:lista);
var q:lista;
begin
new(q);q:=prim;
write(q^.inf,' ');
while q^.urm<>prim do begin q:=q^.urm; write(q^.inf,' ') end;
end;
begin {program principal}
read(n); creare(prim,1);
for i:=2 to n do adaugare(prim,i); listare(prim);
read(m); p:=prim;
for i:=1 to n-1 do begin
if i=1 then for j:=1 to m-2 do p:=p^.urm
else for j:=1 to m-1 do p:=p^.urm;
q:=p^.urm; write(q^.inf,' '); p^.urm:=q^.urm; dispose(q);
end;
writeln('castigator=',p^.inf);
end.

51
Implementarea C (Lista circulară este dublă).
#include <stdio.h>
#include<stdlib.h>
typedef struct nod{int inf; struct nod *urm,*ant; }lista;
lista *prim,*p,*q;
void creare(void);
void parcurgere(void);
int n,m,i,k;
void main(){
creare();
printf("pasul de numarare");scanf("%d",&m);
parcurgere();
while (p!=p->ant){
for( i=1;i<m;i++)p=p->urm;
printf("%d ",p->inf);
p->ant->urm=p->urm;
p->urm->ant=p->ant;
q=p;p=p->urm;free(q);
}
printf("%d",p->inf);
}
void parcurgere(){
for (p=prim,i=1;i<=n;i++,p=p->urm) printf("%d",p->inf);
/* p=p->urm;*/
}
void creare(){
printf("nr. de copii");scanf("%d",&n);
prim=(lista*)malloc(sizeof(lista));prim->inf=1;
prim->urm=prim->ant=NULL; p=prim;
for (i=2; i<=n; i++){
q=(lista*)malloc(sizeof(lista));
q->inf=i;
p->urm=q;
q->ant=p;
p=q;
}
q->urm=prim;prim->ant=q;
}

52
2.4. Stive
O stivă (stack) este o listă liniară cu proprietatea că operaţiile de
inserare/extragere a nodurilor se fac în/din vârful listei. Ultimul nod
inserat va fi şi primul şters, stivele se mai numesc şi liste LIFO (eng.
Last In First Out) sau liste pushdown.

push
pop

Cel mai natural mod de reprezentare pentru o stivă este implementarea


secvenţială într-un tablou S[1 .. n], unde n este numărul maxim de
noduri. Primul nod va fi memorat în S[1], al doilea în S[2], iar ultimul
în S[top], unde top este o variabilă care conţine adresa (indicele)
ultimului nod inserat. Algoritmii de inserare şi de ştergere (extragere)
a unui nod:

function push(x, S[1 .. n])


{adauga nodul x in stiva}
if top = n then return “stiva plina”
top ←top+1
S[top]← x
return “succes”

function pop(S[1 .. n])


{sterge ultimul nod inserat din stiva si il returneaza}
if top =0 then return “stiva vida”
x ← S[top]
top ← top-1
return x

Cei doi algoritmi necesită timp constant, deci nu depind de mărimea


stivei.
53
Problemă rezolvată: Realizaţi un meniu în limbajul Pascal care să
conţină operaţii asupra stivei.

Rezolvare:
Implementarea Pascal:
type stiva=^nod;
nod=record inf:integer; urm:stiva end;
var cap:stiva;
x:integer;
procedure adauga(var cap:stiva);
var nou:stiva;
begin
new(nou);
writeln('Ce sa adaug? ');
readln(nou^.inf);
nou^.urm:=nil;
if cap=nil then cap:=nou
else begin
nou^.urm:=cap;
cap:=nou;
end;
end;
procedure sterge(var cap:stiva);
var q:stiva;
begin
if cap=nil then writeln('Stiva vida')
else begin
q:=cap;
cap:=cap^.urm;
dispose(q)
end;
end;
procedure parcurgere(cap:stiva);
var q:stiva;
begin
q:=cap;
while q<> nil do begin
writeln('|',q^.inf,'|'); q:=q^.urm end;
writeln('___')
end;

54
begin
while x<> 4 do begin
writeln('1.Adaugare');
writeln('2.Stergere');
writeln('3.Listare');
writeln('4.Iesire');
writeln('Dati optiunea');
readln(x);
case x of
1:adauga(cap);
2:sterge(cap);
3:parcurgere(cap)
end
end
end.

Exerciţiu: Să se implementeze în limbajul C aplicaţia de mai sus.


Probleme propuse
1. Să se scrie un program care citind numere întregi din fişierul in.txt
creează o stivă şi o afişează. Să se transforme un număr din baza
10 în baza b folosind o stivă.
2. Pe o linie de cale ferată se găsesc, într-o ordine oarecare, n
vagoane numerotate de al 1 la n. Linia continuă cu alte k linii de
manevră. Cunoscând ordinea iniţială a vagoanelor, să se obţină la
ieşire vagoanele în ordine: 1,2 ,n; liniile de manevră sunt destul
de lungi încât să încapă pe o singură linie toate cele n vagoane.
Indicaţie: Se doreşte partiţionarea vagoanelor în k submulţimi care au
vagoanele ordonate crescător
2.5. Cozi
O coadă (eng. queue) este o listă liniară în care inserările se fac doar
în capul listei, iar extragerile doar din coada listei. Cozile se numesc şi
liste FIFO (eng. First In First Out). O reprezentare secvenţială pentru
o coadă se obţine prin utilizarea unui tablou C[0 .. n-1], pe care îl
tratăm ca şi cum ar fi circular: după locaţia C[n-1] urmează locaţia
C[0]. Fie p variabila care conţine indicele locaţiei predecesoare primei
locaţii ocupate şi fie u variabila care conţine indicele locaţiei ocupate
ultima oară. Variabilele p şi u au aceeaşi valoare atunci şi numai

55
atunci când coada este vidă. Iniţial, avem p= u= 0. Inserarea şi
ştergerea (extragerea) unui nod necesită timp constant.

Operaţii asupra cozii:


function insert-queue(x, C[0 .. n-1])
{adauga nodul x in capul cozii}
p ← (p+1) mod n
if p=u then return “coada plina”
C[p] ← x
return “succes”
function delete-queue(C[0 .. n-1])
{sterge nodul din coada listei si il returneaza}
if p=u then return “coada vida”
u ← (u+1) mod n
x ← C[u]
return x
Testul de coadă vidă este acelaşi cu testul de coadă plină. Dacă s-ar
folosi toate cele n locaţii, atunci nu am putea distinge situaţia de
“coadă plina” şi cea de “coadă vidă”, deoarece în ambele situaţii am
avea p = u. Se folosesc efectiv numai n-1 locaţii din cele n ale
tabloului C, deci se pot implementa astfel cozi cu cel mult n-1 noduri.

Problemă rezolvată: Relizarea unui meniu pentru implementarea


statică a cozii.

Rezolvare: Cea mai simplă implementare a cozii static este folosirea


unui tablou. Pentru gestionarea cozii este nevoie de două elemente: p
poziţia primului element şi u poziţia de după ultimul element din
coadă. Se va face o utilizare circulară a spaţiului alocat astfel:
următoarea poziţie este p mod n +1.

Implementarea Pascal:
TYPE coada=array[1..50] of integer;
var c:coada; p,n,u,o,x:integer;
procedure adaugare(var c:coada; var p,u:integer;x:integer);
begin
if p<>(u mod n)+1 then
begin c[u]:=x; u:=u mod n +1 end
else writeln('coada plina');
end;
56
procedure stergere(var c:coada; var p,u,x:integer);
begin
if p<>u then begin x:=c[p]; p:=p mod n+1 end
else writeln('coada goala');
end;
procedure listare(c:coada;p,u:integer);
var i:integer;
begin
if p=u then writeln('coada goala')
else begin
i:=p;
while i<>u do begin write(c[i],' '); i:=i mod n+1 end;
end;
end;
begin
writeln('nr max de elem');
read(n);n:=n+1;
p:=1;u:=1;
repeat
writeln('1..adaugare');
writeln('2..stergere');
writeln('3..listare');
write('citeste optiunea');readln(o);
case o of
1: begin
write('introduceti numarul adaugat');readln(x);
adaugare(c,p,u,x);
end;
2: begin
stergere(c,p,u,x);
if p<>u then writeln(x);
end;
3: listare(c,p,u);
end;
writeln;
until o=4;
end.

Exerciţiu: Să se implementeze aplicaţia de mai sus în limbajul C++.

57
Problemă rezolvată: Să se creeze un program care să conţină un
meniu cu operaţii asupra unei cozi alocate dinamic.

Rezolvare:
Implementarea în limbajul C++:
#include <conio.h>
#include<stdlib.h>
#include<iostream.h> // intrări-ieşiri în C++
typedef struct nod {int inf; struct nod *urm; }lista;
void adaug(lista* &prim, int x){
lista *nou, *p;
nou=new lista; // crearea unui nod în C++
nou->inf=x;nou->urm=NULL;
if (prim==NULL) prim=nou;
else {
nou->urm=prim; prim=nou;
}
}
lista * creare(){
int n,x;
lista*prim;
prim=NULL;
clrscr();
cout<<"\n nr. de noduri=";cin>>n;
for (int i=0;i<n;i++){
cout<<"inf "<<(i+1)<<":";
cin>>x;
adaug(prim,x);
}
return prim;
}
void parcurgere(lista *prim){
lista *p;
clrscr();
p=prim;
while (p!=NULL){
cout<<p->inf<<' '; p=p->urm;}
cout<<'\n';
getch();
}

58
void sterg(lista *&prim){
lista *p;
if (prim==NULL) cout<<"coada este vida";
else {
p=prim;
while (p->urm->urm!=NULL){p=p->urm;}
delete p->urm; p->urm=NULL;
}
}
void main(){
lista *prim=NULL;
int opt,x;
do{
clrscr();
cout<<"Coada\n";
cout<<"1.Creare coada\n";
cout<<"2.Adaugare\n";
cout<<"3.Stergere\n";
cout<<"4.Parcurgere\n";
cout <<"5.Iesire\n";
do{
cout<<"optiunea";cin >>opt;
}while (opt<1&&opt>5);
switch(opt){
case 1:{prim=creare();break;}
case 2:{
clrscr();cout<<"inf. noua:";cin>>x;
adaug(prim,x);break;
}
case 3:{
clrscr();
cout<<"inf stearsa="<<(prim->inf);
sterg(prim);break;
}
case 4:{
parcurgere(prim); getch();break;
}
}
}while (opt!=5);
}

59
Exerciţiu: Să se realizeze programul în Pascal corespunzător
aplicaţiei de mai sus.

Probleme propuse:
1. Fiind dată o listă simplu înlănţuită având informaţii numere întregi
să se elimine numerele negative.
2. Să se realizeze interclasarea a n liste simplu înlănţuite ordonate
crescător.
3. Fiind date două liste simple cu informaţii de tip întreg, să se
realizeze subprograme pentru următoarele operaţii: intersecţia,
reuniunea, diferenţa şi diferenţa simetrică a elementelor celor
două liste.
4. Să se scrie un program care citeşte cuvintele dintr-un fişier text
in.txt, cuvintele sunt separate prin spaţii şi afişează numărul de
apariţii al fiecărui cuvânt din fişier. Se vor folosi liste simplu
înlănţuite.
5. Să se elimine dintr-o listă simplu înlănţuită toate nodurile care au
o informaţie dată.
6. Să se realizeze operaţii cu matrici rare folosind alocarea dinamica.
7. Fiind dată o listă dublă, să se separe în două liste elementele de pe
poziţii pare de elementele de pe poziţii impare.
8. Fiind dată o listă dublă cu informaţii de tip şir de caractere, să se
facă ordonarea elementelor listei.
9. Fiind dată o listă dublă cu informaţii de tip întreg să se
construiască o listă dublă numai cu elemente prime.
10. Pe o tijă sunt n bile colorate cu cel mult k culori, fiecare bilă
având o etichetă cu un număr de la 1 la n. Să se mute bilele pe alte
k tije, pe fiecare punând numai bile din aceeaşi culoare. Să se
afişeze bilele de pe fiecare din cele k tije, folosind structuri
dinamice.
Indicaţie: Tija iniţială reprezintă o stivă, informaţia dintr-un nod va fi
compusă din numărul bilei şi numărul culorii. Se va parcurge stiva şi
se adaugă în stiva corespunzătoare culorii, apoi se şterge din stiva
iniţială. Se va lucra cu un vector de stive pentru cele k tije.

11. Să se implementeze subprograme pentru operaţiile de bază cu liste


circulare duble.
12. Fie un traseu circular ce cuprinde n oraşe. O maşină trebuie să
parcurgă toate oraşele şi să se întoarcă de unde a plecat.
Parcurgerea se poate face în ambele sensuri. Se cere să se

60
determine un traseu posibil de parcurgere astfel încât să nu rămână
în pană ştiind că în fiecare oraş există o staţie de benzină cu o
cantitate de benzină suficientă, iar maşina consuma c litri la 100
de kilometri.
13. Fiind dată o listă circulară dublă, să se facă un subprogram de
inserare a unui element pe o poziţie dată.
14. Să se realizeze un subprogram care şterge elementele egale cu
zero dintr-o listă circulară.
15. Să se creeze o listă de liste, să se parcurgă şi să se şteargă.
16. Să se scrie un program care citeşte cuvintele dintr-un text şi
afişează numărul de apariţii al fiecărui cuvânt din textul respectiv.
17. Să se scrie un program care citeşte cuvintele dintr-un text şi scrie
numărul de apariţie al fiecărui cuvânt, în ordinea alfabetică a
cuvintelor respective.
18. Într-o gară se consideră un tren de marfă ale cărui vagoane sunt
inventariate într-o listă. Lista conţine, pentru fiecare vagon,
următoarele date: codul vagonului, codul conţinutului vagonului,
adresa expeditorului, adresa destinatarului. Deoarece în gară se
inversează poziţia vagoanelor, se cere listarea datelor despre
vagoanele respective în noua lor ordine.
Indicaţie: se creează o stivă în care se păstrează datele fiecărui vagon.
Datele corespunzătoare unui vagon constituie un element al stivei,
adică un nod al listei simplu înlănţuite. După ce datele au fost puse în
stivă, ele se scot de acolo şi se listează.

19. La o agenţie CEC există un singur ghişeu care se deschide la ora 8


şi se închide la ora 16, dar publicul aflat la coadă este deservit în
continuare. Deoarece la ora închiderii coada este destul de mare se
ridică problema oportunităţii deschiderii a încă unui ghişeu la
agenţia respectivă. Se cunosc operaţiile efectuate la agenţie şi
timpul lor de execuţie. În acest scop se realizează o simulare pe
calculator a situaţiei existente care să stabilească o medie a orelor
suplimentare efectuate zilnic pe o perioadă de un an.
Indicaţie: Programul de simulare a problemei indicate construieşte o
coadă de aşteptare cu persoanele care sosesc la agenţie în intervalul de
timp indicat. Se va folosi funcţia random pentru a determina operaţia
solicitată şi apoi pentru a determina intervalul de timp între două
persoane care vin la agenţie.

61
20. Să se realizeze un program care implementează subprograme
pentru crearea şi exploatarea unei cozi duble.
21. Se dă o matrice rară memorată într-o listă. Se cere:
a) să se afişeze toate elementele diferite de zero de pe diagonala
principală, secundară;
b) să se afişeze pe ecran matricea sub forma obişnuită, fără
reconstituirea ei în memorie.

2.6. Grafuri
Noţiuni introductive
Un graf este o pereche G = <V, M>, unde V este o mulţime de vârfuri,
iar M = VxV este o mulţime de muchii. O muchie de la vârful a la
vârful b este notată cu perechea ordonată (a, b), dacă graful este
orientat, şi cu mulţimea {a, b}, dacă graful este neorientat. Două
vârfuri unite printr-o muchie se numesc adiacente. Un drum este o
succesiune de muchii de forma (a1, a2), (a2, a3), ..., (an-1, an) sau de
forma {a1, a2}, {a2, a3}, ..., {an-1, an} după cum graful este orientat sau
neorientat. Lungimea drumului este egală cu numărul muchiilor care îl
constituie. Un drum simplu este un drum în care nici un vârf nu se
repetă. Un ciclu este un drum care este simplu, cu excepţia primului şi
ultimului vârf, care coincid. Un graf aciclic este un graf fără cicluri.
Un subgraf al lui G este un graf <V', M'>, unde V' ⊆ V, iar M' este
formată din muchiile din M care unesc vârfuri din V'. Un graf parţial
este un graf <V, M">, unde M" ⊆ M.
Un graf neorientat este conex, dacă între oricare două vârfuri
există un drum. Pentru grafuri orientate, această noţiune este întărită:
un graf orientat este tare conex, dacă între oricare două vârfuri i si j
există un drum de la i la j şi un drum de la j la i.
În cazul unui graf neconex, se pune problema determinarii
componentelor sale conexe. O componenta conexă este un subgraf
conex maximal, adică un subgraf conex în care nici un vârf din
subgraf nu este unit cu unul din afară printr-o muchie a grafului iniţial.
Împărţirea unui graf G = <V, M> în componentele sale conexe
determina o partiţie a lui V şi una a lui M.
Un arbore este un graf neorientat aciclic conex. Sau, echivalent,
un arbore este un graf neorientat în care există exact un drum între
oricare două vârfuri. Un graf parţial care este arbore se numeşte
arbore partial. Vârfurilor unui graf li se pot ataşa informaţii numite

62
uneori valori, iar muchiilor li se pot ataşa informaţii numite uneori
lungimi sau costuri.

Metode de reprezentare a grafurilor


a) Matricea de adiacenţă
Cea mai cunoscută metodă de memorare a grafurilor neorientate este
matricea de adiacenţă, definită în felul următor: 1, dacă există muchie
(arc) de la vârful i la vârful j şi 0, în caz contrar. În cazul grafurilor
neorientate, această matrice este simetrică, folosindu-se efectiv numai
jumătate din spaţiul matricei. În unele probleme este eficient ca
cealaltă jumătate a matricei să fie folosită pentru reţinerea altor
informaţii. Matricea de adiacenţă este folosită în general pentru grafuri
cu un număr mic de noduri, deoarece dimensiunea ei este limitată de
dimensiunea stivei.

Exemplu de graf orientat:

8 0 1 2 3 4 5 6 7 8
0 0 0 1 0 0 1 0 0 0
7 1 0 0 0 0 0 0 1 0 0
2 0 0 0 0 0 0 1 0 0
5 3 3 0 0 0 0 1 0 0 0 0
4 0 0 0 0 0 1 0 0 0
2 0 4 5 0 0 0 1 0 0 0 1 0
6 0 0 0 0 0 0 0 0 0
6 1 7 0 0 1 0 0 0 0 0 1
8 0 0 0 0 0 0 0 0 0

b) Listele de muchii
Această metodă de memorare presupune reţinerea unei matrice cu 2
linii şi m coloane, pe fiecare coloană fiind reţinute două extremităţi ale
unei muchii. Aceasta reprezentare este eficientă atunci când avem de
examinat toate muchiile grafului.
63
Listele de muchii sunt folosite în cazul algoritmilor care prelucrează
secvenţial muchiile, cum ar fi de exemplu algoritmul Kruskal de aflare
a arborelui parţial de cost minim în cazul grafurilor rare (cu număr
mic de muchii).

c) Listele de vecini.
Prin liste de vecini (adiacenţă) se realizează ataşarea la fiecare vârf i a
listei de vârfuri adiacente lui (pentru grafuri orientate, este necesar ca
muchia să plece din i). Într-un graf cu m muchii, suma lungimilor
listelor de adiacenţă este 2m, dacă graful este neorientat, respectiv m,
dacă graful este orientat. Dacă numărul muchiilor în graf este mic,
această reprezentare este preferabilă din punct de vedere al memoriei
necesare. Este posibil să examinăm toţi vecinii unui vârf dat, în medie,
în mai puţin de n operaţii. Pe de altă parte, pentru a determina dacă
două vârfuri i şi j sunt adiacente, trebuie să analizăm lista de adiacenţă
a lui i (şi, posibil, lista de adiacenţă a lui j), ceea ce este mai puţin
eficient decât consultarea unei valori logice în matricea de adiacenţă.
Listele de vecini sunt cele mai recomandate în tratarea algoritmilor din
teoria grafurilor din două motive principale:
1. spatiul folosit este gestionat dinamic, putându-se memora astfel
grafuri de dimensiuni mari;
2. complexitatea optimă pentru majoritatea algoritmilor fundamen-
tali din teoria grafurilor (parcurgeri, conexitate, muchii şi puncte
critice, algoritmi de drum minim etc.) este obţinută numai folosind
liste de vecini.
Există două modalităţi de implementare a listelor de vecini.
a) Prima dintre ele foloseşte o matrice T cu 2 linii şi 2m coloane şi
un vector C cu n elemente care reţine pentru fiecare nod indicele
coloanei din T pe care este memorat primul element al listei
nodului respectiv (sau 0 dacă vârful respectiv nu are vecini). Apoi,
pentru o anumită coloană i din T, T(i,1) reţine un element din lista
curentă, iar T(i,2) reţine coloana pe care se găseşte următorul
element din lista respectivă sau 0 dacă lista s-a terminat.
b) Cea de-a doua implementare foloseşte pentru fiecare nod o listă
simplu înlănţuită memorată în heap. Pentru fiecare listă este
suficient să păstrăm o singură santinelă (cea de început a listei),
introducerea vârfurilor făcându-se mereu la începutul listei
(deoarece ordinea vârfurilor în listă nu contează).

64
Exemplu:

8
0 2 5
1 6
7 2 6
3 4
5 3 4 5
5 3 7
2 0 4 6
7 2 8
8
6 1

Parcurgerea grafurilor
Parcurgerea unui graf presupune examinarea în vederea prelucrării
tuturor vârfurilor acelui graf într-o anumită ordine, ordine care să
permită prelucrarea optimă a informaţiilor ataşate grafului.
a) Parcurgerea DF (parcurgerea în adâncime)
Parcurgerea DF (Depth First) presupune ca dintr-un anumit nod v,
parcurgerea să fie continuată cu primul vecin al nodului v nevizitat
încă. Parcurgerea în adâncime a fost formulată cu mult timp în urmă
ca o tehnică de explorare a unui labirint. O persoană care caută ceva
într-un labirint şi aplică această tehnică are avantajul ca “următorul loc
în care caută” este mereu foarte aproape.
Parcurgerea vârfurilor în adâncime se
1 2 face în ordinea: 1 3 4 5 2 6
3
Cel mai cunoscut mod de imple-
mentare a parcurgerii DF se realizează
cu ajutorul unei funcţii recursive, dar
există şi cazuri în care este recomandată
4
o implementare nerecursivă.
6
5 Implementarea acestei metode se
face folosind o stivă. Aceasta este iniţia-
65
lizată cu un nod oarecare al grafului. La fiecare pas, se ia primul vecin
nevizitat al nodului din vârful stivei şi se adaugă în stivă dacă nu mai
există se coboară în stivă.
Algoritmul recursiv:
procedure DF(G)
for fiecare v ∈ V do marca[v] ← nevizitat
for fiecare v ∈ V do
if marca[v] = nevizitat then ad(v)

procedure ad(v)
{varful v nu a fost vizitat}
marca[v] ← vizitat
for fiecare virf w adiacent lui v do
if marca[w] = nevizitat then ad(w)
Algoritmul iterativ:
procedure iterad(v)
S ← stiva vida
marca[v] ← vizitat
push(v, S)
while S nu este vida do
while exista un varf w adiacent lui ftop(S)
astfel incat marca[w] = nevizitat do
marca[w] ← vizitat
push(w, S)
pop(S)

unde funcţia ftop returnează ultimul vârf inserat în stivă.


Parcurgerea în adâncime a unui graf nu este unică; ea depinde
atât de alegerea vârfului iniţial, cât şi de ordinea de vizitare a
vârfurilor adiacente.
Problemă: Cât timp este necesar pentru a parcurge un graf cu n
vărfuri şi m muchii?
Rezolvare:
Deoarece fiecare vârf este vizitat exact o dată, avem n apeluri ale
procedurii ad. În procedura ad, când vizităm un vârf, testăm marcajul
fiecărui vecin al său. Dacă reprezentăm graful prin liste de adiacenţă,
adică prin ataşarea la fiecare vârf a listei de vârfuri adiacente lui,
atunci numărul total al acestor testări este m, dacă graful este orientat,
66
şi 2m, dacă graful este neorientat. Algoritmul necesită un timp O(n)
pentru apelurile procedurii ad şi un timp O(m) pentru inspectarea
mărcilor. Timpul de executare este O(max(m, n)) = O(m+n).
Dacă reprezentăm graful printr-o matrice de adiacenţă, se obţine
un timp de executare de O(n2).

b) Parcurgerea BF (parcurgerea în lăţime)


Parcurgerea BF (Breath First) presupune faptul că după vizitarea unui
anumit nod v, sunt parcurşi toţi vecinii nevizitaţi ai acestuia, apoi toţi
vecinii nevizitaţi ai acestora din urmă, până la vizitarea tuturor nodu-
rilor grafului.
Parcurgerea în lăţime este folosită de obicei atunci când se
explorează parţial anumite grafuri infinite, sau când se caută cel mai
scurt drum dintre două vârfuri.
Implementarea acestei metode se face folosind o coadă. Aceasta
este iniţializată cu un nod oarecare al grafului. La fiecare pas, se
vizitează nodul aflat în vârful cozii şi se adaugă în coadă toţi vecinii
nevizitaţi ai nodului respectiv.

Parcurgere_BF(nod start)
Coada ← start
Vizitat(start) ←adevărat
cât timp coada nu este vidă
ic ←nodul de la începutul cozii
adaugă toti vecinii nevizitati ai lui ic în coadă
Vizitat(i) ← adevărat, unde i este un vecin nevizitat al
nodului ic

Pentru a efectua o parcurgere în laţime a unui graf (orientat sau


neorientat), aplicăm următorul principiu: atunci când ajungem într-un
vârf oarecare v nevizitat, îl marcăm şi vizităm apoi toate vârfurile
nevizitate adiacente lui v, apoi toate vârfurile nevizitate adiacente
vârfurilor adiacente lui v etc. Spre deosebire de parcurgerea în
adâncime, parcurgerea în lăţime nu este în mod natural recursivă.

procedure lat(v)
C ← coada vida
marca[v] ← vizitat
insert-queue(v, C)

67
while C nu este vida do
u ← delete-queue(C)
for fiecare virf w adiacent lui u do
if marca[w] = nevizitat then marca[w] ← vizitat
insert-queue(w, C)
Pentru compararea celor două metode apelăm procedurile iterad şi lat
din procedura parcurgere(G)

procedure parcurge(G)
for fiecare v ← V do marca[v] ← nevizitat
for fiecare v ← V do
if marca[v] = nevizitat then {iterad sau lat} (v)

Aplicaţia 2.6.1: Algoritmul lui Dijkstra


Fie G = (X,U) un graf orientat, unde X este mulţimea vârfurilor şi U
este mulţimea muchiilor. Fiecare muchie are o lungime nenegativă.
Unul din vârfuri este desemnat ca vârf sursă. Problema este să
determinăm lungimea celui mai scurt drum de la sursă către fiecare
vârf din graf. Vom folosi un algoritm greedy, datorat lui E.W. Dijkstra
(1959). Notăm cu C mulţimea vârfurilor disponibile (candidaţii) şi cu
S mulţimea vârfurilor deja selectate. În fiecare moment, S conţine
acele vârfuri a căror distanţă minimă de la sursă este deja cunoscută,
în timp ce mulţimea C conţine toate celelalte vârfuri. La început, S
conţine doar vârful sursă, iar în final S conţine toate vârfurile grafului.
La fiecare pas, adăugam în S acel vârf din C a cărui distanţă de la
sursă este cea mai mică.
La fiecare pas al algoritmului, un tablou D conţine lungimea
celui mai scurt drum special către fiecare vârf al grafului. După ce
adăugăm un nou vârf v la S, cel mai scurt drum special către v va fi,
de asemenea, cel mai scurt dintre toate drumurile către v. Când
algoritmul se termină, toate vârfurile din graf sunt în S, deci toate
drumurile de la sursă către celelalte vârfuri sunt speciale şi valorile din
D reprezintă soluţia problemei.
Matricea M dă lungimea fiecarei muchii, cu M[i, j] = +∞, dacă
muchia (i, j) nu există. Soluţia se va construi în tabloul D[2 .. n].
Algoritmul este:

S = {0}
for i = 1 to n D[i] = M[1][i]

68
for i = 1 to n-2
caut cel mai mic D[v] pentru fiecare v ∉ S
S = S ∪ {v}
for toate vârfurile u ∉ S
if (D[u] > D[v] + M[v][u]) then D[u] = D[v] + M[v][u]

Implementare în C:

#include <stdio.h>
#include <string.h>
#define FALSE 0
#define TRUE 1
#define MAXN 20
int setupDijkstra
(int *nod_p, int a[MAXN][MAXN], int *startnod_p);
void doDijkstra
(int nod, int a[MAXN][MAXN], int startnod);
void traceinfo
(int S[MAXN], int D[MAXN], int nod);
int main(){
int nod, startnod;
int a[MAXN][MAXN];
printf("Algoritmul lui Dijkstra\n");
if (setupDijkstra(&nod, a, &startnod)) return 1;
doDijkstra(nod, a, startnod);
return 0;
}
int setupDijkstra
(int *nod_p, int a[MAXN][MAXN], int *startnod_p){
int i, j;
do{
printf("Introduceti 0 pentru a specifica graful,sau 1 pentru exemple ");
scanf("%d", &i);
}while ((i < 0) || (i > 1));
if (i == 1){
*nod_p = 5;
for (i=0; i<*nod_p; i++)
for (j=0; j<*nod_p; j++) a[i][j]=99;
a[0][1]=a[1][0]=1; a[1][2]=a[2][1]=3; a[0][3]=a[3][0]=2;
a[3][4]=a[4][3]=4; a[1][4]=a[4][1]=2; a[2][4]=a[4][2]=1;

69
printf("A Matrix: a b c d e\n");
for (i=0; i<*nod_p; i++){
printf(" %c", 'a'+i);
for (j=0; j<*nod_p; j++)
printf("%3d", a[i][j]);
printf("\n");
}
*startnod_p = 0;
}
else {
printf("Enter number of nod (1-%d) ", MAXN);
if (scanf("%d", nod_p) != 1) return 1;
if ( (*nod_p < 1) || (*nod_p > MAXN) ) return 2;
for (i=0; i<*nod_p; i++){
printf("Introduceti randul %d matricii: ", i+1);
for (j=0; j<*nod_p; j++)
if (scanf("%d", &a[i][j]) != 1) return 3;
}
printf("introduceti nodul (1-%d) ", *nod_p);
if (scanf("%d", &i) != 1) return 4;
if ( (i < 1) || (i > *nod_p) ) return 5;
*startnod_p = i-1;
}
return 0;
}
void doDijkstra
(int nod, int a[MAXN][MAXN], int startnod){
int S[MAXN];
int D[MAXN]; int i, j, urmnod, mic;
/* initializare */
for (i=0; i<nod; i++) S[i] = FALSE;
S[startnod] = TRUE;
for (i=0; i<nod; i++) {D[i] = a[startnod][i]; }
printf("Initializare\n"); traceinfo(S, D, nod);
for (i=1; i<nod; i++){
urmnod = 999; mic = 999;
for (j=0; j<nod; j++){
if (!S[j]){
if (D[j] < mic){ mic = D[j];urmnod = j;}
}}

70
if (urmnod >= nod) return;
// printf(" D[%c]=%2d, ", 'a'+urmnod, mic);
printf("adauga nodul %c to S\n", 'a'+urmnod);
S[urmnod] = TRUE;
for (j=0; j<nod; j++){
if (!S[j]){
/* printf("Compara D[%c]=%2d si D[%c]+M[%c][%c]=%2d, ", \
'a'+j, D[j], 'a'+urmnod, 'a'+urmnod, 'a'+j, \
D[urmnod]+a[urmnod][j]);
*/
if (D[j] > D[urmnod] + a[urmnod][j]){
D[j] = D[urmnod] + a[urmnod][j];
/*printf("D[%c] schimba %2d\n", 'a'+j, D[j]); */
}
else
/* printf("nu schimba\n"); */
}
}
traceinfo(S, D, nod);
}
}
void traceinfo(int S[MAXN], int D[MAXN], int nod){
int i;
printf(" S = {");
for (i=0; i<nod; i++) if (S[i]) printf("%c", 'a' + i);
printf("}\n");
printf(" D = ");
for (i=0; i<nod; i++) printf("%3d", D[i]);
printf("\n");
}

Aplicaţia 2.6.2: Algoritmul lui Bellman-Ford (determinarea drumu-


rilor minime dintre un nod şi celelalte noduri într-un graf care poate
să aibă cicluri negative).
Dacă graful conţine muchii de cost negativ, algoritmul Dijkstra nu mai
funcţionează corect, deoarece nu putem găsi - acum - nodurile cele
mai apropiate de sursă, în ordine crescătoare a distanţei faţă de
aceasta. Această problemă este rezolvată de algoritmul Bellman-Ford,
care în plus determină şi existenţa ciclurilor de cost negativ care pot fi
atinse pornind din nodul sursă. Ca şi în cazul algoritmului Dijkstra,
71
vom folosi vectorul D care reţine distanţa minimă găsită la un moment
dat de la s la celelalte noduri. Algoritmul foloseste o coadă Q, care
este iniţializată cu nodul s. La fiecare pas, algoritmul scoate un nod v
din coadă şi găseşte toate nodurile w a căror distanţă de la sursă la
acestea poate fi optimizată prin folosirea nodului v. Dacă nodul w nu
se află deja în coadă, el este adăugat acesteia. Aceşti paşi se repetă
până când coada devine vidă.
Algoritmul utilizează tehnica de relaxare, procedând la
descreşterea estimării d[v] a drumului minim de la sursa s la fiecare
vârf v ∈V până când este obţinut costul adevărat δ(u,v) corespunzător
unui drum minim. Algoritmul returnează adevarat dacă şi numai dacă
nu conţine cicluri de cost negativ accesibile din sursă.
Algoritmul este:
BELLMAN-FORD(G,c,s)
1.INIT(G,s)
2. pentru i 1,|X[G]|-1 executa
3. pentru fiecare muchie (u,v) ∈U
4. RELAXEAZA(u,v,c)
5. pentru fiecare muchie (u,v) ∈U executa
6. daca d[v]>d[u]+c(u,v)
7. returneaza FALS
8. returneaza ADEVARAT

Implementare în C:
#include <stdio.h>
#include <string.h>
#define FALSE 0
#define TRUE 1
#define MAXN 26
#define INFINIT 999
int setupBellman(int *nod_p, int a[MAXN][MAXN], int *start_p);
int doBellman(int nod, int a[MAXN][MAXN], int start);
void traceinfo(int D[MAXN], int nod);
int main(){
int nod, start; int a[MAXN][MAXN];
printf("Algoritmul lui Bellman-Ford\n");
if (setupBellman(&nod, a, &start)) return 1;
if (doBellman(nod, a, start)){
printf(" Ciclu negativ\n"); return 2;}
return 0;}
72
int setupBellman
(int *nod_p, int a[MAXN][MAXN], int *start_p){
int i, j;
do{
printf("Introduceti 0 pentru graf, or 1 pentru exemple ");
scanf("%d", &i);
}while ((i < 0) || (i > 1));
if (i == 1){
*nod_p = 5;
for (i=0; i<*nod_p; i++) for (j=0; j<*nod_p; j++) a[i][j]=99;
a[0][1]=a[1][0]=1; a[1][2]=a[2][1]=3; a[0][3]=a[3][0]=2;
a[3][4]=a[4][3]=4; a[1][4]=a[4][1]=2; a[2][4]=a[4][2]=1;
printf("A Matrix: a b c d e\n");
for (i=0; i<*nod_p; i++){
printf(" %c", 'a'+i);
for (j=0; j<*nod_p; j++) printf("%3d", a[i][j]);
printf("\n");
}
*start_p = 0;
}
else{
printf("Introduceti nr. noduri (1-%d) ", MAXN);
if (scanf("%d", nod_p) != 1) return 1;
if ( (*nod_p < 1) || (*nod_p > MAXN) ) return 2;
for (i=0; i<*nod_p; i++){
printf("Introduceti randul %d al matricii: ", i+1);
for (j=0; j<*nod_p; j++)
if (scanf("%d", &a[i][j]) != 1) return 3;
}
printf("Introduceti nodul de start (1-%d) ", *nod_p);
if (scanf("%d", &i) != 1) return 4;
if ( (i < 1) || (i > *nod_p) ) return 5;
*start_p = i-1;
}
return 0;
}

int doBellman(int nod, int a[MAXN][MAXN], int start){


int D[MAXN]; int i, u, v;
/* initializare */

73
for (i=0; i<nod; i++){
if (i == start) D[i] = 0; else D[i] = INFINIT;
}
printf("Initializared\n"); traceinfo(D, nod);
for (i=1; i<nod; i++){
for (u=0; u<nod; u++){
for (v=0; v<nod; v++){
if (a[u][v] < 99){
/*
printf("Compara D[%c]=%3d si D[%c]+M[%c][%c]=%3d, ",\
'a'+v, D[v], 'a'+u, 'a'+u, 'a'+v, D[u]+a[u][v]);
*/
if (D[v] > D[u] + a[u][v]){
D[v] = D[u] + a[u][v];
printf("D[%c] schimba to %2d\n", 'a'+v, D[v]);
}
else printf("nu schimba\n");
}
}
}
traceinfo(D, nod);
}
for (u=0; u<nod; u++){
for (v=0; v<nod; v++){
if (a[u][v] < 99){
/*printf("Compara D[%c]=%3d and D[%c]+M[%c][%c]=%3d, ",\
'a'+v, D[v], 'a'+u, 'a'+u, 'a'+v, D[u]+a[u][v]);
*/
if (D[v] > D[u] + a[u][v]) return 1;
else printf("nu exista ciclu negativ\n");
}
}
}
return 0;
}
void traceinfo(int D[MAXN], int nod){
int i;
printf(" D = ");
for (i=0; i<nod; i++) printf("%4d", D[i]);
printf("\n");}

74
Probleme rezolvate

R2.6.1. Să se afişeze componentele conexe folosind parcurgerea DF a


grafului. Graful este reprezentat prin matricea de adiacenţă citită din
fişier TEXT.

Rezolvare: Implementarea Pascal folosind parcurgerea DF recursivă:


var a:array[1..100,1..100] of integer;
viz:array[1..100] of integer;
f:text;n,i,j:integer;
procedure df(X:integer);
var i:byte;
begin
write(x, ' ');
viz[x]:=1;
for i:=1 to n do if (a[x,i]=1) and (viz[i]=0) then df(i);
end;
procedure citire;
begin
assign(f,'matrice.txt');
reset(f);
readln(f,n);
for i:=1 to n do begin
for j:=1 to n do read(f,a[i,j]);
readln(f);
end;
end;
begin
citire;
for i:=1 to n do viz[i]:=0;
for i:=1 to n do if viz[i]=0 then begin df(i);writeln end;
end.

Exerciţiu:
Implementaţi în lumbajul C aplicaţia de mai sus folosind funcţia Df
(iterativă) de mai jos.

void df(int x){


int i,k,y,s[10],gasit;
k=1;

75
s[1]=x;viz[x]=1;printf("%d ",x);
while (k>=1) /*cat timp stiva este nevida */ {
y=s[k];gasit=0;
for(i=1;i<=n;i++)
if (viz[i]==0&&a[y][i]==1) {
printf("%d ",i); //vecin nevizitat
viz[i]=1;
s[++k]=i;
gasit=1;
break;
}
if (!gasit) k--;
}
}

R2.6.2. Să se parcurgă un graf reprezentat prin matricea sa de


adiacenţă folosind parcurgerea BF.
Rezolvare:
Implementare în limbajul Pascal:
Var a:array[1..100,1..100] of integer;
viz,c:array[-2..100] of integer;
f:text; n,i,j,v,p,u,x:integer;
procedure citire;
begin
assign(f,'matrice.txt');
reset(f);
readln(f,n);
for i:=1 to n do begin
for j:=1 to n do read(f,a[i,j]);
readln(f);
end;
write('Dati nodul de plecare ');
readln(v);
end;
procedure bf(v:integer);
begin
viz[v]:=1;write(v,' ');
c[1]:=v;
p:=1;
u:=1;
76
while p<=u do begin
x:=c[p]; p:=p+1;
for i:=1 to n do if (viz[i]=0) and (a[i,x]=1) then begin
write(i,' '); viz[i]:=1; u:=u+1; c[u]:=i;
end;
end;
end;
begin citire; for i:=1 to n do viz[i]:=0; bf(v) end.
Exerciţiu: Să se implementeze aplicaţia de mai sus în limbajul C.

R2.6.3. Să se determine componentele tari conexe ale unui graf


orientat.
Rezolvare:
Componenta tare conexă care va conţine vârful i va fi dată de sub-
graful indus de multimea nodurilor j pentru care a[i,j]=a[j,i]=1, unde a
este matricea drumurilor .
Implementare Pascal:
program tare_conexe;
const nmax=100;
type graf = array[1..nmax,1..nmax] of byte;
marcaj = array[1..nmax] of boolean;
var a:graf;
i,j,k,n:integer;
m:marcaj;
modificare:boolean;
begin
write('Dati nr de noduri: '); readln(n);
writeln('dati arcele grafului (i,j) :');
writeln('0 0 pentru sfarsit ');
readln(i,j);
while i*j<>0 do begin
a[i,j]:=1; readln(i,j);
end;
for k:=1 to n do for i:=1 to n do for j:=1 to n do
if a[i,j]=0 then a[i,j]:=a[i,k]*a[k,j];
k:=1;
for i:=1 to n do
if not m[i] then begin
77
write('componenta tare conexa ',k,':',i,' ');
for j:=1 to n do
if (j<>i) and (a[i,j]<>0)and (a[j,i]<>0) then
begin
write(j,' ');
m[j]:=true;
end;
inc(k);
writeln;
end;
end.
Implementare în limbajul C:
#include <stdio.h>
int n, a[10][10],viz[10];
void citire(){
int m,i,j,x,y;
printf("n=");scanf("%d",&n); printf("m=");scanf("%d",&m);
for(i=1;i<=n;i++) for(j=1;j<=n;j++)a[i][j]=0;
/*citirea arcelor si construirea matricei de adiacenta */
for(i=1;i<=m;i++) { scanf("%d %d",&x,&y);a[x][y]=1;}
}
void main(){
int k,i,j,m[10];
citire();
//se construieste matricea drumurilor
for (k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++)
if(a[i][j]==0) a[i][j]=a[i][k]&&a[k][j];
k=1; for (i=1;i<=n;i++)m[i]=0;
for(i=1;i<=n;i++)
if(!m[i]) {
/* daca varful i nu a fost introdus intr- componenta tare conexa */
printf("componenta tare conexa %d: %d ",k,i);
for (j=1;j<=n;j++)
if (j!=i&&a[i][j]==1&&a[j][i]==1){
printf("%d ",j); m[j]=1;
}
k++;
printf("\n");
}
}
78
R2.6.4. Dându-se două grafuri reprezentate prin matricea de adia-
cenţă să se precizeze dacă cel de-al doilea graf este graf parţial sau
subgraf al primului graf.
Rezolvare:
Implementarea Pascal este:
uses crt;
type mat=array[1..100,1..100] of integer;
var a,b:mat;
n,m,n1,m1,i,j:integer;
procedure creare;
var x,y,i,j:integer;
begin
writeln(' MARTICEA 1');
write('Dati numarul de varfuri');
readln(n);
write('Dati numarul de muchii');
readln(m);
for i:=1 to n do for j:=1 to n do a[i,j]:=0;
for i:=1 to m do begin
write('Dati primul capat al muchiei ',i,' : ');
readln(x);
write('Dati al doilea capat al muchiei ',i,' : ');
readln(y);
a[x,y]:=1; a[y,x]:=1
end
end;
procedure creare1;
var x,y,i,j:integer;
begin
WRITELN(' MATRICEA 2');
write('Dati numarul de varfuri');readln(n1);
write('Dati numarul de muchii');readln(m1);
for i:=1 to n1 do for j:=1 to n1 do b[i,j]:=0;
for i:=1 to m1 do begin
write('Dati primul capat al muchiei ',i,' : '); readln(x);
write('Dati al doilea capat al muchiei ',i,' : '); readln(y);
b[x,y]:=1;
b[y,x]:=1;
end;
end;
79
procedure subgraf;
var s:boolean;
begin
s:=true;if n1>n then s:=false;
for i:=1 to n1 do for j:=1 to n1 do
if (a[i,j]=1) and (b[i,j]=0) then s:=false;
if s=false then writeln(' B nu e subgraf al lui A.')
else writeln(' B e subgraf al lui A.');
end;
procedure graf_p;
var g:boolean;
begin
g:=true;if n<> n1 then g:=false;
for i:=1 to n do for j:=1 to n do
if (a[i,j]=0) and (b[i,j]=1) then g:=false;
if g=false then writeln(' B nu e graf partial al lui A.')
else writeln(' B e graf partial al lui A.');
end;
begin
clrscr;
creare;creare1;
graf_p;subgraf;
end.
Exerciţiu: Să se implementeze aplicaţia de mai sus în limbajul C.

R2.6.5. Determinarea drumurilor minime între oricare două noduri.


Rezolvare: Se foloseşte algoritmul lui ROY-FLOYD. Se porneşte de
la matricea costurilor C.
pentru k=1,n execută
pentru i=1,n execută
pentru j=1,n execută
c[i,j]=min(c[i,j],c[i,k]+c[k,j])
Simultan cu determinarea lungimilor minime ale drumurilor pot fi
reţinute drumurile folosind un tablou d, unde d[i,j] reprezintă mulţi-
mea predesesorilor lui j pentru drumul minim de la i la j.
Implementare Pascal:
Var c:array[1..100,1..100] of longint; v:array[1..100]of integer;
d:array[1..100,1..100] of set of 1..10;
n,m,i,j,k,nr:integer; x,y:integer;
80
procedure citire;
var cost,x,y:integer;
begin
write('nr. de noduri');readln(n);
write('nr. de muchii');readln(m);
for i:=1 to n do
for j:=1 to n do if i<>j then c[i,j]:=maxint else c[i,j]:=0;
for i:=1 to m do begin
write('Dati capetele arcului si costul ');
readln(x,y,cost); c[x,y]:=cost;
end;
end;
procedure initializare;
{determina multimea predecesorilor, initializeaza multimea D}
begin
for i:=1 to n do
for j:=1 to n do
if (i<>j) and (c[i,j]<maxint) then d[i,j]:=[i] else d[i,j]:=[];
end;
procedure drum(i,j:integer); {reconstituirea drumului}
var k:integer;
begin
if i<>j then
for k:=1 to n do if k in d[i,j] then begin
inc(nr);
v[nr]:=k;
drum(i,k);
dec(nr);
end
else begin
for k:=nr downto 1 do write(v[k], ' ');
writeln;
end;
end;
begin
citire;initializare;
for k:=1 to n do for i:=1 to n do for j:=1 to n do
if c[i,j]>c[i,k]+c[k,j] then
begin c[i,j]:=c[i,k]+c[k,j]; d[i,j]:=d[k,j] end
else if c[i,j]=c[i,k]+c[k,j] then d[i,j]:=d[i,j]+d[k,j];

81
for i:= 1 to n do begin
for j:=1 to n do write(c[i,j]:4);
writeln
end;
write('Dati doua noduri'); readln(x,y);
writeln('costul minim= ', c[x,y], ' drumurile minime=' );
for x:=1 to n do for y:=1 to n do
begin
writeln('drum de la ',x,'la ',y);
if c[x,y]=maxint then write('nu exista')
else begin
nr:=1;v[1]:=y;
drum(x,y)
end
end
end.
Exerciţiu: Să se implementeze algoritmul Roy-Floyd în limbajul C.

R2.6.6. Să se verifice dacă un graf reprezentat dinamic este conex sau


complet.
Rezolvare:
Se foloseşte un tablou de liste unde bl[i] reprezintă lista vecinilor lui i.
Implementare Pascal:
type lista=^nod;
nod= record inf:integer; urm:lista end;
var bl:array[1..100] of lista;
f:text;
i,n,x:integer;
complet:boolean;
procedure adaugare (var p:lista;x:integer);
var t,q:lista;
begin
new(q);q^.inf:=x;q^.urm:=nil;
if p=nil then p:=q
else begin
t:=p;while t^.urm<>nil do t:=t^.urm;
t^.urm:=q;
end;
end;
82
procedure listare(p:lista);
var t:lista;
begin
t:=p;
while t<> nil do begin
write(t^.inf,' ');
t:=t^.urm
end;
writeln;
end;
procedure df(i:integer);
var q,t,st:lista;
viz:array [1..100] of integer;
conex,gasit:boolean;
j:integer;
begin
for j:=1 to n do viz[j]:=0;viz[i]:=1;write(i,' ');new(q);q^.inf:=i;
q^.urm:=nil;st:=q;
while st<>nil do begin
x:=st^.inf; t:=bl[x]; gasit:=false;
while t<> nil do begin
if viz[t^.inf]=0 then begin
new(q); q^.inf:=t^.inf; q^.urm:=nil; viz[t^.inf]:=1;
write(t^.inf,' '); q^.urm:=st; gasit:=true; st:=q; break;
end;
t:=t^.urm;
end;
if not gasit then begin t:=st; st:=st^.urm; dispose(t) end;
end;
conex:=true;
for j:=1 to n do if viz[j]=0 then conex:=false;
if conex then write('conex') else write('nu este conex');
end;
function numarare( p:lista):integer;
var nr:integer; t:lista;
begin
nr:=0;t:=p;
while t<>nil do begin nr:=nr+1; t:=t^.urm end;
numarare:=nr
end;

83
begin
assign(f,'in.txt');reset(f);
readln(f,n);
for i:=1 to n do begin
while not eoln(f) do begin
read(f,x);
adaugare(bl[i],x)
end;
readln(f);
end;
writeln;
df(1);
complet:=true;
for i:=1 to n do if numarare(bl[i])<>n-1 then complet:=false;
if complet then write('complet') else write ('incomplet');
end.
Exerciţiu: Să se implementeze aplicaţia de mai sus în limbajul c.
R2.6.7. Fiind dat un graf, să se verifice dacă este aciclic.
Rezolvare:
Un graf este aciclic dacă este fără cicluri. Presupunem că fiecare nod
face parte dintr-o componentă conexă. Se ia fiecare muchie şi dacă
extremităţile sunt în aceeaşi componentă conexă atunci adăugând
aceea muchie se formează ciclu; dacă nu sunt în aceeaşi componentă
conexă toate nodurile care sunt în aceeaşi componentă cu extremitatea
a doua trec în componenta conexă a extremităţii 1.
Implementare Pascal:
Var f:text;
a:array[1..1000]of integer; n:integer; nume:string;
procedure ciclic;
var i,j,k:integer;
begin
writeln('Care este numele fisierului de intrare ?');
readln(nume);
assign(f,nume); reset(f);
readln(f,n);
for i:=1 to n do a[i]:=i;
while not seekeoln(f) do
begin
readln(f,i,j);
84
if a[i]=a[j] then
begin
writeln('Graful din fisierul ',nume,' contine cel putin un ciclu');
close(f); halt;
end;
for k:=1 to n do if a[k]=a[j] then a[k]:=a[i];
end;
writeln('Graful din fisierul ',nume,' nu contine cicluri');
close(f);
end;
begin
ciclic
end.
Implementare C:
#include <stdlib.h>
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
FILE *f;
int n,a[10];
void ciclic(){
int i,j,k;
fscanf(f,"%d",&n);
for(i=1;i<=n;i++)a[i]=i;
fscanf(f,"%d %d",&i,&j);
if (a[i]==a[j]) {
cout<<"contine ciclu";
fclose(f);
exit(1);
}
for (k=1; k<=n; k++){
if (a[k]==a[j]) a[k]=a[i];
}
cout<<"nu contine cicluri";
}
void main(void){
f=fopen("in.txt","r");clrscr();
ciclic();
}

85
R2.6.8. Se dă un graf şi un nod - considerat ca nodul nr. 1; se
cere să se determine toate nodurile accesibile din acest nod.
Rezolvare:
Implementare Pascal:
var
a: array[1..20,1..20] of integer; {matricea de adiacenta}
c: array[1..20] of integer;
{vectorul unde pastram nodurile accesibile}
n,i,j,k,p:integer; gasit:boolean;
begin
write('dati numarul de noduri; n=');readln(n);
writeln('dati matricea de adiacenta:');
for i:=1 to n do for j:=1 to n do read(a[i,j]);
c[1]:=1; k:=1; gasit:=true; i:=1;
while i<=k do begin
for j:=1 to n do
if a[i,j]=1 then begin
for p:=1 to k do
if c[p]=j then gasit:=false;
if gasit then begin k:=k+1; c[k]:=j end
end;
i:=i+1
end;
if k=n then writeln('Toate nodurile sunt accesibile.')
else for i:=1 to k do write(c[i],',');
end.
Exerciţiu:Să se implementeze aplicaţia de mai sus în limbajul C.

R2.6.9. Determinarea ciclului de lungime 3 dintr-un graf.


Rezolvare:
Implementarea Pascal:
var i,j,n,k:integer;
a:array[1..50,1..50] of integer;
f:text;
begin
assign(f,'c:\lucru\graf.txt');
reset(f);
read(f,n);
86
for i:=1 to n do for j:=1 to n do a[i,j]:=0;
while not eof(f) do begin
readln(f,i,j);
a[i,j]:=1; a[j,i]:=1
end;
for i:=1 to n-2 do for j:=i+1 to n-1 do
for k:=j+1 to n do
if (a[i,j]=1) and (a[j,k]=1) and (a[k,i]=1) then writeln(i,' ',j,' ',k);
end.
2.7. Arbori
Fie G un graf orientat. G este un arbore cu radacina r, dacă există în
G un vârf r din care oricare alt vârf poate fi ajuns printr-un drum unic.
Adâncimea unui vârf este lungimea drumului dintre rădăcina şi
acest vârf; înălţimea unui vârf este lungimea celui mai lung drum
dintre acest vârf şi un vârf terminal. Înălţimea arborelui este înalţimea
rădăcinii; nivelul unui vârf este înălţimea arborelui minus adâncimea
acestui vârf.
Reprezentarea unui arbore cu rădăcină se poate face prin adrese,
ca şi în cazul listelor înlănţuite. Fiecare vârf va fi memorat în trei
locaţii diferite, reprezentând informaţia propriu-zisă a vârfului (valoa-
rea vârfului), adresa celui mai vârstnic fiu şi adresa următorului frate.
Păstrând analogia cu listele înlănţuite, dacă se cunoaşte de la început
numărul maxim de vârfuri, atunci implementarea arborilor cu rădăcina
se poate face prin tablouri paralele.
Dacă fiecare vârf al unui arbore cu rădacină are până la n fii,
arborele respectiv este n-ar.
Într-un arbore binar, numărul maxim de vârfuri de adâncime k
este 2k. Un arbore binar de înălţime i are cel mult 2i+1-1 vârfuri, iar
dacă are exact 2i+1-1 varfuri, se numeste arbore plin. Vârfurile unui
arbore plin se numerotează în ordinea adâncimii.
Un arbore binar cu n vârfuri şi de înălţime i este complet, dacă
se obţine din arborele binar plin de înălţime i, prin eliminarea, dacă
este cazul, a vârfurilor numerotate cu n+1, n+2, …, 2i+1-1. Acest tip de
arbore se poate reprezenta secvenţial folosind un tablou T, punând
vârfurile de adâncime k, de la stânga la dreapta, în poziţiile T[2k],
T[2k+1], …, T[2k+1-1] (cu posibila excepţie a nivelului 0, care poate fi
incomplet). Un arbore binar este un arbore în care fiecare vârf are cel
mult doi descendenţi, făcându-se distincţie între descendentul stâng şi
descendentul drept al fiecărui vârf.
87
Reprezentarea arborilor binari
Reprezentarea prin paranteze: se începe cu rădăcina arborelui, iar
fiecare vârf care are descendenţi este urmat de expresiile ataşate
subarborilor care au ca rădăcină descendenţii vârfului, despărţite prin
virgulă şi cuprinse între paranteze. Dacă lipseşte un descendent sub-
expresia corespunzătoare este cuvântul vid. Pentru arborele de mai
sus, reprezentarea este: 6(2(1,(4(3,5)), 8(7, 9(,10)))
Reprezentarea standard: În care pentru fiecare vârf i este precizat des-
cendentul său stâng ST(i) descendentul său drept DR(i) = i informaţia
asociată v`rfului INF(i):
♦ static:ST şi DR sunt două tablouri ST=(0, 1, 0, 3, 0, 2, 0,7, 0, 0)
DR=(0, 4, 0, 5, 0, 8, 0, 9,10,0) rad=6
♦ dinamic:

type arbore=^nod; typedef struct nod{


nod=record int inf;
inf:integer; struct nod *st;
st,dr:arbore; struct nod *dr;
end; }arbore;
si se declara arbore *rad;

Reprezentarea cu doi vectori DESC şi TATA: În vectorul DESC,


având valori –1, 1,0 se precizează pentru fiecare nod ce fel de
descendent este el pentru părintele său iar în vectorul TATA se indică
pentru fiecare vârf, nodul părinte. Pentru exemplul de mai sus:
TATA=(2,6,4,2,4,0,8,6,8,9) DESC=(-1,-1,-1,1,1,0,-1,1,1,1)

88
Parcurgerea arborilor binari
♦ preordine:se vizitează rădăcina, se traversează subarborele stâng
în preordine, se traversează subarborele drept în preordine;
♦ inordine: se traversează subarborele stâng în inordine, se
vizitează rădăcina, se traversează subarborele drept în inordine;
♦ postordine: se traversează subarborele stâng în postordine, se
traversează subarborele drept în postordine, se traversează
rădăcina.

Aplicaţia 2.7.1. Să se creeze un arbore binar şi apoi să se parcurgă în


preordine, să se caute un nod în arbore, să se afişeze cheile de pe un
anumit nivel dat, să se afişeze drumurile de la rădăcină la frunze şi să
se calculeze adâncimea arborelui creat.
Rezolvare:
Implementare Pascal:
program arb_binar;
type arbore=^nod;
nod =record st,dr:arbore; inf:integer end;
var p:arbore;k,x,y,max,z:integer;a:array[1..100] of integer;
{Procedura creare arbore binar}
procedure creare(var p:arbore);
var x:integer;
begin
read(x);
if x=0 then p:=nil
else begin
new(p); p^.inf:=x;
write('Dati stanga lui ',x); creare(p^.st);
write('Dati dreapta lui ',x); creare(p^.dr);
end;
end;
{Procedura parcurgere arbore binar - prin preordine}
procedure preordine(p:arbore);
begin
if p<>nil then begin
write(p^.inf,' ');
preordine(p^.st); preordine(p^.dr);
end;
end;
89
{Cautarea unui nod din arbore}
function cautare(p:arbore; x:integer):boolean;
begin
if p=nil then cautare:=false
else if x=p^.inf then cautare:=true
else cautare:= cautare(p^.st,x) or cautare(p^.dr,x);
end;
{Afisarea tuturor cheilor de pe nivelul dat}
procedure nivel(p:arbore;k,x:integer);
begin
if p<>nil then begin
if k=x then write(p^.inf,' ');
nivel(p^.st,k+1,x);
nivel(p^.dr,k+1,x);
end;
end;
{Drumurile de la radacina la frunze}
procedure drum_frunze(p:arbore;k:integer);
var i:integer;
begin
if (p^.st=nil)and(p^.dr=nil) then begin
a[k]:=p^.inf;
for i:=1 to k do write(a[i],' ');
writeln
end
else if p<>nil then begin
a[k]:=p^.inf;
drum_frunze(p^.st,k+1);
drum_frunze(p^.dr,k+1);
end;
end;
{Afisarea numarului maxim de nivele}
procedure adancime(p:arbore;k:integer; var max:integer);
begin
if p<>nil then begin
if k>max then max:=k;
adancime(p^.st,k+1,max);
adancime(p^.dr,k+1,max);
end
end;

90
begin
creare(p);
preordine(p);
{Pentru procedura cautare}
read(x);
writeln(cautare(p,x));
{Pentru procedura nivel}
write('Nivelul= ');read(y);nivel(p,0,y);
{Pentru procedura drum_frunze}
drum_frunze(p,1);
{Pentru procedura adancime}
adancime(p,0,max);
writeln('Numarul de nivele este: ',max);
end.
Implementare în C++ :
#include <stdio.h>
#include <alloc.h>
#include <iostream.h>
#include <conio.h>
typedef struct nod{
int inf;
struct nod *st, *dr;
}arbore;
arbore *rad;
int a[10], max=0;
arbore *creare(){
int x;arbore *p;
cout<<"inf"; cin>>x;
if (x==0) return NULL;
else { (p)=(arbore*) malloc(sizeof(arbore)); (p)->inf=x;
cout<<"inf st. a lui"<<x; p->st=creare();
cout<<"inf dreapta a lui"<<x; p->dr=creare();
}
return p;
}
void preordine(arbore *p){
if (p){
printf("%d ",p->inf); preordine(p->st); preordine(p->dr);
}
}
91
void inordine(arbore *p){
if (p){
inordine(p->st); printf("%d",p->inf); inordine(p->dr);
}
}
void postordine(arbore *p){
if (p){
postordine(p->st); postordine(p->dr); printf("%d",p->inf);
}
}
int cautare(arbore *p, int x){
if(p){
if (p->inf==x) return 1;
return cautare(p->st,x)||cautare(p->dr,x);
}
else return 0;
}
void nivel(arbore*p, int k,int x){
if(p){
if (k==x) printf("%d ",p->inf);
nivel(p->st,k+1,x); nivel(p->dr,k+1,x);
}
}
void drum_frunze(arbore*p,int k){
int i;
if ((p->st)&&(p->dr)) {
a[k]=p->inf;
for (i=1;i<=k;i++) printf("%d ",a[i]);printf("\n");
}
else{
if(p) {
a[k]=p->inf;
drum_frunze(p->st,k+1); drum_frunze(p->dr,k+1);
}
}
}
void adancime(arbore *p, int k){
if (p) {
if (k>max) max=k; adancime(p->dr,k+1) ;adancime(p->st,k+1);
}}

92
void main(){
int x;
rad=creare();
preordine(rad);
printf("inf cautata:");scanf("%d", &x);
if (cautare(rad,x)) printf("exista");else printf("nu exista");
cout<<"nivelul";cin>>x;
cout<<"nodurile de pe nivelul "<<x<<":";nivel(rad,0,x);
cout<<"drumuri:";drum_frunze(rad,1);
adancime(rad,0);
printf("adancimea=%d",max);
}
Arbori binari de căutare
Arborele binar de căutare este un arbore binar în care pentru orice nod,
cheia din succesorul său stâng este mai mică decât cheia din nod, iar
cheia din succesorul său drept este mai mare decât cheia din nod. În
arborele binar de căutare informaţia din noduri este unică. Prin
parcurgerea în inordine a arborelui de căutare se obţine în ordine
crescătoare valorile din câmpurile cheie.
Crearea se realizează adăugând în arbore rând pe rând câte un
nod corespunzător fiecărei informaţii.
Adăugarea unu nod:
♦ dacă arborele nu are nici un nod se crează rădăcina;
♦ dacă arborele există se compară informaţia nodului nou cu cea din
nodul curent. Dacă este mai mare, se reia procesul pentru
subarborele din dreapta; dacă este mai mică, se reia pentru
subarborele din stânga, iar în caz de egalitate se afişează eroare.
Căutarea se realizează astfel: se caută în nodul curent; dacă
informaţia s-a găsit, algoritmul se încheie, astfel dacă informaţia este
mai mare decât informaţia din nodul curent, se caută în subarborele
drept al acestuia, altfel în subarborele stâng.
Aplicaţia 2.7.2. Program pentru crearea şi exploatarea unui arbore
binar de căutare.
Rezolvare:
Implementare Pascal:
type arbore=^nod;
nod = record inf:integer; st,dr:arbore end;
var p:arbore;x:integer;
93
procedure inserare(var p:arbore;x:integer);
var q:arbore;
begin
new(q);
q^.inf:=x;
q^.st:=nil;
q^.dr:=nil;
if p=nil then p:=q
else if p^.inf=x then write('Exista')
else if p^.inf<x then inserare(p^.dr,x)
else inserare(p^.st,x)
end;
procedure srd(p:arbore);
begin
if p<>nil then begin srd(p^.st); write(p^.inf,' '); srd(p^.dr) end
end;
procedure stergere(var p:arbore;x:integer);
var t:arbore;
begin
if p=nil then write('nu exista')
else if p^.inf<x then stergere(p^.dr,x)
else if p^.inf>x then stergere(p^.st,x)
else if p^.st=nil then begin
t:=p; p:=p^.dr; dispose(t)
end
else if p^.dr=nil then begin
t:=p;p:=p^.st;dispose(t);
end
else begin
t:=p^.st;
while t^.dr^.dr<>nil do t:=t^.dr;
p^.inf:=t^.dr^.inf; dispose(t^.dr);
t^.dr:=nil
end
end;
begin
read(x);
while x<>0 do begin inserare(p,x); read(x) end;
srd(p);read(x);stergere(p,x);srd(p);
end.

94
Implementare C++:
#include<iostream.h>
#include<conio.h>
#include<stdio.h>
typedef struct nod {int inf; nod *st,*dr;}arbore;
arbore *rad;
arbore *q;
void adaug(arbore* &p,int x){
if (p==NULL){
p=new nod;
p->inf=x;
p->st=p->dr=NULL;
}
else if (p->inf>x) adaug(p->st,x);
else if (p->inf<x) adaug(p->dr,x);
else cout <<"informatia exista";
}
void creare(arbore* &p){
int x,n,i;
cout <<"n=";cin>>n;
for (i=1;i<=n;i++){
cout<<"inf";cin>>x;
adaug(p,x);
}
}
void inordine(arbore *p){
if (p){
inordine(p->st); printf("%d",p->inf); inordine(p->dr);
}
}
arbore *cauta(arbore*p, int x){
if (p==NULL) return NULL;
else if (p->inf<x) {
q=p;p=p->st;cauta(p,x);
}
else if (p->inf>x){
q=p;p=p->st; cauta(p,x);
}
else return p;
}

95
void sterge(arbore *&r,int x){
arbore *t,*q1,*p;
int a;
t=cauta(r,x);
if(t==NULL){
cout<<"informatia nu se gaseste";
getch();
}
else if (t->dr==t->st){
if (t->inf<q->inf) q->st=NULL;
delete t;
}
else if (t->st==NULL&&t->st){
if (q->inf<t->inf) q->dr=t->st; else q->st=t->st;
delete t;
}
else if (t->st==NULL&&t->dr){
if(q->inf>t->inf) q->st=t->dr; else q->dr=t->dr;
delete t;
}
else {
p=t;
while (p->st!=NULL){q=p;p=p->st;}
}
q1=t;
t->inf=p->inf;
if (p->dr==p->st){
q->st=NULL;
delete p;
}
else {
q->st=p->dr;
delete p;
}
while (q1->st&&q1->inf<q1->st->inf){
a=q1->inf;
q1->inf=q1->st->inf; q1->st->inf=a;
q1=q1->st;
}
}

96
void main(){
int x;
arbore *rad=0;
creare(rad);
inordine(rad);
sterge(rad,2);
}

Probleme rezolvate în limbajul Pascal


R2.7.1. Să se scrie un subprogram pentru afişarea numărului cheilor
negative şi pozitive dintr-un arbore binar.
procedure p3(p:arbore);
begin
if p<>nil then begin
p3(p^.st);
if p^.inf<0 then nr1:=nr1+1;
if p^.inf>=0 then nr2:=nr2+1;
p3(p^.dr);
end;
end;
R2.7.2. Să se scrie un subprogram pentru afişarea cheilor impare
dintr-un arborele binar.
procedure p2(p:arbore);
begin
if p<>nil then begin
if p^.inf mod 2 =1 then write(p^.inf,' ');
p2(p^.st); p2(p^.dr)
end
end;
R2.7.3. Să se scrie un subprogram pentru aflarea produsului cheilor
pozitive dintr-un arborele binar.
function p4(p:arbore):longint;
begin
if p<>nil then if p^.inf >0 then p4:=p^.inf*p4(p^.st)*p4(p^.dr)
else p4:=p4(p^.st)*p4(p^.dr)
else p4:=1
end;
97
R2.7.4. Să se scrie un subprogram pentru aflarea numărului de
frunze dintr-un arborele binar .
function p5(p:arbore):integer;
begin
if p=nil then p5:=0 else if (p^.st=nil) and (p^.dr=nil) then p5:=1
else p5:=p5(p^.st)+p5(p^.dr)
end;
R2.7.5.. Să se scrie un subprogram pentru afişarea nodurilor care au
un succesor dintr-un arbore binar.
procedure p6(p:arbore);
begin
if p<>nil then begin
if ((p^.st=nil) and (p^.dr<>nil)) or ((p^.st<>nil) and (p^.dr=nil))
then write(p^.inf,' ');
p6(p^.st); p6(p^.dr)
end
end;
R2.7.6. Să se scrie un subprogram pentru aflarea numărului de
noduri de pe un nivel dat dintr-un arbore binar.
function p7(p:arbore;k:integer):integer;
begin
if p<>nil then if k=l then p7:=1+p7(p^.st,k+1)+p7(p^.dr,k+1)
else p7:=p7(p^.st,k+1)+p7(p^.dr,k+1)
else p7:=0
end;

Probleme propuse:

1. Se dă un graf orientat cu n noduri. Să se verifice dacă există un


nod având gradul interior n-1 şi gradul exterior 0.
2. Fie G = (X,U) un graf orientat şi fară circuite. Să se determine o
renumerotare a vârfurilor sale astfel încat dacă (u,v)∈U, atunci
numărul de ordine al lui u este mai mic decât numărul de ordine al
lui v (sortare topologică).
3. Fie G = (X,U) un graf orientat cu n vârfuri. Să se determine un alt
graf având aceleaşi vârfuri, aceeaşi matrice a drumurilor şi având
un număr minim de arce (se pot scoate, introduce noi arce).

98
4. La un turneu participă n jucători, fiecare jucător jucând pe rând
împotriva celorlalţi jucători. Ştiind că nu există jocuri egale, să se
construiască o listă care să cuprindă toţi jucătorii astfel încat doi
jucători i, j sunt alături dacă jucătorul i la învins pe jucătorul j.
5. La curtea regelui Artur s-au adunat 2n cavaleri şi fiecare din ei are
printre cei prezenţi cel mult n-1 duşmani. Să se arate că Merlin,
consilierul lui Artur, poate să-i aşeze în aşa fel pe cavaleri la o
masa rotundă încât nici unul dintre ei să nu stea alături de vreun
duşman.
6. Fie G=(X,U) un graf conex cu n noduri. Să se determine eficient
cel mai mic k astfel încât ştergând nodurile etichetate cu 1,2...k, în
această ordine să rezulte un graf ale cărui componente conexe au
toate cel mult n/2 noduri.
7. Să se determine, într-un graf conex, un ciclu care conţine două
noduri date, dar nu conţine un al treilea nod.
8. Numim transpusul unui graf G = (X,U), graful care are aceeaşi
mulţime de noduri, arcele sale fiind arcele grafului G, dar având
sens opus. Dându-se G prin matricea de adiacenţă sau prin liste de
vecini, să se determine în fiecare caz transpusul grafului dat.
9. Find date n persoane în care fiecare persoană se cunoaşte pe sine
şi eventual alte persoane. Să se formeze grupuri în care fiecare
persoană să cunoască toate celelalte persoane din grup (o persoană
aparţine unui singur grup). Relaţia “x cunoaşte pe y” nu este în
mod normal nici simetrică, nici tranzitivă.
Indicaţie: Se asociază problemei date un graf orientat cu n noduri şi se
construieşte matricea de adiacenţă (a[i,j]=1 daca i cunoaste pe j =i 0
dacă i nu cunoaşte pe j). Pentru fiecare persoană neataşată la un
moment dat unui grup se va construi grupul din care face parte
aceasta. Soluţia nu este unică.
10. Să se determine, într-un graf turneu, un drum elementar care trece
prin toate vârfurile. Un graf turneu este un graf orientat cu
proprietatea că între oricare două vârfuri distincte există un arc şi
numai unul.
Indicaţie: Se adaugă arcul format din nodurile 1 şi 2 într-o listă liniară.
Fiecare din vârfurile următoare se adaugă la drumul creat anterior fie
în faţă, fie la sfârşit, fie intercalat în listă.
11. Fie G=(X,U) un graf ponderat. Să se calculeze diametrul grafului.
Se numeşte diametrul unui graf, notat d(G), d(G)=max{l[i]/ i∈X},
unde l[i] este lungimea drumului maxim care are ca extremitate
iniţială vârful i.
99
12. Fie G un graf orientat în care fiecare arc are asociat un cost pozi-
tiv. Să se determine un circuit elementar care să treacă prin toate
vârfurile grafului care să aibă cost minim.
13. Se consideră un grup de n persoane. Fiecare persoana are cel puţin
n/2 prieteni şi cel mult k duşmani în grup. Una din persoane are o
carte pe care fiecare doreşte să o citească. Să se determine o mo-
dalitate prin care cartea să circule pe la fiecare persoană o singură
dată, transmiterea ei facându-se numai între doi prieteni, iar în
final cartea să ajungă din nou la proprietarul cărţii.
14. Pentru un graf dat în numerotare aciclică (orice arc u = (x,y) din U
satisface condiţia x<y), determinaţi distanţa minimă de la vârful 1 la
toate celelate vârfuri z pentru care există drumuri de la 1 la z, precum şi
câte un drum de lungime minimă. Sunt date arcele şi lungimile lor.
15. Pătratul unui graf orientat G se determină astfel: există arc de la x
la y în pătratul unui graf dacă există în G un drum de lungime 2 de
la x la y. Să se determine pătratul unui graf când graful este repre-
zentat fie prin liste de adiacenţă, fie prin matricea de adiacenţă.
16. Se dă un graf orientat aciclic şi se cere aranjarea vârfurilor sale
astfel încât orice arc are prima extremitate înaintea celei de-a doua
extremităţi în aranjare.
17. Se dă un graf orientat aciclic, în care numărul maxim de arce în
orice drum este k. Să se determine o partiţie a mulţimii vârfurilor
grafului în cel mult k submulţimi, astfel încât pentru orice două
noduri x,y din aceeaşi submulţime să nu existe drum de la x la y şi
nici de la y la x.
Indicaţie: Graful este aciclic, deci există noduri cu gradul 0 şi se
formează prima submulţime cu aceste noduri şi apoi se vor elimina
aceste noduri din graf împreuna cu arcele ce pleacă din ele obţinându-
se un graf orientat aciclic pentru care se reia procedeul.
18. Un graf orientat se numeşte semi-conex dacă pentru pentru orice
pereche de vârfuri diferite x şi y există drum de la x la y sau drum
de la y la x. Să se verifice dacă G este semi-conex.
19. Să se realizeze un program care să deseneze un graf (planar)şi apoi să
se marcheze succesiv pe desen drumul de lungime minimă între două
noduri date.
20. Se consideră o clasă de n ( n <= 40 ) cursanţi între care există relaţii de
simpatie, nu neaparat reciproce. Să se formeze grupuri de cursanţi între
care există relaţii de prietenie reciprocă. Un cursant nu poate să
aparţină mai multor grupuri.

100
3. Metode pentru rezolvarea problemelor
3.1. Metoda Divide et Impera
3.1.1. Prezentarea metodei
Metoda generală de programare cunoscută sub numele de divide et
impera (“dezbină şi stapâneşte”) constă în împărţirea repetată a unei
probleme în subprobleme de acelaşi tip, dar de dimensiune mai mică,
urmată de combinarea soluţiilor subproblemelor rezolvate pentru a
obţine soluţia problemei iniţiale. Fiecare subproblemă se rezolvă
direct dacă dimensiunea ei este suficient de mică încât să poată fi
rezolvată imediat cu un procedeu specific, altfel este împărţită în
subprobleme mai mici folosind acelaşi procedeu prin care a fost
descompusă problema iniţială. Procedeul se reia până când, în urma
descompunerilor repetate, se ajunge la probleme care admit rezolvare
imediată.
Algoritmul fiind de natură repetitivă şi deoarece subproblemele
au aceeaşi formă cu cea a problemei iniţiale, metoda “Divide et
impera” poate fi implementată elegant folosind o funcţie recursivă.
În continuare este dată funcţia generală care implementează
algoritmul.

function divimp(X: problema)


if (X este suficient de mica) then y ← rezolvă(X)
else{
descompune problema x în subproblemele X1, X2,…, Xk
for i ← 1 to k do
yi ← divimp(Xi)
/* combină y1, y2, …, yk pentru a obţine y soluţia problemei X */
y ← combină(y1, y2, …, yk)
return y
}
Uneori recursivitatea se poate înlocui cu un ciclu iterativ. Versiunea
iterativă poate fi mai rapidă şi foloseşte mai puţină memorie
comparativ cu varianta recursivă care utilizează o stivă pentru
memorarea apelurilor.
3.1.2. Probleme rezolvate
R3.1.1. [Cel mai mare divizor comun] Fie n numere naturale nenule
x1, x2,…,xn. Să se calculeze cmmdc pentru numere date.

101
Rezolvare:
program divizor_comun;
var x:array[1..25] of integer; n, i:integer;
function Divizor(a,b:integer):integer;
begin
while a<>b do if a>b then a:=a-b else b:=b-a;
Divizor := a
end;
function cmmdc(lo,hi:integer):integer;
var m:integer;
begin
if (hi-lo<=1) then cmmdc := Divizor(x[lo],x[hi])
else begin
m:=(lo+hi) div 2;
cmmdc:= Divizor(cmmdc(lo,m), cmmdc(m+1,hi));
end;
end;
begin
write('n = '); readln(n); writeln('Introduceti numerele');
for i:=1 to n do begin write('x[',i,'] = '); readln(x[i]) end;
writeln('cmmdc:= ', cmmdc(1,n));
end.
R3.1.2. [Căutare binară] Fie x1, x2,…,xn un şir de numere întregi
ordonate crescător şi x un număr întreg. Să se verifice dacă x se află
printre elementele şirului şi dacă da, să se afişeze poziţia acestuia.
Rezolvare:
program cautare;
uses crt;
var x:array[1..100] of integer; nr:integer;
function cauta(lo,hi:integer):integer;
var m:integer;
begin
if (lo<=hi) then begin m:=(lo+hi) div 2;
if nr=x[m] then cauta:=m
else if nr<x[m] then cauta:=cauta(lo,m-1)
else cauta:=cauta(m+1,hi)
end
else cauta:=0
end;

102
var n,i,pos:integer;
ch:char;
begin
write('n = ');readln(n);
writeln('Introduceti numerele');
for i:=1 to n do begin write('x[',i,'] = '); readln(x[i]) end;
repeat
writeln;
write('Numarul cautat = ');
readln(nr);
pos := cauta(1,n);
if pos <> 0 then
writeln(nr,' se afla in sir la pozitia ', pos)
else writeln(nr,' nu se afla in sir!');
write('Continuati (y/n) ?[y] ');
ch:=UpCase(readkey);
until ch = 'N';
end.
R3.1.3. [Sortare rapidă] Fie n ∈ N* şi numerele x1, x2,…,xn. Să se scrie
o procedură recursivă de sortare (quicksort) în ordine crescătoare a
numerelor date.
Rezolvare:
#include <stdio.h>
void QSort (int *table, int left,int right){
int leftp,rightp,aux;
unsigned type = 1;
leftp =left;
rightp=right;
do{
if ( table[leftp]>table[rightp] ){
type ^= 1;
aux = table[leftp];
table[leftp] = table[rightp];
table[rightp] = aux;
}
else type ? rightp--: leftp++;
}while (leftp < rightp);
if ( leftp-left > 1) QSort(table,left,leftp-1);
if ( right-rightp >1) QSort(table,rightp+1,right);
}
103
void main(){
int n,i;
int x[100];
printf(“n = ”); scanf(“%d”,&d);
for (i=0;i<n;i++) scanf(“%d”,&x[i]);
Qsort(x,0,n-1);
for (i=0;i<n;i++) printf(“%8d”,x[i]);
}
R3.1.4. [Problema turnurilor din Hanoi] Se dau 3 tije numerotate cu 1,
2, 3 şi n discuri perforate, cu diametre diferite. Iniţial toate discurile
se află pe tija 1 în ordinea descrescatoare a diametrelor lor, în sensul
de la bază la vârful tijei. Se pune problema de a muta toate cele n
discuri pe tija 2 (utilizând şi tija 3) şi respectând urmatoarele reguli:
• a fiecare pas se mută un singur disc;
• pe fiecare tijă deasupra unui disc pot apare numai discuri
cu diametru mai mic.
Rezolvare:
#include<stdio.h>
void Hanoi(int n, int a, int b){
if (n==1) printf("\%d - %d\n",a,b);
else {
Hanoi(n-1, a, 6-a-b);
printf("\%d - %d\n",a,b);
Hanoi(n-1, 6-a-b,b);
}
}
void main(){
int n;
printf("Numarul de discuri = "); scanf("%d",&n);
Hanoi(n,1,2);
}
R3.1.5. [Sortare prin interclasare] Se consideră un vector ne numere
intregi de lungime n. Descrieţi un algoritm de ordonare a numerelor
prin metoda de sortare prin interclasare.
Rezolvare:
#include <stdio.h>
#define MAX 100

104
void MSort(int tabel[],int temp[], int lo, int hi){
int mid, k, t_lo, t_hi;
if (lo >= hi) return;
mid = (lo+hi) / 2;
MSort(tabel,temp, lo, mid);
MSort(tabel,temp, mid+1, hi);
t_lo = lo;
t_hi = mid+1;
for (k = lo; k <= hi; k++)
if ( (t_hi > hi || tabel[t_lo] < tabel[t_hi]) && (t_lo <= mid) )
temp[k] = tabel[t_lo++];
else temp[k] = tabel[t_hi++];
for (k = lo; k <= hi; k++)
tabel[k] = temp[k];
}
void main(){
int n, i;
int tabel[MAX];
int temp[MAX];
printf("n = ");
scanf("%d",&n);
printf("Introduceti numerele:\n");
for(i=0;i<n;i++){
printf("tabel[%d] = ",i);
scanf("%d",&tabel[i]);
}
MSort(tabel, temp, 0, n-1);
for(i=0;i<n;i++) printf("%d ",tabel[i]);
}
3.1.3. Probleme propuse
1. Fie x1, x2,…,xn un şir de numere reale. Să se determine max{x1,
x2,…,xn}.
2. Se dă x1, x2,…,xn (cu n∈N) un şir de numere, descrieţi un program
“Divide et Impera” care să determine al k-lea cel mai mic element
din şir (k ∈N, k<n).
3. Se consideră un vector de x cu n componente numere intregi. Să
se sorteze componentele vectorului folosind metoda de sortare
prin interclasare.

105
4. Se dă o placă de tablă cu lungimea x şi lăţimea y având n găuri
date prin coordonatele lor întregi. Se cere să se decupeze o bucată
dreptunghiulară de arie maximă şi fără găuri ştiind că sunt permise
numai tăieturi orizontale şi verticale.
5. Se consideră un vector de lungime n. Se numeşte plierea
vectorului operaţia de suprapunere a unei jumătăţi (donatoare)
peste cealaltă jumătate (receptoare). Dacă n este impar elementul
din mijloc se elimină. Elementele rezultate după pliere vor avea
numeroatarea jumătăţii receptoare. Plierea se poate repeta până
când se obţine un singur element (final). Scrieţi un program care
să determine toate elementele finale posibile şi să afişeze
succesiunile de plieri corespunzătoare. Rezolvaţi aceiaşi problemă
ţinând cont că la pliere fiecare element receptor se dublează şi din
acesta se scade elementul donator corespunzător.
6. Rezolvaţi problema turnurilor din Hanoi folosint două tije de
manevră. Comparaţi numărul de mutări efectuate.
7. Fie P(x) un polinom de grag n cu coficienţi reali. Să se evalueze
polinomul în punctul x0 folosind metoda “Divide et Imera”.
8. Scrieţi o procedură “Divide et Impera” pentru inversarea unui şir
de caractere.
9. Se dă un dreptunghi prin dimensiunile sale numere naturale.
Dreptunghiul trebuie descompus în pătrate cu laturi numere
naturale, paralele cu laturile dreptunghiului iniţial. Se cere
numărul minim de pătrate în care se poate descompune
dreptunghiul.
10. Se dau un pătrat şi un cerc. Se cere să se calculeze aria lor comună
cu precizie de o zecimală. Coordonatele se citesc de la tastatură şi
sunt numere reale. Aria se va afişa pe ecran.
11. Un arbore cartezian al unui vector este un arbore binar definit
recursiv astfel: rădăcina arborelui este elementul cel mai mic din
vector; subarborele stâng este arborele cartezian al subvectorului
stâng (faţă de poziţia elementului din rădăcină); subarborele drept
este arborele cartezian al subvectorului drept. Se dă un vector de
dimensiune n. Să se afişeze arborele său cartezian.

106
3.2. Metoda programării dinamice
3.2.1. Principii fundamentale ale programării dinamice
Programarea dinamică, ca şi metoda divide et impera, rezolvă
problemele "combinând" soluţiile subproblemelor. Un algoritm bazat
pe programare dinamică rezolvă fiecare subproblemă o singură dată şi,
apoi, memorează soluţia într-un tablou, prin aceasta evitând
recalcularea soluţiei dacă subproblema mai apare din nou şi
subprobemele care apar în descompunere nu sunt independente.
Metoda programării dinamice se aplică problemelor de
optimizare. În problemele de optim metoda constă în determinarea
soluţiei pe baza unui şir de decizii d1, d2, d3....dn, unde di transformă
problema din starea si-1 în starea si.
Paşii pentru rezolvarea unei probleme folosind programarea
dinamică:
1. Caracterizarea structurii unei soluţii optime;
2. Definirea recursivă a valorii unei soluţii optime;
3. Calculul valorii unei soluţii optime
4. Construirea unei soluţii optime din informaţia calculată.
Principii fundamentale ale programării dinamice:
1. Dacă d1, d2, ... dn este un şir optim de decizii care duc un
sistem din starea iniţială în starea finală, atunci pentru orice i
(1≤i≤n) d1, d2, ... di este un şir optim de decizii.
2. Dacă d1, d2, ... dn este un şir optim de decizii care duc un
sistem din starea iniţială în starea finală, atunci pentru orice i
(1≤i≤n) di, di+1, ... dn este un şir optim de decizii.
3. Dacă d1, d2, ... , di, di+1 ... dn este un şir optim de decizii care
duc un sistem din starea iniţială în starea finală, atunci pentru
orice i (1≤i≤n) d1, d2,... , di şi di, di+1, ... dn sunt două şiruri
optime de decizii.

3.2.2. Probleme rezolvate


R3.2.1. [Subşir crescător maximal] Se consideră un şir de n numere
întregi. Să se determine cel mai lung subşir crescător din acesta.
Exemplu: n=8
7 1 8 2 11 4 12 3
Subşirul crescător maximal: 7 8 11 12 cu lungimea 4
Rezolvare: Se construieşte tabloul L reprezentând lungimea maximă a
unui subşir maximal care îl conţine ca prim element pe a[i] astfel
107
L[n]=1 şi L[i]=1+max{L[j]/ j>i şi a[j]>a[i]} pentru i de la n-1 la 1
(metoda înainte - deoarece soluţia unei subprobleme se află pe baza
subproblemelor din urmă).
Implementare Pascal:
var i,j,n,poz,max:integer;
l,a:array[1..30] of integer;
begin
write('n=');read(n);
for i:=1 to n do begin write('a[',i,']='); read(a[i]) end;
for i:=1 to n do l[i]:=0;
l[n]:=1;
for i:=n-1 downto 1 do
begin
max:=0;
for j:=1+i to n do
if (a[j]>a[i]) and (max<l[j]) then max:=l[j];
l[i]:=1+max
end;
max:=0;
for i:=1 to n do if max<l[i] then begin
max:=l[i];
poz:=i
end;
write(a[poz],' '); i:=poz;
while max>1 do begin
if (a[i]>a[poz]) and (l[i]=max-1) then begin
poz:=i;
write(a[poz],' ');
max:=max-1
end;
i:=i+1
end
end.
Implementare C:
#include<stdio.h>
void main(){
int n,i,j,poz,max,a[100],l[100];
printf("n=");scanf("%d",&n);
for (i=1;i<=n;i++) scanf("%d",&a[i]);
108
for(i=1;i<=n;i++) l[i]=0;
l[n]=1;
for (i=n-1;i>0;i--){
max=0;
for (j=i+1;j<=n;j++)
if ((max<l[j])&&(a[j]>a[i])) max=l[j];
max=max++;
l[i]=max;
}
max=0;
for(i=1;i<=n;i++) if (l[i]>max) {max=l[i];poz=i;}
printf(" %d ",a[poz]); i=poz;
while (max>1){
if ((a[i]>a[poz]) &&(l[i]==max-1)) {
poz=i;
printf("%d ",a[poz]);
max=max-1;
}
i++;
}
}
Exerciţiu: Să se rezolve această problemă folosind varianta “înapoi”
de programare dinamică (soluţia unei subprobleme se află pe baza
subproblemelor dinainte).
Indicaţie: Tabloul L se construieşte astfe L[1]=1 şi L[i]=1+max{L[j] /
j<i şi a[j]<a[i]} pentru i de la 2 la n.
R3.2.2. [Datorii] Domnul Tudor s-a împrumutat de n ori de la o bancă
cu diferite sume de bani. El trebuie să restituie banii băncii, dar după
prima restituire ştie că nu mai poate restitui toate sumele împrumutate
la rând ci doar acelea care nu au fost împrumutate în etape succesive.
Să se determine suma maximă pe care o poate recupera banca şi care
sunt împrumuturile restituite.
Rezolvare: Elementele tabloului L se calculează după formula
L[i]=a[i]+max{L[j] / j>i+1]}
Implementare Pascal:
var a,l:array[1..100]of integer;
max,n,i,j,poz:integer;
begin
read(n);
109
for i:=1 to n do read(a[i]);
for i:=n downto 1 do begin
max:=0;
for j:=i+2 to n do if (max<l[j]) then max:=l[j];
max:=max+a[i];
l[i]:=max
end;
max:=l[1];poz:=1;
for i:=1 to n do if l[i]>max then begin
max:=l[i];
poz:=i
end;
writeln('suma maxima este: ',l[1]);
max:=l[1];poz:=1;write('sirul: ');
repeat
write(a[poz],' '); i:=poz+2;
while (i<n ) and (l[i]<>max-a[poz]) do i:=i+1;
max:=max-a[poz];
poz:=i;
until max=0
end.
Implementare C:
#include<stdio.h>
void main(){
int n,i,j,poz,max,a[100],l[100];
printf("n=");scanf("%d",&n);
for (i=1;i<=n;i++) scanf("%d",&a[i]);
for (i=n;i>0;i--){
max=0;
for (j=i+2;j<=n;j++) if (max<l[j]) max=l[j];
max=max+a[i];
l[i]=max;
}
max=l[1];poz=1;
for(i=1;i<=n;i++) if (l[i]>max) {max=l[i];poz=i;}
printf("suma maxima %d\n",l[1]);
max=l[1];poz=1;
while (max>0){
printf("%d ",a[poz]);
i=poz+2;
110
while ((i<n) && (l[i]!=max-a[poz])) i++ ;
max=max-a[poz];
poz=i;
}
}
R3.2.3. [Subşiruri comune] Dându-se două şiruri comune să se deter-
mine cel mai lung subşir comun.
Rezolvare:
Implementarea Pascal (varianta mersului “înapoi”): Se construieşte
tabloul c care reţine lungimea maximă a celui mai lung subşir comun a
celor două şiruri până la i respectiv j.
var a,b,d:string;
i,j,n,m,max,t,v:integer;
c:array[1..50,1..50] of integer;
begin
write('primul sir:');readln(a);write('sirul doi:');readln(b);
n:=length(a);m:=length(b);
for i:=1 to m do if a[1]=b[i] then c[1,i]:=1 else c[1,i]:=0;
for i:=1 to n do if a[i]=b[1] then c[i,1]:=1 else c[i,1]:=0;
for i:=2 to n do for j:=2 to m do
if a[i]=b[j] then c[i,j]:=c[i-1,j-1]+1 else c[i,j]:=0;
max:=0;
for i:=1 to n do for j:=1 to m do
if max<c[i,j] then begin max:=c[i,j]; t:=i; v:=j end;
writeln('secventa maxima are lungimea :',max);
d:=a[t];
while max>1 do begin
dec(max); dec(t);
d:=a[t]+d
end;
writeln(d);
end.
Exerciţiu: Să se implementeze în C aplicaţia de mai sus.
R3.2.4. [Partiţii] Să se determine numărul de moduri în care se poate
descompune un număr natural în suma de numere.
Exemplu: n=4, numărul de moduri = 5
Rezolvare: Se construieşte tabloul a unde a[n][0] reprezintă numărul
de moduri în care se poate descompune:

111
a[n,n]=1;
a[n,k]=a[n-k,0] pentru n≤2k;
a[n,k]=Σa[n-k,i] pentru i de la 1 la k.
Implementare C:
#include <stdio.h>
void main(){
int n,i,k,j,p,a[100][100];
printf("n=");scanf("%d",&n);
a[1][0]=1;a[1][1]=1;
for( i=2;i<=n;i++){
a[i][0]=0;
for(k=1;k<=i;k++){
if(k==i) a[i][k]=1;
else if(i<2*k) a[i][k]=a[i-k][0];
else {
a[i][k]=a[i-k][k];
for(int l=1;l<=k-1;l++)
a[i][k]=a[i][k]+a[i-k][l];
}
a[i][0]=a[i][0]+a[i][k];
}
}
printf("nr de moduri=%d",a[n][0]);
}
Exerciţiu: Să se implementeze în Pascal aplicaţia de mai sus.
R3.2.5. [Drum] Dându-se un tablou bidimensional cu elemente
numere naturale. Să se determine un traseu de la poziţia 1,1 la poziţia
n,m, deplasarea făcându-se numai în direcţiile S şi E, astfel încât
suma elementelor de pe traseu să fie maximă.
Rezolvare: Implementarea Pascal este:
var i,j,n,m,max,p,q:integer;
a,l:array[1..10,1..10]of integer;
begin
read(m,n);
for i:=1 to n do for j:=1 to m do read(a[i,j]);
l[1,1]:=a[1,1];
for i:=2 to m do l[1,i]:=l[1,i-1]+a[1,i];
for i:=2 to n do l[i,1]:=l[i-1,1]+a[i,1];
for i:=2 to n do for j:=2 to m do

112
if l[i-1,j]>l[i,j-1] then l[i,j]:=l[i-1,j]+a[i,j]
else l[i,j]:=l[i,j-1]+a[i,j];
writeln(l[n,m]); max:=l[n,m];p:=n;q:=m; write(a[n,m],' ');
while (p<>1) and (q<>1) do begin
if l[p-1,q] = l[p,q]-a[p,q] then p:=p-1 else q:=q-1;
write(a[p,q],' ');
end;
write(a[1,1]);
end.
Exerciţiu: Să se implementeze aplicaţia de mai sus în C.
3.2.3. Probleme propuse
1. [Triunghi] Se consideră un triunghi de numere. Să se calculeze cea
mai mare dintre sumele numerelor ce apar pe drumurile ce pleacă din
vârf şi ajung la bază şi să se reconstituie drumul de sumă maximă. În
punctul (i,j) se poate ajunge numai din (i-1,j) sau (i-1,j-1).
Exemplu:
N=3
6
3 8
8 4 1
suma maximă este 18 şi drumul 6 8 4
Indicaţie: Se construieşte tabloul c astfel c[i,j]=max{a[i,j]+ c[i+1,j],
a[i,j]+ c[i+1,j+1]} (varianta mersului “înainte”). c[1,1] este suma
maximă cerută.
2. [Înmulţirea optimă a unui şir de matrice] Se dau n matrice.
Asociativitatea înmulţirii a două matrice ne oferă mai multe
posibilităţi de a calcula produsul şi care au un număr diferit de
operaţii. Să se determine o posibilitate de asociere a înmulţirii
matricelor astfel încât numărul de operaţii să fie minim.
Indicaţie: Se construieşte tabloul bidimensional L, unde l[i,j] repre-
zintă numărul minim de înmulţiri pentru a efectua produsul AiAi+1.....Aj.
3. [Triangularizarea optimă a poligoanelor] Se dă un poligon convex şi
o funcţie de pondere p definită pe triunghiurile formate de laturile şi
diagonalele poligonului. Să se găsească o triangulare care minimizea-
ză suma ponderilor triunghiurilor din triangularizare.
Indicaţie Există o corespondenţă între triangularizarea unui poligon şi
parantezarea unei expresii cum ar fi produsul unui şir de matrici.
4.[ Tipărirea uniformă] Textul de intrare este o succesiune de n cuvin-
te de lungimi l1, l2,...ln, lungimea fiind măsurată în caractere. Se doreş-
113
te tipărirea uniformă a paragrafului pe un anumit număr de linii, fieca-
re linie având cel mult m caractere astfel: dacă o linie conţine cuvinte-
le de la cuvântul i la cuvântul j, cu i ≤j iar între cuvinte se lasă exact
un spaţiu. Să se minimizeze suma cuburilor numărului de spaţii supli-
mentare de la sfârşitul liniilor, în raport cu toate liniile, mai puţin ultima.
5. [Problema patronului] Un patron a cumpărat un calculator şi doreşte
să înveţe să lucreze cu el. Pentru aceasta va umple un raft de cărţi din
colecţia “Informatica în lecţii de 9 minute şi 60 secunde”. Raftul are
lungimea L cm (L număr natural). Seria dispune de n titluri 1, 2, …,n
având grosimile g1, g2, ..., gn cm (numere naturale). Să se selecteze
titlurile pe care le poate cumpăra patronul, astfel încât raftul să fie
umplut complet (suma grosimilor cărţilor cumpărate să fie egală cu
lungimea raftului) şi numărul cărţilor achiziţionate să fie maxim.
Olimpiada Naţională de Informatică 1996
Indicaţie: Se construieşte tabloul bidimensional a unde a[i,j] reprezin-
tă numărul maxim de cărţi dintre primele i (în ordinea dată la intrare)
cu lungimea totală j sau, 0 dacă nu există nici o submulţime a mulţimii
primelor i cărţi cu lungimea totală j.
6. [Planificarea unei recepţii] Corporaţia AMC are o structură ierarhi-
că, relaţiile de subordonare formează un arbore în care rădăcina este
preşedintele. Fiecare angajat are un coeficient de convieţuire (număr
real). Preşedintele doreşte să organizeze o petrecere astfel încât nici un
angajat să nu se întâlnească cu seful său direct. Informaticianul Tom
trebuie să alcătuiască lista invitaţilor astfel încât suma coeficienţilor de
convieţuire a invitaţilor să se maximizeze şi preşedintele să participe
la propia petrecere.
7. [Mouse] Un experiment urmăreşte comportarea unui şoricel pus
într-o cutie dreptunghiulară, împărţită în mxn cămăruţe egale de formă
pătrată. Fiecare cămăruţă conţine o anumită cantitate de hrană. Şorice-
lul trebuie să pornească din colţul (1,1) al cutiei şi să ajungă în colţul
opus, mâncând cât mai multă hrană. El poate trece dintr-o cameră în
una alăturată (două camere sunt alăturate dacă au un perete comun),
mănâncă toată hrana din cămăruţă atunci când intră şi nu intră
niciodată într-o cameră fără hrană. Stabiliţi care este cantitatea maxi-
mă de hrană pe care o poate mânca şi traseul pe care îl poate urma
pentru a culege această cantitate maximă. Se dă n, m şi cantitatea de
hrană existentă în fiecare cămeruţă (numere între 1 şi 100).
Olimpiada judeţeană de informatică, 2002

114
Exemplu:
24
1263
3412
Ieşire:
7 21
1 1 --> 2 1 --> 2 2 --> 1 2 --> 1 3 --> 1 4 -->2 4
8. [Semne] Pentru n număr natural, să se găsească o combinaţie de semne
+ şi - (adică un vector x = (x(1), x(2), ..., x(k)), x(i) se află în <-1, 1>) şi
un număr k natural nenul astfel încât:
n = x(1)*12+x(2)*22+...+x(k)*k2.
Datele se citesc dintr-un fişier text ce conţine pe fiecare linie câte un
număr ce reprezintă valorile lui n pentru care se doresc reprezentări ca
mai sus. Datele de ieşire vor fi introduse într-un fişier text pe câte o
linie combinaţia de semne corespunzătoare.
Exemplu:
Intrare:
2
4
8
5
Ieşire:
---+
--+
--++--+
++--+
Indicaţie: Pentru k începând de la 1, se găsesc într-o listă numerele care
se pot obţine cu k semne; lista pentru k se obţine din cea pentru k-1.
Pentru unele numere k este foarte mare şi programul nu ar intra în
timp. O altă soluţie ar fi folosirea observaţiei:
(n+3)2-(n+2)2-(n+1)2+n2=(n+3+n+2)*1-(n+1+n)*1=2n+5-2n-1=4
Prin adăugarea secvenţei + - - + se ajunge de la un numar n la n+4.
Dacă am obţine secvenţele pentru n = 0, 1, 2 şi 3, am rezolva
problema. Acestea sunt:
0 secvenţa vidă
1+
2 - - - + (dată în exemplu)
3 - (pentru -1), apoi + - - +

115
9. [Şah] a) Dându-se două poziţii pe o tablă de şah de dimensiune
pătratică nxn, să se determine cel mai scurt drum dintre acestea
utilizând mutările calului.
b) Determinaţi numărul minim de cai care pot fi amplasaţi pe o tablă
de şah de dimensiune nxm astfel încât să nu se atace.
10. [Linii navale] Pe malul de nord şi cel de sud al unui fluviu există n
oraşe. Fiecare oraş are un unic oraş prieten pe celălalt mal; nu există
două oraşe pe un mal având acelaşi priten pe celălalt mal. Intenţia este
ca oraşele prietene să se lege printr-o linie navală. Se impune însă
restricţia ca să nu existe linii navale care să se intersecteze. Să se
determine numărul maxim de linii navale care pot fi înfiinţate, cu
respectarea condiţiei de mai sus. Pe prima linie a fişierului de intrare
apare numărul n de perechi de oraşe prietene, iar pe următoarele n linii
apar perechile de oraşe prietene (primul pe malul de nord, iar al doilea
pe malul de sud), fiecare oraş fiind specificat prin distanţa sa (cel mult
egală cu 6000) faţă de kilometrul 0 al fluviului.
Indicaţie: Se ordonează posibilele linii navale după primul argument
şi se determină cel mai lung subşir crescător format din coordonatele
celui de-al doilea argument. (vezi problema rezolvată 1).
11. [Drumuri minime] Se consideră un graf orientat, fiecare arc având
un cost ataşat. Se cere să se determine pentru fiecare pereche de
vârfuri ( i, j) şi să se tipărească lungimea drumului minim de la i la j.
12. [Submulţimi de sumă dată] Să se determine pentru un vector dat
dacă un număr k se poate scrie ca sumă de numere din vector, un
element din vector se poate folosi o dată.
Indicaţie: Se construieşte un tablou a cu valori logice, unde a[i] este
true dacă i se poate scrie ca sumă de elemente din vectorul dat şi false
în caz contrar.

3.3. Metoda Greedy


3.3.1. Prezentarea metodei
Algoritmii de tip Greedy sunt folosiţi la probleme de optimizare şi
sunt în general simpli şi eficienţi. Un astfel de algoritm determină
soluţia optimă în urma unei succesiuni de alegeri. La fiecare moment
decizional, din algoritm se va alege opţiunea care pare a fi cea mai
potrivită pe baza unui criteriu de optim local, în ideea că la final se va
obţine o soluţie optimă globală.

116
În general, datele de intrare pentru un algoritm Greedy se prezintă sub
forma unei mulţimi finite, iar soluţia este o submulţime sau o
permutare a datelor de intrare în aşa fel încât să fie îndeplinite anumite
condiţii de optim. Condiţiile de optim cer determinarea unei soluţii
care maximizează sau minimizează o funcţie dată, numită funcţie
obiectiv.
Dacă un element este inclus în soluţie el va rămâne în această
mulţime, nu se face nici o revenire asupra unei decizii luate.
Selectarea unui element la un moment dat poate depinde de
elementele alese până la acel moment dar nu depinde de cele care
urmează să fie alese ulterior.
Dacă un element a fost selectat, dar nu poate face parte din
soluţie atunci acest element nu va mai fi luat în calcul la alegerile
următoare. În acest fel se poate ajunge rapid la o soluţie, dar nu se
asigură (pentru toate problemele) optimalitatea globală. Există şi
probleme pentru care o astfel de metodă nu conduce la soluţie, deşi
aceasta există. Problemele care se pot rezolva folosind algoritmi de tip
Greedy trebuie să verifice două proprietăţi: proprietatea alegerii
Greedy şi substructura optimală.
Proprietatea alegerii Greedy spune că se poate ajunge la o
soluţie de optim global dacă la fiecare pas de decizie se selectează un
element care îndeplineşte criteriul de optim local. Proprietatea de
structură optimală spune că soluţia optimală a problemei conţine
soluţiile optimale ale subproblemelor.
O problemă care se va rezolva folosind o strategie Greedy este
dată de:
• O mulţime finită de “candidaţi” care reprezintă datele de
intrare;
• O funcţie care verifică dacă o anumită mulţime de elemente
constituie o soluţie posibilă, nu neaparat optimă, a
problemei;
• O funcţie care verifică dacă o submulţime a datelor de
intrare este o soluţie parţială a problemei;
• O funcţie de selecţie care alege la orice moment cel mai
potrivit candidat dintre elementele nefolosite încă;
• O funcţie obiectiv care trebuie minimizată sau maximizată.

Un algoritm Greedy nu furnizează decât o singură soluţie. În


contiunare este prezentată forma generală a metodei.

117
function greedy(C) {C este mulţimea datelor de intrare }
S←∅ {S este multimea care va conţine soluţia}
while not Soluţie(S) and C ≠ ∅ do
begin
x ← select(C)
C ← C \ {x}
if Posibil(S ∪ {x}) then S ← S ∪ {x}
end
if Solutie(S) then return S
else return write(‘nu exista soluţie’)
Această metodă îşi găseşte multe aplicaţii în teoria grafurilor la
determinarea unui arbore parţial de cost minim (Kruskal, Prim), la
determinarea drumurilor de cost minim de la un vârf la celelalte
vârfuri ale unui graf (Dijkstra), compresia datelor (arbori Huffman)
ş.a.
3.3.2. Probleme rezolvate
R3.3.1. [Problema continuă a rucsacului] Avem la dispoziţie un
rucsac cu care se poate transporta o greutate maximă G. Trebuie să
alegem din n obiecte pentru care se cunoaşte greutatea gi şi profitul pi
care se obţine prin transportul obiectului i cu rucsacul. Să se
determine obiectele care trebuiesc alese pentru transport astfel încât
profitul obţinut să fie maxim, iar greutatea totală a acestora să nu
depăşească G ştiind că putem lua orice parte dintr-un obiect.
Rezolvare: Programul în C este:
#include <stdio.h>
#define MAX 25
void main(){
int p[MAX]; int g[MAX];
int n,i,j,aux,nr[MAX];
float G, gr,profit,sol[MAX];
printf("Greutatea maxima G = ");scanf("%f",&G);
printf("Numarul de obiecte: ");
scanf("%d", &n);
for (i=0; i<n; i++){
printf("Obiectul nr. %d:\n",i+1);
printf("Greutatea = "); scanf("%d",&g[i]);
printf("Profitul = "); scanf("%d",&p[i]);
sol[i] = 0.0; nr[i] = i;
}
118
/*
ordonare descrescatoare dupa raportul p/g
*/
for (i=0;i<n-1;i++) for (j=i+1;j<n;j++)
if(p[i]/g[i] < p[j]/g[j]){
aux=p[i]; p[i]=p[j]; p[j]=aux;
aux=g[i]; g[i]=g[j]; g[j]=aux;
aux=nr[i]; nr[i]=nr[j]; nr[j]=aux;
}
/*alegere */
gr = G;
for (i=0; i<n && g[i]<gr; i++){
sol[i] = 1.0; //100%
gr -= g[i];
}
if (i < n) sol[i] = gr/g[i];
/*afisare zezultate */
profit = 0.0;
for (i=0;i<n;i++)
if(sol[i] > 0){
printf("Obiect %d - %5.2f%%\n", nr[i]+1, sol[i] *100);
profit += sol[i] * p[i];
}
printf("Profit = %5.2f", profit);
}
R3.3.2.[Planificarea activităţilor] Într-o sală, într-o zi trebuie plani-
ficate n activităţi, pentru fiecare activitate se cunoaşte intervalul în
care se desfăşoară [s, f). Se cere să se planifice un număr maxim de
activităţi astfel încât să nu se suprapună.
Rezolvare:Programul Pascal este:
program Activitati;
uses crt;
const max=100;
var n,i,j,aux:integer;
s,f:array[1..max] of integer;
sol:array[1..max] of 0..1;
nsol, finish:integer;
begin
{ citire date }

119
write('Nr de activitati = '); readln(n);
for i:=1 to n do
begin
writeln('Activitatea nr. ',i,':');
write('Start = '); readln(s[i]);
write('Finish = '); readln(f[i]);
sol[i]:=0;
end;
{ordonare după termenul final}
for i := 1 to n-1 do
for j := i + 1 to n do
if ( f[i] > f[j]) then
begin
aux:=s[i]; s[i]:=s[j]; s[j]:=aux;
aux:=f[i]; f[i]:=f[j]; f[j]:=aux;
end;
{calcul}
finish := 0;
for i := 1 to n do
if (s[i] >= finish ) then
begin
sol[i]:=1;
finish := f[i];
end;
{afisare rezultate}
writeln('Solutie:'); j := 1;
for i := 1 to n do
if sol[i]=1 then
begin
writeln('Activitatea nr. ',j,': ',
s[i], ' - ' ,f[i]);
j := j + 1;
end;
end.
R3.3.3. [Schimbarea banilor] Presupunem că a avem la dispoziţie k
tipuri de monede cu valorile c0, c1,… ck-1 unde c∈N*, să se scrie un
program care să determine o modalitate de schimb a unei sume S
folosind un număr minim de monede (numărul de monede din fiecare
tip fiind nelimitat).
Rezolvare: Programul C este:

120
#include<stdio.h>
#include<conio.h>
void main(){
int i,j,k,c,S; int sol[100]; int x,xmax;
/*citire date */
printf("k = "); scanf("%d",&k); printf("c = "); scanf("%d",&c);
printf("S = "); scanf("%d",&S);
/*calcul */
xmax=1; for(i=1; i<k; i++) xmax*=c;
x = xmax;
for(i=0; S>0; i++){ sol[i] = S / x; S %= x; x /= c; }
/*afisare */
x = xmax;
for(j=0; j<i; j++,x/=c)
if(sol[j]>0) printf("%d monede cu valoarea %d\n", sol[j], x);
getch();
}
R3.3.4. [Numere frumoase] Numerele frumoase sunt acele numere
care au în descompunerea lor numai factori primi de 2, 3 şi 5. Fiind
dat un număr natural n<1000, să se afişeze pe ecran primele n
numere frumoase.
Rezolvare: Programul Pascal este:
program nrFrumoase;
const max = 1000;
function min(a,b:integer):integer;
begin if a <= b then min:=a else min:=b end;
var
t,n2,n3,n5:integer;
f:array[1..max] of longint;
i,j,k,l,n:integer;
begin
write('n = '); readln(n);
n2:=2; i:=1; n3:=3; j:=1; n5:=5; k:=1; t:=1; l:=1; f[1]:=1;
while(l<=n) do
begin
t:=min(n2,min(n3,n5));
l:=l+1; f[l]:=t;
if t = n2 then begin inc(i); n2 := 2*f[i] end;
if t = n3 then begin inc(j); n3 := 3*f[j] end;

121
if t = n5 then begin inc(k); n5 := 5*f[k] end
end;
for i:=1 to n do write(f[i],' ');
readln;
end.
R3.3.5. [Copaci şi ciori] Pe n copaci aşezaţi în cerc se află n-1 ciori,
maxim una pe fiecare copac. Dându-se o configuraţie iniţială şi una
finală a ciorilor şi ştiind că la un moment dat o singură cioară poate
zbura de pe copacul ei pe cel liber, să se scrie un program care să de-
termine o secvenţă de zboruri pentru a ajunge la configuraţia finală.
Rezolvare: Programul C este:
#include <stdio.h>
#define MAX 10
int initial[MAX]; int final[MAX]; int n;
int CopacLiber(){
int i=0; while(initial[i]!=0) i++; return i; }
int Copac(int c){
int i=0; while(initial[i] != c) i++; return i; }
void Zboara(int s,int d){
printf("Cioara %d zboara de pe copacul %d pe %d !\n", \
initial[s], s+1,d+1);
initial[d] = initial[s]; initial[s] = 0;
}
void main(){ int i, k;
printf("n = "); scanf("%d",&n);
printf("Introduceti configuratia initiala (0 pt. liber):\n");
for(i=0;i<n;i++)
{printf("initial[%d] = ",i+1); scanf("%d",&initial[i]);}
printf("Introduceti configuratia finala (0 pt. liber):\n");
for(i=0;i<n;i++)
{printf("final[%d] = ",i+1);scanf("%d",&final[i]);}
for(i=0;i<n;i++)
if (final[i] && initial[i] != final[i]){
if(initial[i]) {
k = CopacLiber(); Zboara(i,k);
}
k = Copac(final[i]); Zboara(k,i);
}
}

122
R3.3.6. [Problema comis-voiajorului] Un comis-voiajor trebuie să
viziteze un număr de n oraşe şi să revină în oraşul de plecare.
Cunoscând legăturile dintre oricare două oraşe se cere să se afişeze
cel mai scurt traseu posibil pe care poate să-l parcurgă comis-
voiajorul ştiind că nu trebuie să treacă de două ori prin acelaşi oraş.
Rezolvare: Programul Pascal este:
program comis;
const max = 25;
var harta:array[1..max,1..max] of integer;
traseu:array[1..max] of integer;
v:array[1..max] of 0..1;
i,j,k,min,n,lungime,pos:integer;
begin
write('n = '); readln(n);
for i:=1 to n-1 do for j:=i+1 to n do
begin
write('distanta[',i,', ', j,'] = '); readln(harta[i,j]);
harta[j,i]:=harta[i,j];
end;
write('Orasul de pornire = '); readln(k);
lungime:=0; traseu[1]:=k; v[k]:=1;
for i:=2 to n do
begin
min:=maxint;
for j:=1 to n do
if v[j] = 0 then
if harta[k, j] < min then
begin
min := harta[k, j];
pos := j
end;
inc(lungime,min); traseu[i]:=pos; v[pos]:=1; k:=pos
end;
inc(lungime,harta[k,1]);
for i:=1 to n do write(traseu[i]:3);
writeln(traseu[1]:3);
writeln('lungimea = ', lungime);
readln;
end.

123
3.3.3. Probleme propuse
1. [Problema planificării optime a lucrărilor] Se presupune că avem de
executat un număr de n lucrări, cunoscându-se pentru fiecare lucrare i
termenul de finalizare ti şi penalizarea pi plătită în cazul în care
lucrarea nu este predată la termen. Ştiind că fiecare lucrare este
executată într-o unitate de timp şi că nu se pot executa mai multe
lucrări simultan, să se planifice ordinea de execuţie a lucrărilor astfel
încât penalizarea totală să fie minimă.
2. [Amplasarea canalelor] În jurul unui oraş avem N bazine cu apă ce
trebuiesc unite între ele prin canale. Fiecare bazin poate fi unit prin
câte un canal cu mai multe bazine. Perechile de bazine ce vor fi unite
sunt date într-o listă. Un canal poate trece doar prin interiorul sau
exteriorul cercului format de bazine. Se cere modul de amplasare al
fiecărui canal.
3. Se dau numerele întregi nenule a1, a2, …, an şi b1, b2, …, bm cu n ≥
m. Să se determine o submulţime {x1, x2, …, xm} a mulţimii {a1, a2,
…, an} care să maximizeze expresia E = b1x1 + b2x2 + … + bmxm.
4. Într-un depozit al monetăriei statului sosesc n saci cu monezi. Se
cunoaşte numărul de monezi din fiecare sac şi se doreşte mutarea
monezilor dintr-un sac în altul în aşa fel încât în fiecare sac să apară
acelaşi număr de monezi. Scrieţi un program care rezolvă problema
efectuând un număr minim de mutări.
5. Fie n şiruri ordonate crescător S1, S2, …, Sn cu lungimile L1, L2, …,
Ln. Să se interclaseze cele n şiruri obţinându-se un singur şir crescător
de lungime L1 + L2 + … + Ln cu un număr minim de comparaţii între
elementele şirurilor.
6. Fie X = {x1, x2, …,xn} o mulţime de numere reale. Să se detmine o
mulţime de cardinal minim de intervale de lungime 1 a căror reuniune
să includă mulţimea X.
7. Se cunosc poziţiile oraşelor unei ţări date prin coordonatele lor. Să
se determine configuraţia unei reţele telefonice astfel încât toate
oraşele să fie conectate la reţea şi costul reţelei să fie minim.
8. Se dau n şi k, naturale, k≤n. Să se construiască un tablou n×n care
îndeplineşte simultan condiţiile:
♦ conţine toate numerele de la 1 la n2 o singură dată;
♦ pe fiecare linie numerele sunt aşezate în ordine crescătoare, de la
stânga la dreapta;
♦ suma elementelor de pe coloana k să fie minimă.
9. Un instructor de schi trebuie să distribuie n perechi de schiuri la n
elevi începători. Descrieţi un algoritm care să facă distribuirea în aşa
124
fel încât suma diferenţelor absolute dintre înălţimea elevului şi
lungimea schiurilor să fie minimă.
10. Se dă un polinom P(X) cu coeficienţi întregi. Să se afle toate
rădăcinile raţionale ale polinomului.
11. Se dau n puncte în plan prin coordonatele lor numere întregi. Să se
construiască, dacă este posibil, un poligon convex care să aibă ca
vârfuri aceste puncte.
12. Fiind dată o hartă cu n ţări, descrieţi un algoritm euristic care să
determine o modalitate de colorare a hărţii astfel încât două ţări care
se învecinează să fie colorate diferit.
13. Descrieţi un algoritm euristic care să determine o modalitate prin
care un cal aflat în colţul stânga-sus să străbată întreaga tablă de şah
fără a trece de două ori prin aceeaşi poziţie.
14. Descrieţi un algoritm euristic care să schimbe o sumă S dată
folosind un număr minim de monete având valorile v1, v2, …, vn.
3.4. Metoda backtracking
3.4.1.Prezentarea metodei
Această tehnică se foloseşte la rezolvarea problemelor a căror soluţie
se poate reprezenta sub forma unui vector S = x1x2...xn cu x1 ∈ S1, x2
∈ S 2, …, xn ∈ S n, iar mulţimile S1, S2, …, Sn, care pot coincide, sunt
mulţimi finite ordonate liniar (pentru orice element, cu exceptia
ultimului este cunoscut succesorul). Pentru fiecare problemă concretă
sunt precizate relaţiile dintre componentele vectorului care memorează
soluţia, numite condiţii interne. Mulţimea S = S1 × … × S1 se numeşte
spaţiul soluţiilor, iar elementele produsului cartezian care satisfac şi
condiţiile interne se numesc soluţii rezultat. Metoda evită generarea
tuturor combinaţiilor completând pe rând componentele vectorului
soluţie cu valori ce îndeplinesc condiţiile de continuare, scurtându-se
astfel timpul de calcul.
Vectorul x este privit ca o stivă, lui xk nu i se atribuie o valoare
decât după ce x1, …, xk-1 au primit valori. La început xk este iniţializat
la o valoare (cu ajutorul unei funcţii init) al cărei succesor este primul
element din Sk. Cu ajutorul unei funcţii succesor xk ia valoarea
primului element din Sk. După ce xk a primit o valoare, nu se trece
direct la atribuirea unei valori lui xk+1 ci se verifică mai întâi anumite
condiţii de continuare referitoare la x1, …, xk cu ajutorul unei funcţii
valid de tip logic. Dacă aceste condiţii sunt satisfăcute se verifică dacă
s-a obţinut o soluţie caz în care se trece la afişarea acesteia.

125
Neîndeplinirea condiţiilor de continuare exprimă faptul că oricum am
alege xk+1, …, xn, nu vom ajunge la o soluţie. În acest caz se face o
nouă alegere pentru xk sau, dacă Sk a fost epuizat, se micşorează k cu o
unitate şi se încearca o nouă alegere pentru xk (k >0) din restul
elementelor lui Sk încă nealese folosind funcţia succesor. Ca urmare se
trece la atribuirea unei valori lui xk+1 doar dacă sunt îndeplinite
condiţiile de continuare.
Algoritmul se termină în momentul în care au fost luate în calcul
toate elementele mulţimii S1.
Metoda backtracking poate fi descrisă prin următoarea proce-
dură (în varianta iterativă):
k := 1; init(k, st);
while k > 0 do begin
repeat
succesor(as, st, k);
if as then valid(ev, st, k);
until (not as) or (as and ev);
if as if solutie(k) then tipar
else begin k := k + 1; init(k, st) end
else k := k – 1;
end;
Rezolvarea problemelor prin această metodă necesită un timp mare de
execuţie, motiv pentru care metoda este recomandată numai în
cazurile în care spaţiul soluţiilor este destul de redus, sau dacă nu se
cunoaşte un alt algoritm mai rapid.
Metoda admite şi o variantă recursivă în care stiva nu este
gestionată explicit de programator. În continuare este dată forma
generală a procedurii backtracking în variantă recursivă (care se va
apela cu Back(1)):
procedure Back(k);
var i:integer;
begin
if solutie(k) then tipar(x)
else begin
init(k,x);
while succesor(x,k) do
begin x[k]:=i; if valid(x,k) then Back(k+1) end;
end
end;

126
3.4.2. Probleme rezolvate
R3.4.1. [Permutări] Se dă un număr n ∈ N, se cere să se genereze
toate permutările mulţimii {1, 2, …,n}.
Rezolvare:
program permutari;
uses crt;
type stack = array[1..25] of integer;
var st: stack;
n:integer; k:integer;
as, ev: boolean;

procedure init(k:integer; var st: stack);


begin st[k]:=0 end;
procedure succesor(var as:boolean; var st: stack; k:integer);
begin
if st[k] < n then begin st[k] := st[k] + 1; as := true end
else as:=false;
end;
procedure valid(var ev:boolean; st: stack; k:integer);
var i:integer;
begin ev:=true; for i:=1 to k-1 do if st[k] = st[i] then ev:=false end;
function solutie(k:integer) :boolean;
begin solutie:=(k=n) end;
procedure tipar;
var i:integer;
begin for i:=1 to n do write(st[i],' '); writeln end;
begin
write ( ' n = ' ); readln(n) ;
k:=1; init(k,st);
while (k>0) do begin
repeat
succesor (as, st, k) ;
if as then valid(ev,st,k);
until (not as) or (as and ev) ;
if as then if solutie(k) then tipar
else begin k:=k+1; init(k,st) end
else k:=k-1
end;
end.

127
R3.4.2. [Descompunere ca sumă de numere prime] Să se descompună
un număr natural n în toate modurile posibile ca sumă de numere
prime.
Rezolvare:
program desc_prime;
var Prime, x :array[1..100] of integer;
n,k,i,p,S:integer;
function Prim(nr:integer):boolean;
var i:integer;
begin
Prim:=true; i:=2;
while i*i <= nr do
if nr mod i = 0 then
begin
Prim:=false; break;
end
else inc(i);
end;
procedure Tipar;
var i:integer;
begin for i:=1 to k do write(Prime[x[i]],' '); writeln end;

begin
write('n = '); readln(n);
p:=0;
for i:=2 to n do if Prim(i) then
begin inc(p); Prime[p]:=i end;
S:=0;
k:=1; x[k]:=0;
while k>0 do begin
inc(x[k]);
if (x[k] <= p) and (S + Prime[x[k]] <= n) then
if S + Prime[x[k]] = n then Tipar
else begin
inc(S,Prime[x[k]]); inc(k); x[k] := x[k-1]-1
end
else begin dec(k); Dec(S,Prime[x[k]]); end
end
end.

128
R3.4.3. [Problema damelor] Pe o tablă de şah trebuie aşezate opt
dame astfel încât să nu existe două dame pe aceeaşi linie, coloană sau
diagonală. Problema se generalizează imediat pentru n dame pe o
tablă cu n linii şi n coloane. Să se determine toate soluţiile.
Rezolvare :
#include <stdio.h>
#include <conio.h>
#include <math.h>
typedef int stack[25];
stack st;
int n;
void tipar(){
int j;
for(j = 0; j < n; j++) printf("%d ", st[j]);
printf("\n");
}
void dame(int k){
int i,j, valid;
if (k==n) tipar();
else for(i = st[k]+1; i <= n; i++) {
st[k] = i; valid = 1;
for(j = 0; j < k; j++)
if ((st[j]==st[k])||(abs(st[k]-st[j])==k-j)) valid = 0;
if (valid ==1) dame(k+1);
}
st[k] = 0;
}
void main(){
int i;
printf("n = "); scanf("%d",&n);
for(i = 0; i < n; i++) st[i]=0;
dame(0);
getch();
}
R3.4.4. [Problema colorării hărţilor] Fiind dată o hartă cu n ţări, se
cer toate soluţiile de colorare a hărţii utilizănd un număr minim de
culori, astfel încât două tări cu frontieră comună să fie colorate
diferit. Menţionăm că este demonstrat faptul că sunt suficiente 4
culori pentru a colora orice hartă.

129
Rezolvare:
program Culori;
uses crt;
type stack = array[1..25] of integer;
var st: stack;
i,j,k,n:integer;
as, ev: boolean;
a:array[1..25,1..25] of integer;
procedure init(k:integer;var st: stack);
begin st[k]:=0 end;
procedure succesor(var as:boolean; var st: stack; k:integer);
begin
if st[k] < 4 then begin st[k] := st[k] + 1; as := true end
else as:=false;
end;
procedure valid(var ev:boolean; st: stack; k:integer);
var i:integer;
begin
ev:=true;
for i:=1 to k-1 do if (st[k]=st[i]) and (a[i,k]=1) then ev:= false;
end;
function solutie(k:integer) :boolean;
begin solutie:=(k=n) end;
procedure tipar;
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 for j:=1 to i-1 do
begin write('a[',i,',',j,']= '); readln(a[i,j]); a[j,i]:=a[i,j] end;
k:=1; init(k,st);
while (k>0) do
begin repeat
succesor (as, st, k) ; if as then valid(ev,st,k);
until (not as) or (as and ev) ;
if as then if solutie(k) then tipar
else begin k:=k+1; init(k,st) end
else k:=k-1;
end;
end.

130
R3.4.5. [Săritura calului] Se consideră o tablă de şah n x n şi un cal
plasat în colţul din stânga sus. Se cere să se afişeze toate posibilităţile
de mutare a calului astfel încât să treacă o singură dată prin fiecare
pătrat al tablei.
Rezolvare:
#include <stdio.h>
#include <conio.h>
typedef int tabla[25][25];
int linT[8] = {-1,+1,+2,+2,+1,-1,-2,-2};
int colT[8] = {+2,+2,+1,-1,-2,-2,-1,+1};
tabla tab;
int n;
void tipar(){
int i,j;
for(i = 0; i < n; i++){ printf("\n");
for(j = 0; j < n; j++) printf("%3d", tab[i][j]);
}
printf("\n"); getch();
}
void cal(int lin, int col, int k){
int i,lin2,col2;
if (tab[lin][col]) return;
k++;
tab[lin][col]=k;
if (k==n*n) tipar();
else for (int i = 0; i < 8; i++){
lin2 = lin + linT[i];
col2 = col + colT[i];
if (lin2>=0 && lin2<n && col2>=0 && col2<n)
cal(lin2,col2,k);
}
tab[lin][col] = 0;
}
void main(){
int i,j;
printf("n = "); scanf("%d",&n);
for(i = 0; i < n; i++) for(j = 0; j < n; j++) tab[i][j]=0;
cal(0,0,0);
printf("\n%d solutii",t);
}
131
3.4.3. Probleme propuse
1. Se dau numerele naturale n şi k, să se genereze toate
aranjamentele (funcţiile injective) de elemente din mulţimea {1, 2,
…, n} luate câte k.
2. Fie două numere naturale n şi k cu n ≥ k, se cere să se genereze
toate submulţimile (combinările) de k elemente din mulţimea {1,
2, …,n}.
3. Se dau mulţimile A1 = {1, 2, …, p1}, A2 = {1, 2, …, p2}, …, An =
{1, 2, …, pn} se cere să se genereze produsul cartezian al
mulţimilor A1, A2, …, An.
4. Să se descompună un număr natural n în toate modurile posibile
ca sumă de numere naturale.
5. Se consideră mulţimea {1, 2, …, n}. Se cer toate partiţiile acestei
mulţimi.
6. Se dă o sumă S şi n tipuri de monede având valorile v1, v2, …, vn
lei. Se cer toate modalităţile de plată a sumei S utilizând aceste
monede.
7. Considerându-se o tablă de şah de dimensiuni n × n, să se
determine toate modalităţile de amplasare a n ture pe această tablă
în aşa fel încât să nu se atace între ele.
8. Un întreprinzător ce dispune de un capital C trebuie să leagă
dintre n oferte la care trebuie avansate fonfurile fi şi care aduc
profiturile pi pe acelea pe care le poate onora cu capitalul de care
dispune şi care îi aduc profitul total maxim.
9. Să se determine toate delegaţiile de n persoane din care p femei
care se pot forma din t persoane din care v femei.
10. Avem la dispoziţie 6 culori: alb, negru, roşu, albastru, verde,
galben. Să se genereze toate steagurile ce se pot forma cu trei
culori diferite având la mijloc alb sau negru.
11. Un comis-voiajor trebuie să viziteze un număr de n oraşe şi să
revină în oraşul de plecare. Cunoscând legăturile dintre oraşe se
cere să se afişeze toate drumurile posibile pe care poate să le
parcurgă comis-voiajorul ştiind că nu trebuie să treacă de două ori
prin acelaşi oraş.
12. Într-un grup de persoane fiecare persoană se cunoaşte pe sine şi
cunoaşte eventual şi alte persoane din grup. Să se determine toate
modurile în care cele N persoane se pot repartiza în echipe astfel
încât orice persoană din grup să fie cunoscută de cel puţin un
membru al fiecărei echipe.

132
13. Cei doi fii ai unei familii moştenesc prin testament, în mod egal, o
avere formată din monede având ca valori numere întregi pozitive.
O condiţie din testament cere ca fraţii să împartă monezile în
două părţi având valori egale pentru a intra în posesia lor, altfel
suma va fi donată unei biserici. Scrieţi un program care să îi ajute
pe cei doi fraţi să intre în posesia moştenirii.
14. Pe malul unui râu se găsesc c canibali şi m misionari. Ei urmează
să treacă râul cu o barcă cu două locuri. Dacă pe unul dintre
maluri se vor afla mai mulţi canibali decât misionari aceştia din
urmă vor fi mâncaţi de canibali. Se cere un program care să
afişeze toate modalităţile de trecere a râului în care misionarii să
nu fie mâncaţi.
15. Pentru elaborarea unui test de aptitudini avem un set de n
întrebări, fiecare fiind cotată cu un număr de puncte. Să se
elaboreze toate chestionarele având între a şi b întrebări distincte,
fiecare chestionar totalizând între p şi q puncte. Întrebările sunt
date prin număr şi punctaj.
16. 2n + 1 persoane participă la discuţii (masă rotundă) care durează n
zile. Să se găsească variantele de aşezare la masă astfel ca o
persoană să nu aibă în două zile diferite acelaşi vecin.
17. Să se determine 5 numere de câte n cifre fiecare cifră putând fi 1
sau 2, astfel încât oricare dintre cele 5 numere să coincidă în m
poziţii şi să nu existe nici o pozitie care să conţină aceeaşi cifră în
toate cele 5 numere.
18. Să se genereze o secvenţă binară de lungime 2n+n+1 astfel încât
orice două secvente consecutive de n biti să fie diferite.
19. Se dau x1, x2, …, xn numere naturale cu cel mult 9 cifre şi n ≤ 100.
Fiecare număr xi este reprezentat în baza bi (2 ≤ bi ≤ 10) care nu
este cunoscută. Să se gasească baza bi pentru fiecare număr xi
astfel încât intervalul [a, b] în care sunt cuprinse cele n numere
(după transformarea în baza 10) să fie de lungime minimă.
20. Numărul 123456789 înmulţit cu 2, 4, 7 sau 8 dă ca rezultat tot un
număr de nouă cifre distincte (fără 0). 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.
21. 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.

133
22. Toate oraşele unei ţări sunt legate la o reţea de linii feroviare.
Două oraşe i şi j pot fi legate prin cel mult o legătură directă prin
care circulă un singur tren pe zi între orele p(i, j) şi s(i, j). Dacă se
dau două oraşe A şi B, să se afişeze traseul, staţiile de schimbare a
trenurilor şi timpii de aşteptare în aceste staţii astfel încât durata
totală a călătoriei să fie minimă.
23. Configuraţia unui teren este specificată printr-o grilă gen tablă de
şah de dimensiune n×n, fiecare careu având o anumită înălţime.
Într-un careu precizat al grilei se plasează o minge. Ştiind că
mingea se poate deplasa într-unul din cele maxim 8 careuri vecine
doar dacă acesta are o cotă strict mai mică, să se genereze toate
trasele pe care le poate urma mingea pentru a părăsi terenul.
24. 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".
25. Fie o matrice n x m cu valori de 0 şi 1. Dându-se o poziţie iniţială
(i,j) să se găsească toate ieşirile din matrice mergând doar pe
elemente cu valoarea 1.
26. O fotografie alb-negru este dată printr-o matrice cu valori 0 şi 1.
Fotografia prezintă unul sau mai multe obiecte. Punctele fiecărui
obiect sunt marcate cu 1. Se cere un program care să calculeze
numărul de obiecte din fotografie.
27. Se dă o matrice binară în care elementele cu valoarea 1
delimitează o suprafaţă marcată cu elemente de zero. Dându-se
coordonatele unui punct în cadrul matricei să se “coloreze”
suprafaţa în interiorul căreia cade punctul.
28. Se dau n puncte albe şi n puncte negre în plan prin perechi (i,j) de
numere întregi. Fiecare punct alb se va uni cu un singur punct
negru asttfel încăt segmentele formate să nu se intersecteze. Să se
găsească o soluţie ştiind ca oricare 3 puncte sunt necoliniare.
29. Se dau n puncte în plan prin perechi (i,j) de numere întregi. Să se
selecteze 4 puncte ce reprezintă colţurile pătratului care cuprinde
numărul maxim de puncte din cele n puncte date.

134
30. Se dă un cuvânt si o matrice de caractere de dimensiune m x n. Să
se găsească în matrice prefixul de lunginme maximă al cuvântului
dat.
31. Se dă un număr natural par n. Să se afişeze toate şirurile de n
paranteze care se închid corect.
32. Se consideră o bară de lungime n şi k repere de lungimi l1, l2,…,
lk. Din bară trebuiesc tăiate bucăţi de lungimea reperelor date,
astfel încât să avem cel puţin câte o bucată de lungime li cu i =
1..k şi pierderile să fie nule.
33. Se consideră un număr nelimitat de piese dreptunghiulare de
dimensiuni a × 2a. Piesele se vor folosi la pavarea fără fisuri a
unei zone rectangulare de dimensiuni ka × pa. Se cer toate
soluţiile (reprezentare grafică).

3.5. Metoda Branch and Bound


3.5.1. Prezentarea metodei
Metoda branch and bound foloseste la rezolvarea problemelor la care
domeniul în care se caută soluţia este foarte mare şi nu se cunoaşte un
alt algoritm care să conducă mai rapid la rezultat. Problemele care pot
fi abordate prin această metodă pot fi modelate într-un mod asemă-
nător celui folosit la metoda backtracking. Se pleacă de la o configu-
raţie iniţială şi se reţine şirul de operaţii prin care aceasta este transfor-
mată într-o configuraţie finală dacă aceasta este dată, în alte cazuri se
cere configuraţia finală ştiind că trebuie să verifice anumite condiţii de
optim.
Diferenţa dintre cele două metode constă în faptul că metoda
backtracking la fiecare etapă selectează un singur succesor după care
se face verificarea condiţiilor de continuare, iar metoda branch and
bound generează la fiecare pas toate configuraţiile posibile (toţi succe-
sorii) care rezultă din cea curentă şi le stochează într-o listă. Se alege
apoi un element din listă după un criteriu ce depinde de problemă şi
se reia procesul de expandare.
Alegerea optimă a unui element din această listă pentru
expandare se face cu ajutorul unei funcţii f = g + h în care g este o
funcţie care măsoară lungimea drumului parcurs de la configuraţia
iniţială până la nodul curent, iar h este o funcţie (euristică) care
estimează efortul necesar pană se ajunge la soluţie şi este specifică
fiecărei probleme. Alegerea funcţiei h este foarte importantă din punct
de vedere a vitezei de execuţie a programului.
135
Se lucrează cu două liste: lista open în care se reţin configuraţiile
neexpandate încă şi lista close care le memorează pe cele expandate.
Soluţia se construieşte folosind un arbore care se parcurge pe baza
legăturii tată.
Nodurile sunt înregistrări care cuprind următoarele informaţii:
1. configuraţia la care s-a ajuns, t;
2. valorile funcţiilor g si h,
3. adresa nodului 'tată';
4. adresa înregistrării următoare în lista open sau close;
5. adresa înregistrării următoare în lista succesorilor.
Algoritmul este următorul:
A. înregistrarea corespunzătoare configuraţiei iniţiale este încărcată
în open cu g=0 şi f=h;
B. atât timp cât nu s-a selectat spre expandare nodul corespunzător
configuraţiei finale şi lista open este nevidă, se execută
următoarele:
1. se selectează din lista open nodul t cu f minim;
2. se expandează acest nod obţinând o listă liniară simplu
înlănţuită cu succesorii săi;
3. pentru fiecare succesor din această listă se execută:
3.1. se ataşează noul g, obţinut ca sumă între valoarea lui g
a configuraţiei t şi costul expandării (de obicei 1);
3.2. se testează dacă acest succesor aparţine listei open sau
close şi în caz afirmativ, se verifică dacă valoarea lui g
este mai mică decât cea a configuraţiei găsite în listă:
3.2.1. în caz afirmativ, nodul găsit este direcţionat
către actualul părinte (prin fixarea legăturii tată)
şi se ataşează noul g, iar dacă acest nod se găseşte
în close, este trecut în open;
3.2.2. în caz că acest nod nu se găseşte în open sau
close, este introdus în lista open;
C. dacă s-a selectat pentru expandare nodul corespunzător configu-
raţiei finale atunci se trasează folosind o procedură recursivă
drumul de la configuraţia iniţială la cea finală utilizând legătura
'tată';
D. dacă ciclul se încheie deoarece lista open este vidă înseamnă că
problema nu are soluţie.
Observaţie. Algoritmul se poate implementa folosind o singură listă,
iar diferenţierea dintre nodurile expandate şi cele neexpandate se va
face după un câmp special. Apare însă dezavantajul că operaţiile de
136
căutare se fac într-o listă mai lungă. Acest dezavantaj se poate elimina
folosind o structură specială numită tabelă hash. În unele cazuri se
poate folosi un arbore binar.
3.5.2. Probleme rezolvate
R.3.5.1. [Problema discretă a rucsacului] Se dă un rucsac de capacita-
te M şi un număr de n obiecte, specificându-se masele obiectelor şi
profiturile ce se obţin prin transportul lor cu rucsacul. Se cere un pro-
gram care să determine obiectele alese astfel încât să se obţină un
profit maxim. Soluţia se va da sub forma unui vector definit astfel:
xk=0 dacă obiectul k nu este luat în rucsac, xp = 1 dacă obiectul p este
luat în rucsac.
Rezolvare:
În rezolvarea acestei probleme nu vom folosi două liste ci un arbore
binar. Algoritmul face, în prealabil, o ordonare descrescătoare a
obiectelor după raportul profit/masă. Se porneşte de la configuraţia
iniţială în care nici obiect nu a fost încă ales. Se parcurge iterativ lista
de obiecte. La fiecare iteraţie se decide includerea sau nu a obiectului
curent în rucsac şi se expandează configuraţia curentă în două
configuraţii corespunzătoare celor doi subarbori. Nodul corespunzător
fiului drept se obţine din nodul tată la care se adaugă obiectul curent,
iar cel corespunzător fiului stâng se obţine din nodul tată însă fără
adăugarea obiectului analizat. Analiza continuă numai pe una din cele
două ramuri ale arborelui binar, iar opţiunea pentru una din cele două
ramuri se face pe baza unei funcţii H care estimează profitul.

program RucsacBB;
uses crt;
const max = 25;
type TObiect = record masa, profit:integer; nr:integer end;
PNod= ^Nod;
Nod = record
nivel,luat:integer;
p_estimat,profit,masa:integer;
tata,drept,stang:PNod;
end;
var n: integer;
obiect: array[1..max] of TObiect;
G, profitmax,masatotala:integer;
solutie:array[1..max] of 0..1;
137
procedure Sort;
var i,j:integer; aux:TObiect;
begin
for i:=1 to n-1 do for j:=i+1 to n do
if
(obiect[i].profit/obiect[i].masa < obiect[j].profit/obiect[j].masa)
then begin aux:= obiect[i]; obiect[i]:=obiect[j]; obiect[j]:=aux;
end;
end;
function H(pn:PNod):integer;
var i,k:integer; p,m:integer;
begin
p := pn^.profit; m := pn^.masa; k := pn^.nivel;
if m <= G then begin
for i:= k + 1 to n do if m + obiect[i].masa <= G then
begin
p := p + obiect[i].profit; m := m + obiect[i].masa
end;
H := p
end
else H:=-1;
end;
procedure Branch;
var k:integer; nodc,stang,drept:PNod;
begin
k:=0; new(nodc); nodc^.tata:= nil; nodc^.nivel:=k;
nodc^.luat:=0; nodc^.masa:=0; nodc^.profit:=0;
repeat
inc(k); new(stang); new(drept);
nodc^.stang:=stang; nodc^.drept:=drept;
stang^.tata:=nodc; stang^.nivel:=k; stang^.luat:=0;
stang^.masa:=nodc^.masa; stang^.profit:=nodc^.profit;
stang^.p_estimat:= H(stang); drept^.tata:=nodc;
drept^.nivel:=k; drept^.luat:=1;
drept^.masa:=nodc^.masa + obiect[k].masa;
drept^.profit:=nodc^.profit + obiect[k].profit;
drept^.p_estimat:=H(drept);
if stang^.p_estimat > drept^.p_estimat
then nodc:=stang else nodc:=drept;
until k=n;

138
profitmax := nodc^.profit; masatotala := nodc^.masa;
while nodc^.tata<>nil do begin
solutie[nodc^.nivel]:=nodc^.luat; nodc:=nodc^.tata;
end
end;
var i:integer;
begin {main}
write('Capacitatea rucsacului = '); readln(G);
write('Numarul obiectelor = '); readln(n);
for i:= 1 to n do
begin
write('Masa[',i ,'] = '); readln(obiect[i].masa);
write('Profitul[',i ,'] = '); readln(obiect[i].profit);
obiect[i].nr := i;
end;
Sort;
Branch;
writeln('Solutie:');
for i:= 1 to n do if solutie[i]=1 then write(obiect[i].nr,' ');
writeln;
writeln('Profitul obtinut = ', profitmax);
writeln('Masa totala = ', masatotala);
readln;
end.
R3.5.2. [Jocul Perspico] Se consideră o tablă cu 4 x 4 căsuţe, fiecare
căsuţă conţine un număr între 1 si 15 si o căsută conţine zero (este
liberă). Ştiind că 0 îşi poate schimba poziţia cu orice numâr natural
aflat deasupra, la dreapta, la stânga sau jos, în raport cu poziţiâ în
care se află numărul 0, se cere să se precizeze şirul de mutări prin
care se poate ajunge de la o configuraţie iniţială dată la o confi-
guraţie finală. Se cere de asemenea ca acest sir să fie optim, în sensul
că trebuie să se ajungă la configuraţia finalâ într-un număr minim de
mutări.
Rezolvare:
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#define MAX 4
#define maxop 4

139
typedef struct Nod{
char conf[MAX][MAX];
int h, g;
struct Nod *down, * up;
}Nod, *PNod;
PNod New(PNod p){
PNod pnod = (PNod) malloc( sizeof(Nod) );
if (pnod==NULL){
printf("\nNot enough memory !\n");
exit(0);
}
*pnod = *p;
return pnod;
}
void Insert(PNod * list, PNod pnod){
pnod->down = *list; *list = pnod;
}
void RemoveAll(PNod *list){
PNod p;
while(*list){
p = *list; *list = (*list)->down;free(p);
}
}
PNod Expand(PNod pnod){
int i, j, k, ii, jj;
static int lin[maxop] = {-1, 0, 1, 0};
static int col[maxop] = { 0, 1, 0,-1};
PNod list = NULL; PNod newNod = NULL;
for(i=0; i < MAX; i++) for(j=0; j < MAX; j++)
if (pnod->conf[i][j]==0) ii=i,jj=j;
for(k = 0; k < maxop; k++){
i = ii + lin[k]; j = jj + col[k];
if(i >= 0 && i < MAX && j >= 0 && j < MAX){
newNod = New(pnod);
newNod->conf[ii][jj] = newNod->conf[i][j];
newNod->conf[i][j] = 0; Insert(&list,newNod);
}
}
return list;
}

140
int Equals(PNod n1, PNod n2){
int i, j;
if (n1->h != n2->h) return 0;
for (i = 0; i < MAX; i++) for (j = 0; j < MAX; j++)
if (n1->conf[i][j] != n2->conf[i][j]) return 0;
return 1;
}
PNod Extract(PNod * list, PNod p){
PNod temp = *list, spy = *list;
if (p==NULL || p == *list){ *list = (*list)->down; return temp;}
while(temp != p){ spy = temp; temp = temp->down;}
spy->down = temp->down;
return temp;
}
void PrintNod(PNod pnod){
int i,j;
for(i = 0; i < MAX; i++){ printf("\n|");
for(j = 0; j < MAX; j++) printf("%3d", pnod->conf[i][j]);
printf(" |");
}
printf("\nApasati orice tasta ...”); getch();
}
void Print(PNod pnod){
if (pnod->up) Print(pnod->up); PrintNod(pnod);}
PNod Choice(PNod list){
int f, min = 32000; PNod pmin = NULL;
while (list){
f = list->h + list->g; if ( f < min ){pmin = list; min = f;}
list=list->down;}
return pmin;
}
void ComputeH(PNod t, PNod fin){
int i, j, k, r, s; t->h = 0;
for(i = 0; i < MAX; i++) for(j = 0; j < MAX; j++)
for(r = 0; r < MAX; r++) for(s = 0; s < MAX; s++)
if (t->conf[i][j] == fin->conf[r][s]) t->h += abs(r-i) + abs(s-j);
}
PNod Search(PNod list, PNod pnod){
while (list && !Equals(list,pnod)) list = list->down;
return list;}

141
void Branch(Nod *in, Nod *fin){
PNod Open = NULL; PNod Close = NULL;
PNod TempList = NULL; PNod theOne, temp, found;
PNod initial = New(in); Pnod final = New(fin);
int ok = 0; initial->g = 0;
ComputeH(initial,final); final->h = 0; Insert(&Open,initial);
while(Open){ theOne = Choice(Open); Extract(&Open,theOne);
Insert(&Close,theOne);
if (Equals(theOne, final)){ok = 1; break;}
TempList = Expand(theOne);
while(TempList){
temp = Extract(&TempList,NULL);
temp -> g = theOne->g + 1;
if( found = Search(Open, temp) ){
if(temp->g < found->g){
found->g = temp->g; found->up = theOne;}
free(temp);
}
else if( found = Search(Close, temp) ){
if(temp->g < found->g){
found-> g = temp->g; found->up = theOne;
Extract(&Close, found);
Insert(&Open, found);}
free(temp);
}
else{ temp->up = theOne; ComputeH(temp,final);
Insert(&Open,temp);}
}
}
if (ok){ printf("\nSolutie:\n"); Print(theOne);}
else printf("Nu am avem solutii !");
RemoveAll(Open); RemoveAll(Close);
}
void main(){
Nod in = {{ 5, 1, 3, 4, 9, 7, 8, 0, 6, 2, 10, 11,
13, 14, 15, 12}, 0, 0, NULL,NULL};
Nod fin ={{ 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,
13,14,15, 0},0,0, NULL,NULL};
clrscr(); Branch(&in,&fin); getch();
}

142
3.5.3. Probleme propuse
1. [Generalizare] Să se rezolve problema Perspico pentru o tablă de
dimensiuni n x n cu numere de la 1 la n2-2 şi cu două zerouri (locuri
libere).
2. [Diamante] Se dă o tablă sub formă de cruce pe care sunt aşejate 32
de piese de diamant (◊) aşa cum se poate observa în figura de mai jos.
Poziţia din centru este liberă. O piesă se poate muta doar sărind
peste o altă piesă într-un loc liber, numai pe orizontală sau pe verticală
iar piesa peste care se sare trebuie luată. Se cere să se gasească un şir
de mutări astfel încât (să luăm cât mai multe piese de diamant)
pornind de la configuraţia iniţială să se ajungă la o configuraţie finală
care să conţină numai o singură piesă şi aceasta să se afle în centru.

◊ ◊ ◊
◊ ◊ ◊
◊ ◊ ◊ ◊ ◊ ◊ ◊
◊ ◊ ◊ ◊ ◊ ◊ ◊
◊ ◊ ◊ ◊ ◊ ◊ ◊
◊ ◊ ◊
◊ ◊ ◊

Configuraţia iniţială Configuraţia finală

3. [Operaţii] Se dă un tablou m×n (1 ≤ m+n ≤15) cu elemente numere


întregi. Se numeşte "operaţie" în acest tablou înmulţirea unei linii sau
coloane cu -1. Să se determine cea mai mică secvenţă de operaţii care
aduce tabloul la o formă în care toate sumele de linii sau coloane dau
numere nenegative.
4. [Buldozerul] Un teren de formă dreptunghiulară de dimensiuni m×n
este împărţit în pătrate de dimensiuni egale (ca la şah). Unele pătrate
conţin grămezi de pamânt iar altele sunt netede. Un buldozer plasat
într-un pătrat neted trebuie să niveleze tot terenul. Să se găsească un
loc de unde poate porni buldozerul şi ordinea de nivelare a pătratelor
(în cazul în care există o soluţie) ştiind că acesta:
• se deplasează doar pe direcţiile N, S, E, V;
• nu se poate deplasa peste un loc neted;
143
• trece dintr-un loc neted peste pătratul cu pământ de lângă
el în alt pătrat neted netezind astfel pătratul peste care a
trecut.
5. [Obstacole şi cai] Pe o tablă de şah n × n se găsesc p cai şi k
obstacole amplasate într-o configuraţie iniţială dată. Mutarea calului
este cea cunoscută de la şah. Calul poate sări peste un obstacol sau
peste un alt cal, dar nu poate staţiona pe o poziţie ocupată de un cal
sau de un obstacol. Să se determine, dacă există, un şir de mutări ale
cailor astfel încât să se ajungă la o configuraţie finală dată.
6. [Inele] Fie două inele în care sunt aşezate echidistant 13 bile.
Inelele au două bile în comun. Deci în
total vor fi 24 de bile din care 11 negre,
11 roşii şi două gri (ca în figura de mai
jos). Fiecare inel se poate roti în sensul
acelor de ceasornic cel puţin o poziţie şi
cel mult 12, antrenând cu el cele 13 bile
din interiorul său. Problema constă în a
aduce o configuraţie iniţială dată, la
configuraţia (finală) din figura următoare printr-un număr minim de
rotiri.
7. [Joc] Se dă o tablă de joc cu 31 de pătrate care conţine opt piese de
‘O’ şi opt piese de ‘X’ ca în figura de mai jos.
O piesă poate fi mutată numai pe orizontală şi pe verticală într-
un loc liber învecinat, sau prin săritură peste o altă piesă alăturată dacă
se ajunge într-un loc liber. Jocul constă în a schimba între ele locurile
ocupate de piesele 'O’ şi 'X' într-un număr minim de mutări.

A B C D E F G
1
2 O O O
3 O O O
4 O O X X
5 X X X
6 X X X
7

144
Tehnici avansate de programare - 2009

Probleme propuse

1. Colectia 2008
2. Probleme rezolvabile folosind metoda backtracking
3. Probleme diverse

Se presupun cunoscute structurile de date: tablouri, liste, arbori si definitii privind


grafurile (introduce in cadrul cursurilor din anul I).

Bibliografie
1. G. Albeanu, Algoritmi si limbaje de programare, Editura FRM, 2000.
(Capitolul 5).
2. G. Albeanu (coord.), L. Rapeanu, L. Radu, A. Averian, Tehnici de programare,
Editura FRM, 2003 (capitolele 2 si 3). Acest material este disponibil in
sectiunea Biblioteca virtuala.

Se presupune cunoscut limbajul de programare C.


TEHNICI AVANSATE DE PROGRAMARE
Exemple de subiecte – colectia 2008

1.
a. q, r, p c. r, q, p
b. p, q, r d. p, q, r

2.
a. Q, R, P c. P, R, Q
b. R, Q, P d. P, Q, R

3.
a. Q -> urm -> urm == P
b. P -> urm == Q
c. P -> urm -> urm == Q
d. Q -> urm == P -> urm -> urm
4.
a. P -> urm -> urm == Q -> urm
b. P -> urm -> urm == Q
c. Q -> urm -> urm -> urm == P -> urm
d. Q -> urm -> urm == P -> urm

5.
a. P -> URM = Q
b. Q -> URM = P
c. Q -> URM -> URM = P
d. P -> URM -> URM = Q

6.
a. c.
b. d.

7. Intr-o lista simplu inlantuita alocata dinamic fiecare element retine in campul nr un numar intreg si

in campul urm adresa urmatorului element din lista. Stiind ca variabila p contine adresa primului element din
lista si variabila t este de acelasi tip cu variabila p, stabiliti care dintre urmatoarele secvente elibereaza
intreaga zona de memorie ocupata de elementele listei.
a. while(p) {t = p; p = p->urm; free(p);}
b. while(p) {t = p; p = p->urm; free(t);}
c. while(p) {t=p; t=t->urm; free(t);}
d. free(p);

8. Intr-o lista liniara simplu inlantuita, fiecare element retine in campul urm adresa urmatorului nod din

lista, iar in campul inf un numar intreg. Adresa primului element al listei este retinuta in variabila p. Daca in
lista sunt memorate, in aceasta ordine, numerele: 5, 9, 3, si 6 (6 fiind ultimul element), in urma executarii
secventei de instructiuni (p indica, initial, nodul cu numarul 5):
{ q = p -> urm -> urm; p->urm -> urm = q -> urm; q->urm = p -> urm; p -> urm = q;}
in lista vor fi in ordine numerele:
a. 9, 5, 3, 6
b. 5, 9, 6, 3
c. 5, 3, 6, 9
d. 5, 3, 9, 6

9. O lista liniara simplu inlantuita formata dintr-un numar impar de cel putin 5 noduri are adresa

primului nod memorata in variabila prim. In campul urm al fiecarui nod al listei se memoreaza adresa
urmatorului element din lista. Adresa carui nod va fi memorata in variabila p, dupa executarea secventei de
program:
{p = prim; q = prim;
while(q->urm) {
q = q -> urm -> urm;
p = p -> urm;
}
}
a. penultimul nod al listei
b. nodul aflat in mijlocul listei
c. ultimul nod al listei
d. nodul al treilea din lista

10. Intr-o lista simplu inlantuita, alocata dinamic, fiecare element retine in campul next adresa

urmatorului nod din lista, iar in campul info un numar intreg. Adresa primului element al listei este
memorata in variabila prim. Se stie ca lista are cel putin 3 noduri. Care dintre urmatoarele secvente de
instructiuni elimina corect penultimul element al listei?
a. {
p = prim; do p = p->next; while(p->next->next->next);
p->next=p->next->next;
}
b. {
p = prim;
while (p->next->next->next) p = p->next;
p->next=p->next->next;
}
c. {
p = prim;
while (p->next->next) p = p->next;
p->next=p->next->next;
}
d. prim -> next = prim->next -> next;

11. Intr-o lista liniara, simplu inlantuita, alocata dinamic, fiecare element retine in campul next

adresa urmatorului nod din lista, iar in campul info in numar intreg. Adresa primului element al listei este
memorata in variabila prim. Lista contine cel putin 3 noduri. Care este efectul executarii urmatoarei secvente
de program
{
p = prim; q = p->next -> next;
while ( q-> next) {p = p->next; q = q-> next;}
p -> next = q;
}

a. Eliminarea nodului din mijlocul listei


b. Eliminarea din lista a ultimului nod;
c. Eliminarea din lista a penultimului nod
d. Eliminarea celui de-al doilea nod al listei
12 . Fiecare element al unei liste liniare simplu inlantuite alocata dinamic retine in campul adru

adresa elementului urmator din lista. Daca p retine adresa primului element, iar lista are cel putin doua
elemente, care dintre urmatoarele secvente dee instructiuni sterge al doilea element al listei?
a. q = p->adru; p->adru = q -> adru; free(q);
b. p -> adru = p->adru -> adru; free (p->adru);
c. q = p-> adru; free(q); p ->adru = q->adru;
d. free(p->adru);

13 . O lista liniara simplu inlantuita alocata dinamic, in care fiecare element memoreaza in

campul nr un numar intreg, iar in campul urm adresa elementului urmator din lista, contine exact trei
elemente ale caror adrese sunt memorate in variabilele p, q si r. Stiind ca q -> nr == 3, p -> nr == 5, r -> nr
== 8, q -> urm != NULL, p -> urm == NULL si r -> urm == q, care este ordinea numerelor din lista?
a. 8, 3, 5
b. 5, 8, 3
c. 3, 8, 5
d. 5, 3, 8

14. Intr-o lista circulara simplu inlantuita alocata dinamic cu cel putin un element, fiecare

element retine in campul nr un numar intreg si in campul urm adresa urmatorului element din lista. Stiind ca
variabila p retine adresa unui element din lista si variabila t este de acelasi tip cu p, stabiliti care dintre
urmatoarele secvente afiseaza toate valorile memorate in nodurile listei, fiecare valoare fiind afisata exact
odata.
a. t = p;
while(t -> urm != p) {
printf(“%d “, t -> nr;
t = t->urm;}
b. t = p;
do{
printf(“%d “, t -> nr;}
t = t->urm;
}while(t != p);
c. t = p;
while(t != p) {
printf(“%d “, t -> nr;
t = t->urm;}
d. t = p->urm;
do{
printf(“%d “, t -> nr;}
t = t->urm;
}while(t != p);
15. Intr-o lista dublu inlantuita care incepe cu elementul memorat la adresa p si contine cel putin

4 elemente, fiecare element retine in campul urm adresa elementului urmator, in campul pre adresa
elementului precedent, iar in campul inf o valoare intreaga. Care dintre urmatoarele variante tipareste
valoarea celui de-al treilea element al listei?
a. printf(“%d “, p->urm -> urm -> pre -> inf);
b. printf(“%d “, p->urm -> urm -> urm -> pre -> inf);
c. printf(“%d “, p->urm -> urm -> urm);
d. printf(“%d “, p->urm -> urm);

16. Variabila p retine adresa unui element oarecare al unei liste circulare nevide alocata dinamic,

in care fiecare element memoreaza in campul nr un numar intreg, iar in campul urm adresa elementului
urmator. Care dintre urmatoarele variante tipareste toate elementele listei?
a. q = p; do{
printf(“%d”, q -> nr); q = q -> urm;
} while (q != p);
b. q = p; while (q -> urm != p){
printf(“%d”, q -> nr); q = q -> urm;
}
c. q = p; while (q != p){
printf(“%d”, q -> nr); q = q -> urm;
}
d. q = p->urm;
while (q != p){
printf(“%d”, q -> nr); q = q -> urm;
}

17. Se considera o coada in care initial au fost introduse, in aceasta ordine, elementele 1 si 2.

Daca se noteaza cu AD(x) operatia prin care se adauga informatia x in coada, si cu EL() operatia prin care se
elimina un element din coada, care este rezultatul executarii secventei: EL(); Ad(3); EL(); AD(4); AD(5);?
a. 1, 4, 5
b. 5, 4, 2
c. 3, 4, 5
d. 5, 4, 3
18 Se considera o stiva in care initial au fost introduse, in aceasta ordine, valorile 1 si 2. Daca se

noteaza cu PUSH(x). operatia prin care se insereaza valoarea x in varful stivei si POP() operatia prin care se
extrage elementul din varful stivei, care este continutul stivei in urma secventei de operatii: POP(); PUSH(3);
POP(); PUSH(4); PUSH(5);
a. 5 b. 5 c. 2 d. 1
4 4 3 4
3 1 5 5

19 . In lista circulara simplu inlantuita ce contine numerele 1, 2, 3, 2, 3 in aceasta ordine, iar p

este adresa nodului ce contine primul numar 2 (fiecare nod are un camp nr ce contine numarul intreg si un
camp urm care indica adresa elementului urmator din lista). Prin executarea secventei
while (p -> nr > 0) {p -> nr = p -> nr -1; p = p -> urm;}
continutul listei, citit de la adresa de plecare va fi:
a. 0, 1, 0, 2,0
b. 1, 2, 1, 2, 0
c. 0, 1, 1, 2, 0
d. 0, 1, 0, 1, 0

20. Se considera ca variabilele p si q memoreaza adresa primului, respectiv ultimului element al

unei liste liniare nevide dublu inlantuite. Elementele listei retin in campul urm adresa elementului urmator,
iar in campul prec adresa elementului anterior. Stabiliti care este numarul de noduri din lista daca p -> urm -
> urm si q -> prec -> prec indica acelasi nod al listei.
a. 4 c. 3
b. 5 d. 2

21. Se considera lista circulara simplu inlantuita ce contine celulele cu numerele 1, 2, 3, 4 (in

aceasta ordine). Fiecare element memoreaza in campul nr un numar intreg, iar in campul urm adresa
elementului urmator din lista. Variabila prim indica nodul ce contine numarul 1. Cate treceri sunt necesare
pentru ca toate elementele din lista sa ajunga egale. Definim prin trecere prelucrarea data de secventa
urmatoare:
p = prim;
do {if(p->nr > prim->nr) p->nr = p->nr -1; p = p -> urm;}
while (p != prim);

a. 5 c. 3
b. 2 d. 4
22 . Intr-o lista circulara simplu inlantuita, p este adresa unui nod din lista si campul next

memoreaza pentru fiecare nod adresa nodului urmator din lista. Pentru a numara elementele listei vom scrie
secventa (variabila q este de acelasi tip cu variabila p):
a. q = p; k = 1; while(q -> next != p) {k++; q = q -> next;}
b. q = p; k = 1; do{ q = q -> next; k++; } while(q ==p);
c. q = p; k = 1; while(q!=p) {k++; q = q->next;}
d. k=0; do{p=p->next; k++;} while (p!=NULL);

23 . Se considera o stiva alocata dinamic care are cel putin 10 elemente. Variabila vf memoreaza

adresa de inceput a stivei si orice element al stivei memoreaza in campul info un numar intreg, iar in campul
next adresa nodului urmator. Se considera seceventa de program:
while (vf && vf -> info %2 == 0) {
aux = vf;
vf = aux-> next;
free (aux);
}
Daca in urma executarii secventei de program, variabila vf are valoarea NULL, atunci:
a. Primul element memorat in stiva este par, celelalte fiind numere impare.
b. In stiva nu s-a memorat nici un numar impar.
c. Ultimul element memorat in stiva este par, celelalte elemente fiind numere impare.
d. In stiva nu s-a memorat nici un numar par.

24 . Se considera o lista circulara cu 8 elemente numerotate cu 1, 2, 3, 4, 5, 6, 7, 8. Mai intai se

elimina elementul numerotat cu 3, apoi se elimina fiecare al treilea element al parcurgeri, numararea
continuandu-se cu succesorul elementului eliminat, pana cand lista va mai contine un singur element. Care
va fi numarul de ordine al elementului ramas?
a. 2 c. 3
b. 7 d. 4

25 . Se considera o lista circulara dublu inlantuita ale carei noduri retin in campul st adresa

nodului anterior, iar in campul dr adresa nodului urmator din lista. Lista are cel putin doua elemente. Stiind
ca p retine adresa unui nod din lista, care este numarul de noduri din lista astfel incat relatia
p->st->st == p->dr sa fie adevarata?
a. 5 c. 2
b. 3 d. 4

26 . Se considera lista dublu inlantuita cu noduri care contin in campul inf (ce retine un n umar

natural), in aceasta ordine, numerele: 3, 4, 5, 6, 7, 8. In campurile st si dr sunt retinute adresa nodului


precedent, respectiv adresa nodului urmator din lista.Variabilele globale p si sf retin adresele primului si
respectiv ultimului element din lista. O variabila ce retine adresa unui element este de tip nod. Care va fi
continutul listei la o parcurgere de la st la dr dupa apelul functiei sub(), unde, functia sub este:
void sub(){
nod *man = sf;
while(man->inf > sf -> inf /2) man = man ->st;
nod *q = man;
man -> st -> dr = q -> dr;
q -> dr -> st = man -> st;
free(q);
}
a. 3, 5, 6, 7, 8
b. 4, 5, 6, 7, 8
c. 3, 4, 5, 6, 7, 8
d. 3, 4, 6, 7, 8

27 . Se considera lista dublu inlantuita cu noduri care contin in campul inf (ce retine un n umar

natural), in aceasta ordine, numerele: 7, 5, 6, 2, 4, 6. In campurile st si dr sunt retinute adresa nodului


precedent, respectiv adresa nodului urmator din lista.Variabilele globale p si sf retin adresele primului si
respectiv ultimului element din lista. O variabila ce retine adresa unui element este de tip nod. Care va fi
continutul listei la o parcurgere de la st la dr dupa apelul functiei sub(), unde, functia sub este:
void sub(){
nod *man = sf;
while(man->inf > sf -> inf ) man = man ->st;
nod *q = man;
man -> st -> dr = q -> dr;
q -> dr -> st = man -> st;
free(q);
}
a. 7, 5, 6, 2, 4, 6
b. 7, 5, 6, 2, 6
c. 7, 5, 6, 4, 6
d. 7, 5, 6, 2, 4
28 . Se considera lista dublu inlantuita cu noduri care contin in campul inf (ce retine un n umar

natural), in aceasta ordine, numerele: 9, 7, 8, 3, 2, 4. In campurile st si dr sunt retinute adresa nodului


precedent, respectiv adresa nodului urmator din lista.Variabilele globale p si sf retin adresele primului si
respectiv ultimului element din lista. O variabila ce retine adresa unui element este de tip nod. Care va fi
continutul listei la o parcurgere de la st la dr dupa apelul functiei sub(), unde, functia sub este:
void sub(){
nod *man = sf;
while(man->inf > sf -> inf ) man = man ->st;
nod *q = man;
man -> st -> dr = q -> dr;
q -> dr -> st = man -> st;
free(q);
}
a. 9, 7, 3, 2, 4 c. 9, 7, 8, 3, 2
b. 9, 7, 8, 2, 4 d. 9, 8, 3, 2, 7

29 . Intr-o lista simplu inlantuita circulara, fiecare element retine in campul adr adresa

elementului urmator din lista. Daca p si q sunt adresele a doua elemente distincte din lista astfel incat sunt
satisfacute conditiile p == q -> adr si q == p -> adr. Atunci lista are
a. un numar impar de elemente c. cel putin 3 elemente
b. exact 2 elemente d. exact 1 element

30 . Se considera o stiva implementata prin intermediul vectorului a cu elementele a[0] = 0, a[1]

= 10, a[2] = 20, a[3] = 30, a[4] = 40, a[5] = 50. Daca cel de-al doilea element, incepand de la baza stivei este
10, atunci primul element care iese din stiva este:
a. a[6] c. a[5]
b. a[1] d. a[0]

31 . Intr-o lista circulara simplu inlantuita, cu cel putin un element, fiecare nod retine in campul

adr adresa elementului urmator din lista. Daca p este o variabila care retine adresa primului element din lista,
iar q este o variabila care poate sa retina adresa unui element din lista, care dintre urmatoarele secvente de
instructiuni calculeaza in variabila nr, de tip int, numarul de elemente al listei?
a. nr = 0; q = p; while(q != p) {nr++; q = q -> adr;}
b. nr = 0; q = p; do {nr ++; q = q -> adr;} while (q != p);
c. nr = 0; q = p; do {nr ++; q = p -> adr;} while (q != p);
d. nr = 0; q = p; while (p != q){ nr ++; p = p -> adr;}

32 . Intr-o lista circulara simplu inlantuita fiecare element retine in campul adr adresa

elementului urmator din lista. Daca p reprezinta adresa unui element din lista atunci stabiliti care dintre
urmatoarele expresii are valoarea 1 daca si numai daca lista contine exact doua noduri.
a. p -> adr == p c. p -> adr -> adr == p
b. p -> adr -> adr == NULL d. p -> adr != NULL

33 . Se considera urmatoarea functie recursiva apelata numai pentru numere naturale nenule:

int f(int a, int b){


if (a<b) return a; else return f(a-b, b);
}
Care dintre urmatoarele functii este echivalenta cu functia data?

a. int f(int a, int b){return a*b;}


b. int f(int a, int b){return a-b+1;}
c. int f(int a, int b){return a%b;}
d. int f(int a, int b){return a/b;}

34 .

void f(int n){


Se considera definitia

int j;
if (n>0) for (j=1; j<=n; j++) {printf(“%d”,j); f(n-1);}
}
Ce se afiseaza ca urmare a apelului f(2)?
a. 1122 c. 121
b. 112 d. 1121

35 .

long f(int n){


Se considera definitia:

if (n == 0) return 1;
else if (n == 1) return 4;
else return f(n-1) - f(n-2);
}
Stabiliti ce valoasre returneaza apelul f(7).

a. 1 c. -4
b. -3 d. 4

36. Se considera definitia

long f(int n, int k){


if (n == k || k == 1) return 1;
if (n < k) return 0;
long s=0, i;
for (i=1; i<=k; i++) s+=f(n-k,i);
return s;
}
Stabiliti ce valoare returneaza apelul f(6,3).

a. 3 c. 2
b. 1 d. 4

37 . Se considera definitia:

long f(int x, int y){


if (x == y || x == 0) return 1;
else return f(x,y-1)+f(x-1,y-1);
}
Ce valoare returneaza apelul f(8,10)?

a. 50 c. 40
b. 45 d. 55

38 .

void star(int i){


In functia recursiva de mai jos se considera ca tabloul unidimensional v este declarat global.

if(i<10) {
printf(“*”);
if (v[i] == i+1) star(i+2); else star(i+1);
}
}
Pentru care dintre declaratiile urmatoare, apelul star(0) produce 7 asteriscuri (stelute)?
a. int v[] = {1, 4, 3, 2, 1, 6, 5, 4, 3, 10};
b. int v[] = {3, 2, 1, 4, 3, 6, 7, 2, 9, 2};
c. int v[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d. int v[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};

39 . Pentru o valoare naturala mai mare decat 1 memorata in variabila globala n, subprogramul

urmator afiseaza cel mai mare divizor al lui n, mai mic decat n, la apelul divi(n).
void divi(long i){
if ( ... == 0) printf(“%ld”, ...); else divi(i-1);
}
Cu ce expresii trebuie completate punctele de suspensie?
a. n % i si i c. n%(i-1)=0 si i
b. n% (i-1) si i-1 d. n%i si i-1

40 . Stiind ca p este un vector (tablou unidimensional) cu 3 componente intregi (tabloul este

declarat global), M este multimea tuturor cifrelor nenule, iar functia tipar afiseaza valorile elementelot p[0],
p[1] si p[2], cu ce trebuie inlocuite simbolurile a, b si c in definitia functiei G astfel incat in urma apelului
G(0) sa se afiseze toate elementele produsului cartezian MxMxM?
void G(int k){
int i;
for (i = a; i<=b; i++) { p[k] = i; if (k == c) tipar(); else G(k+1);}
}
a. a = 0, b = 10, c = 3 c. a = 1, b = 9, c = 3
b. a = 1, b = 3, c = 9 d. a = 1, b = 9, c = 2

41 .

void ex(int x){


Pentru definitia alaturata a functiei ex(), stabiliti ce se afiseaza la apelul ex(120)?

if (x != 0){
printf(“%d”, x %10);
ex(x/10);
}
}

a. 012 c. 021
b. 120 d. 21
42.
a. 230
Gasiti elementul f(20) din sirul definit prin relatia (f(n))2 = 8(f(n-1))2, unde f(0) = 2

c. 219
b. 220 d. 231

43 . Se considera relatia de recurenbta neomogena de ordinul intai f(n) - f(n-1) = 9n2, f(0) = 8,

n>0; Atunci f(n) =


a. c.

b. d.

44
a.
. Se considera relatia de recurenta f(n) - 7f(n-1) = 9(5n), n > 0; f(0) = 3. Atunci f(n) =

c.

b. d.

45 .

a. (9n+3)7n
Solutia f(n) a relatiei de recurenta f(n) - 7f(n-1) = 9(7n), n>0, f(0) = 3, este

c. (9n+9)7n
b. (3n+9)7n d. (3n+3)7n

46 .

a. 3n-n3n-1
Solutia relatiei de recurenta f(n) = 6 f(n-1) - 9 f(n-2), n≥0, f(0) = 1, f(1) = 2 este f(n) =

c. 3n+1-n3n
b. 3n-1-n3n d. 3n+1-n3n-1
47
a.
. Solutia relatiei de recurenta f(n) = 2f(n-1) - 4f(n-2), n>1, f(0)=1, f(1) = 3 este f(n) =

c.

b. d.

48 . Un algoritm de tip backtracking genereaza in ordine lexicografica, toate sirurile de 5 cifre 0

si 1 cu proprietatea ca nu exista mai mult de doua cifre de 0 consecutive. Primele sase solutii generate sunt:
00100, 00101, 00110, 01001, 01010. Care este cea de-a opta solutie?
a. 01110 c. 01011
b. 01100 d. 01101

49 Un algoritm backtracking genereaza toate sirurile alcatuite din cate 5 cifre binare (0 si 1).

Numarul tuturor solutiilor generate va fi egal cu :


a. 5 c. 10
b. 32 d. 31

50 . Aplicand metoda backtracking pentru a genera toate permutarile celor n elemente ale unei

multimi, o solutie se memoreaza sub forma unui tablou unidimensional x1, x2, ..., xn. Daca sunt deja generate
valori pentru componentele x1, x2, ..., xk-1, iar pentru componenta xk (1 <k<n)au fost testate toate valorile
posibile si nu a fost gasita niciuna convenabila, atunci:
a. se incearca alegerea unei noi valori pentru componenta xk-1.
b. se incearca alegerea unei noi valori pentru componenta x1, oricare ar fi valoarea k.
c. se incheie algoritmul.
d. se incearca alegerea unei valori pentru componenta xk+1.
51 . Daca se utilizeaza metoda backtracking pentru a genera toate numerele naturale, in ordine

strict crescatoare, formate din 4 cifre pare distincte, care dintre numerele de mai jos trebuie, eliminate astfel
incat cele ramase sa reprezinte o succesiune de numere corect generate?
1) 2068; 2) 2084; 3) 2088; 4) 2468; 5) 2086; 6) 2406
a. numai 3)
b. atat 3) cat si 5)
c. atat 3) cat si 4)
d. numai 4)

52 . Se considera multimea {1, 7, 5, 16, 12}. Se genereaza prin metoda backtracking toate

submultimile sale formate din exact 3 elemente: primele patru solutii generate sunt, in ordine: {1, 7, 5}, {1,
7, 16}, {1, 7, 12}. Care dintre solutii trebuie eliminate din sirul urmator astfel incat cele ramase sa apara in
sir in ordinea generarii lor:
{1, 16, 12}, {5, 16, 12}, {7, 5, 16}, {7, 5, 12}
a. {1, 16, 12}
b. {5, 16, 12}
c. {7, 5, 16}
d. {7, 5, 12}

53 . Se considera algoritmul care genereaza in ordine strict crescatoare toate numerele formate cu

5 cifre distincte alese din multimea {1, 0, 5, 7, 9} in care cifra din mijloc este 0.Selectati numarul care
precede si numarul care urmeaza secventei de numere generate:
19075; 51079; 51097
a. 19057, 57019
b. 15079, 71059
c. 19057, 59071
d. 15097, 71095

54 . Daca pentru generarea tuturor submultimilor unei multimi A = {1, 2, ..., n} cu 1 ≤ n ≤ 10, se

utilizeaza un algoritm backtracking astfel incat se afiseaza in ordine, pentru n=3, submultimile {}, {1}, {2},
{3}, {1, 2}, {1,3}, {2,3}, {1, 2, 3}, atunci, utilizand exact acelasi algoritm pentr n = 4, in sirul submultimilor
generate, solutia a 7-a va fi:
a. {1,3}
b. {4}
c. {1,2,3}
d. {1,4}

55 . Produsul cartezia {1,2,3}x{2,3} este obtinut cu ajutorul unui algoritm backtracking care

genereaza perechile (1,2), (1,3), (2,2), (2,3), (3,2) si (3,3). Care este numarul perechilor obtinute prin
utilizarea aceluiasi algoritm la generarea produsului cartezian {1, 2, 3, 4}x{2, 3, 4}?
a. 12 c. 81
b. 10 d. 6

56 . Se genereaza toate sirurile strict crescatoare de numere naturale nenule mai mici sau egale cu

4, avand primul termen 1 sau 2, ultimul termen 4 si cu diferenta dintre oricare doi termeni aflati pe pozitii
consecutive cel mult 2, obtinandu-se solutiile (1, 2, 3,4), (1, 2, 4), (1, 3, 4), (2, 3, 4), (2, 4). Folosind aceeasi
metoda generam toate sirurile strict crescatoare de numere naturale nenule mai mic sau egale cu 6, avand
primul termen 1 sau 2, ultimul termen 6 si diferenta dintre oricare doi termeni aflati pe pozitii consecutive cel
mult 2, care dintre afirmatiile urmatoare este adevarata:
a. imediat dupa solutia (1, 3, 4, 5, 6) se genereaza solutia (2, 3, 4, 5, 6)
b. penultima solutie generata este (2, 3, 5, 6)
c. imediat dupa solutia (1, 2, 4, 6) se genereaza solutia (1, 3, 4, 6)
d. in total sunt generate 13 solutii.

57 . Avand la dispozitie cifrele 0, 1 si 2 putem genera, in ordine crescatoare, numerele care au

suma cifrelor egala cu 2 astfel: 2, 11, 20, 101, 110, 200, etc. Folosind acest algoritm generati numerele cu
cifrele 0, 1 si 2 care au suma cifrelor egala cu 3. Care va fi al saptelea numar din aceasta generare?
a. 120
b. 1002
c. 201
d. 210

58 . Generarea tuturor cuvintelor de 4 litere, fiecare litera putand fi orice element din multimea

{a, c, e, m, v, s}, se realizeaza cu ajutorul unui algoritm echivalent cu algoritmul de generare a:


a. produsului cartezian c. partitiilor unei multimi
b. combinarilor d. permutarilor
59 . Folosind un algoritm de generare putem obtine numere naturale de k cifre care au suma

cifrelor egala cu un numar natural s introdus de la tastatura, unde s si k sunt numere naturale nenule. Astfel
pentru valorile k = 2 si s = 6 se genereaza numerele: 15, 24, 33, 42, 51, 60. Care vor fi primele 4 numere ce
se vor genera pentru k = 3 si s=8?
a. 800, 710, 620, 530 c. 125, 233, 341, 431
b. 107, 116, 125, 134 d. 116, 125, 134, 143

60 . Se considera multimile A = {1, 2, 3}, B = {1}, C = {2, 3, 4}. Elementele produsului

cartezian AxBxC se genereaza, in ordine astfel: (1, 1, 2), (1, 1, 3), (1, 1, 4), (2, 1, 2), (2, 1, 3), (2, 1, 4), (3, 1,
2), (3, 1, 3), (3, 1, 4). Daca prin acelasi algoritm se genereaza produsul cartezian al multimilor AxBxC, unde
A = {a}, B ={a, b}, C = {b, c, d}, atunci cel de-al patrulea element generat este:

a. (a, b, c) c. (a, b, b)
b. (a, c, b) d. (a, c, d)

61 . Pentru a determina toate modalitatile de a scrie numarul 8 ca suma de numere naturale

nenule distincte (abstractie facand de ordinea termenilor) se foloseste metoda backtracking obtinandu-se, in
ordine, toate solutiile 1+2+5, 1+3+4, 1+7, 2+6, 3+5. Aplicand exact acelasi procedeu, se determina solutiile
pentru scrierea numarului 10. Cate solutii de forma 1+ ... exista?
a. 3 c. 5
b. 4 d. 6

62 . Se considera multimile A = {1, 2, 3}, B = {1}, C = {2, 3, 4}. Elementele produsului

cartezian AxBxC se genereaza, folosind metoda backtracking, in ordinea (1, 1, 2), (1, 1, 3), (1, 1, 4), (2, 1,
2), (2, 1, 3), (2, 1, 4), (3, 1, 2), (3, 1, 3), (3, 1, 4). Daca prin acelasi algoritm se genereaza produsul cartezian
al multimilor AxBxC unde A = {x, y}, B = {x}, c = {x, y, z}, atunci cel de-al treilea element generat este:
a. (x, x, y) c. (x, x, z)
b. (x, y, x) d. (x, y, z)
63 . Generarea tuturor sirurilor formate din trei elemente, fiecare element putand fi oricare numar

din multimea {1, 2, 3}, se realizeaza cu ajutorul unui algoritm echivalent cu algoritmul de generare a:
a. permutarilor c. produsului cartezian
b. combinarilor d. aranjamentelor

64 . In utilizarea metodei backtracking pentru a genera toate cuvintele alcatuite din doua litere

ale multimii {a, c, e, q}, astfel incat sa nu existe doua consoane alaturate, cuvintele se genereaza in
urmatoarea ordine: aa, ac, ae, aq, ca, ce, ea, ec, ee, eq, qa, qe. Daca se utilizeaza exact aceeasi metoda pentru
a genera cuvinte formate din 4 litere ale multimii {a, b, c, d, e, f}, astfel incat sa nu existe doua consoane
alaturate in cuvant, care este penultimul cuvant generat?
a. fefa c. feef
b. fafe d. fefe

65 . Utilizand metoda backtracking se genereaza toate numerele formate doar din trei cifre astfel

incat fiecare numar sa aiba cifrele distincte. Cifrele fiecarui numar sunt din multimea {12, 2, 3, 4}. acest
algoritm genereaza numerele, in aceasta ordine: 123, 124, 132, 134, 213, 214, 231, 234, 312, 314, 321, 324,
412, b413, 421, 423, 431, 432. Daca utilizam acelasi algoritm pentru a genera toate numerele de 4 cifre,
fiecare numar fiind format din cifre distincte din multimea {1, 2, 3, 4, 5}, precizati care este numarul generat
imedia dupa 4325.
a. 4351 c. 4521
b. 5123 d. 4321

66 . Utilizand metoda backtracking se genereaza toate numerele palindrom formate din 4 cifre.

Fiecare numar contine cifre din multimea {1, 3, 5}. Elementele sunt generate in urmatoarea ordine: 111,
1331, 1551, 3113, 3333, 3553, 5115, 5335, 5555. Daca se utilizeaza exact aceeasi metoda pentru a genera
toate numerele palindrom formate din 4 cifre, fiecare element avand cifre din multimea {1, 2, 3, 4, 5, 6, 7, 8,
9}. Sa se precizeze cate numere pare se vor genera.
a. 99 c. 36
b. 40 d. 72
67 . Utilizand metoda backtracking se genereaza elementele produsului cartezian a n multimi A1,

A2, ..., An. Daca utilizam acest algoritm pentru a genera elementele produsului cartezian a 3 multimi: M = {1,
2, 3}, N = {1, 2} si P = {1, 2, 3, 4} atunci care din urmatoarele secvente nu reprezinta o solutie acestui
algoritm, pentru produsul cartezian PxNxM?
a. (4, 2, 3) c. (3, 2, 1)
b. (3, 3, 3) d. (1, 1, 1)

68 . Utilizand metoda backtracking se genereaza toate numerele de cate 3 cifre astfel incat

fiecare numar generat are cifrele distincte si suma lor este un numar par. Precizati care dintre urmatoarele
numere reprezinta o solutie a algoritmului?
a. 235 c. 281
b. 986 d. 455

69 . Utilizand metoda backtracking se genereaza in ordine lexicografica toate posibilitatile de

aranjare a 8 dame pe tabla de sah astfel incat aceastea sa nu se atace. fiecare solutie se exprima sub forma
unui vector c = (c1, c2, ..., c8) unde c1 reprezinta coloana pe care se afla dama de pe linia i. Stiind ca primele
doua solutii generate sunt (1, 5, 8, 6, 3, 7, 2, 4), (1, 6, 8, 3, 7, 4, 2, 5) sa se determine solutia generata de
algoritm imediat dupa solutia (8, 2, 4, 1, 7, 5, 3, 6).
a. (8, 1, 2, 3, 4, 5, 6, 7) c. (8, 2, 5, 3, 1, 7, 4, 6)
b. (8, 4, 2, 7, 6, 1, 3, 5) d. (7, 4, 2, 5, 8, 1, 3, 6)

70 . Se genereaza toate sirurile strict crescatoare de numere naturale nenule mai mici sau egale cu

4, avand primul termen 1 sau 2, ultimul termen 4 si cu diferenta dintre oricare doi termeni aflati pe pozitii
consecutive cel mult 2, obtinandu-se solutiile (1, 2, 3, 4), (1, 2, 4), (1, 3, 4), (2, 3, 4), (2, 4). Folosind aceeasi
metoda, generam toate sirurile strict crescatoare de numere naturale nenule mai mici sau egale cu 5, care
dintre afirmatiile urmatoare este adevarata:
a. imediat dupa solutia (1, 3, 5) se genereaza solutia (2, 3, 4, 5).
b. imediat dupa solutia (2, 3, 5) se genereaza solutia (2, 5).
c. penultima solutie generata este (2, 4, 5).
d. in total sunt generate 5 solutii.
71 . Se genereaza in ordine crescatoare numerele de cate sase cifre care contin cifra 1 o singura

data, cifra 2 de cate doua ori si cifra 3 de trei ori. Se obtin, in aceasta ordine, numerele 122333, 123233,
123323, ...,333221. care din urmatoarele propozitii este adevarata?
a. Imediat dupa numarul 332312 se genereaza 332321
b. Sunt 8 numere generate prin aceasta metoda care au prima cifra 1 si ultima cifra 2.
c. Sunt 6 numere generate prin aceasta metoda care au prima cifra si a doua cifra 2.
d. Penultimul numar generat este 333122.

72 . Utilizand metoda backtracking se genereaza in ordine lexicografica toate anagramele

cuvantului caiet. Stiind ca primele 2 solutii sunt aceit si aceti, care este cuvantul generat inaintea cuvantului
tiaec?
a. teica c. ticae
b. tieac d. tiace

73 . O singura statie de servire (procesor, pompa de benzina etc) trebuie sa satisfaca cererile a n

clienti. Timpul de servire necesar fiecarui client este cunoscut in prealabil: pentru clientul i este necesar un
timp ti, 1 ≤ i ≤ n. Daca dorim sa minimizam timpul total de asteptare atunci
a. selectam intotdeauna clientul cu timpul maxim de servire din multimea de clienti ramasa
b. selectam intotdeauna clientul cu timpul minim de servire din multimea de clienti ramasa

74 . Se considera graful ponderat din imaginea alaturata.


Ordinea de selectare a muchiilor in vederea obtinerii unui arbore partial de cost minim, prin utilizarea
strategiei Greedy de tip Kruskal, este:
a. (1, 2), (2, 3), (4, 5), (6, 7), (1, 4), (4, 7)
b. (1, 2), (2, 3), (6, 7), (4, 5), (2, 5), (1, 4)
c. (5, 6), (5, 7), (3, 6), (2, 4), (3, 5), (1, 4)

75 . Managerul artistic al unui festival trebuie sa selecteze o multime cat mai ampla de spectacole

care pot fi jucate in singura sala pe care o are la dispozitie. Stiind ca i s-au propus 8 spectacole si pentru
fiecare spectacol i-a fost anuntat intervalul in care se va desfasura:
1: [10, 15)
2: [2, 4)
3: [7, 9)
4: [21, 25)
5: [10, 12)
6: [12, 15)
7: [7, 8)
8: [20, 27)
Care spectacole trebuie selectate pentru a permite spectatorilor sa vizioneze un numar cat mai mare de
spectacole?
a. 2, 3, 5, 6, 8
b. 1, 8
c. 2, 4, 5, 6, 7
d. 2, 3, 1, 8

76 . Se considera ca trebuie transportate cu ajutorul unui rucsac de capacitate 10kg, obiecte cu

greutatile 8kg, 6kg si 4kg. Pentru fiecare kg transportat castigul obtinut este 1 LEU.
Stiind ca obiectele se incarca integral in sac si ca se poate alege cel mult un obiect din fiecare tip, atunci
solutia optima este (se noteaza prin 1 - selectarea obiectului, iar prin 0 - neselectarea acestuia):
a. (1, 0, 0) c. (1, 1, 1)
b. (0, 1, 1) d. (1, 1, 0)

77 . Se doreste planificarea optimala (penalizarea totala sa fie minima) a 7 lucrari, fiecare lucrare

i fiind data prin termenul de predare t[i] si penalizarea p[i] care se plateste in cazul in care lucrarea nu este
finalizata la timp. Se presupune ca pentru executarea unei lucrari este necesara o unitate de timp si ca nu se
pot executa doua lucrari in acelasi timp.
Se considera datele de intrare:
i t[i] p[i]
1 4 50
2 3 40
3 2 60
4 3 20
5 4 70
6 2 10
7 1 130
Care este penalizarea totala minima ce se poate obtine?

a. 10 c. 20
b. 130 d. 70

78 . Fie tabloul unidimensional a in care elementele sunt, in ordine 1, 3, 5, 7, 10, 16, 21. Pentru a

verifica daca numarul x = 4 se afla printre elementele tabloului, se aplica metoda cautarii binare. Care este
succesiunea corecta de elemente cu care se compara x?
a. 1, 3, 5
b. 7, 5, 3
c. 7, 3, 5
d. 21, 16, 10, 7, 5, 3

79 . Se considera doua tablouri unidimensionale A si B: A = (1, 3, 5, 9, 10), respectiv B = (2, 4,

6, 7). In urma interclasarii lor in ordine crescatoare se obtine tabloul cu elementele:


a. (1, 2, 3, 4, 5, 6, 9, 7, 10) c. Nu se poate realiza interclasarea
b. (1, 2, 3, 4, 5, 6, 7, 9, 10) d. (1, 3, 5, 9, 10, 2, 4, 6, 7)

80 . Pentru cautarea unei valori intre elementele unui tablou ordonat descrescator vom utiliza

utiliza un algoritm eficient de tip:


a. interclasare c. cautare binara
b. quicksort d. backtracking

81 .

i) 1, 4, 6, 8, 9
Fie secventele de numere:

ii) 8, 5, 4, 3, 2, 1
iii) 2, 3, 8, 5, 9
Algoritmul de cautare binara se poate aplica direct, fara alte prelucrari prealabile
a. numai secventei i) c. numai secventei ii)
b. numai secventei iii) d. atat secventei i) cat si secventei ii)
82 . Se considera metoda sortarii prin interclasare a n siruri de caractere in ordine lexicografica

crescatoare. Presupunand ca procesul de divizare se bazeaza pe metoda injumatatirii la fiecare pas, atunci
timpul cerut de algoritm este:
a. O(n) c. O(n log2n)
b. O(n2) d. O(n ln n)

83
a.
. Pentru rezolvarea problemei Turnurilor din Hanoi se poate utiliza:

numai metoda backtracking


b. numai metoda Divide et Impera
c. numai metoda Gready
d. numai metoda eliminarii stivei
e. Atat metoda Divide et Impera cat si metoda eliminarii stivei

84 .

a. k-1 comparatii
Se considera algoritmul cautarii binare si 2k-1≤ n ≤ 2k. In cazul unei cautari cu succes se fac

c. cel mult k comparatii


b. exact k comparatii d. n comparatii

85 . Fie S(n) numarul de comparatii necesar sortarii a n siruri de caractere prin metoda insertiei

binare, Atunci S(n) este


a. c.

b. d.

86 . Se presupune ca n siruri de caractere sunt sortate prin metoda sortarii rapide (quicksort).

Notam prin T(n) numarul mediu de comparatii pentru ordonarea lexicografica crescatoare a celor n siruri.
Atunci T(n) =
a. O(n) c. O(n ln n)
b. O(n2) d. O(n log2n)

87 . Se considera functia C din biblioteca standard:

void * bsearch(const void *x, const void *s, size_t dim, size_t n, int (*f)(const void *, const void *));
Atunci:
a. f este functie de comparare definita de c. s este adresa elementului ce va fi cautat
utilizator
b. x este tabloul in care se cauta d. n este numarul de componente ale sirului
in care se face cautarea

88 . Se considera arborele binar a carui reprezentare standard (ST[i] - descendent stang, DR[i] -

descendent drept) este ST = (2, 3, 4, 0, 6, 0, 0, 0, 0) si DR = (8, 5, 0, 0, 7, 0, 0, 9, 0), unde prin 0 s-a notat
lipsa descendentului corespunzator. Atunci prin parcurgerea in inordine, nodurile arborelui sunt vizitate
astfel:
a. 1, 2, 3, 4, 5, 6, 7, 8, 9 c. 4, 3, 2, 6, 5, 7, 1, 8, 9
b. 1, 2, 8, 3, 5, 9, 4, 6, 7 d. 4, 3, 6, 7, 5, 2, 9, 8, 1

89 . Metoda Divide et impera, cu divizare binara, pentru rezolvarea unei probleme relativ la

obiectele O1, O2, ..., On, se poarte reprezenta sub forma unui arbore binar. Daca fiecare secventa Op, Op+1, ....,
Oq se reprezinta prin perechea (p, q), atunci varfurile terminale ale arborelui sunt etichetate cu:
a. (1, n)
b. (n+1, ∞)
c. (p, q) cu q = p+1
d. (p, q) cu q-p ≤ ε, unde ε este dimensiunea subproblemei ce se poate rezolva direct.
Metoda backtracking (modele de subiecte)

1. [Cuvinte]

Urmatorul enunt este comun pentru intrebarile i)- iv). “Utilizând metoda backtracking
se generează în ordine lexicografică cuvintele de câte patru litere din mulţimea
A={a,b,c,d,e}, cuvinte care nu conţin două vocale alăturate. Primele opt cuvinte
generate sunt, în ordine: abab, abac, abad, abba, abbb, abbc, abbd, abbe. “

i) Câte dintre cuvintele generate încep cu litera b şi se termină cu litera e?


a. 9 b. 15 c. 12 d. 20

ii) Care este ultimul cuvânt generat?


a. edcb b. eeee c. edde d. eded

iii) Care este penultimul cuvânt generat?


a. edec b. eded c. edde d. edcb

iv) Care este antepenultimul cuvânt generat?


a. edde b. eddb c. edeb d. edcb

v) Se generează, utilizând metoda bactracking, cuvintele cu exact 3 litere din


mulţimea {a,x,c,f,g}. Dacă primele patru cuvinte generate sunt, în ordine, aaa, aax,
aac, aaf, scrieţi ultimele trei cuvinte care încep cu litera a, în ordinea în care vor fi
generate.

vi) Se utilizează metoda backtracking pentru a genera toate cuvintele formate din
două litere distincte din muţimea {w,x,z,y} astfel încât niciun cuvânt să nu înceapă cu
litera x şi niciun cuvânt să nu conţină litera w lângă litera z. Cuvintele vor fi generate
în ordinea wx, wy, zx, zy, yw, yx, yz. Folosind aceeaşi metodă se generează toate
cuvintele de două litere distincte din mulţimea {w,x,z,y,t} astfel încât niciun cuvânt să
nu înceapă cu litera x şi niciun cuvânt să nu conţină litera w lângă litera z. Care este a
treia şi a patra soluţie generată?

2. [Combinari]

i) Folosind modelul combinărilor se generează numerele naturale cu câte trei cifre


distincte din mulţimea {1, 2, 3, 7}, numere cu cifrele în ordine strict crescătoare,
obţinându-se, în ordine: 123, 127, 137, 237. Dacă se utilizează exact aceeaşi tehnică
pentru a genera numerele naturale cu patru cifre distincte din mulţimea
{1,2,3,4,5,6,7,8}, câte dintre numerele generate au prima cifră 2 şi ultima cifră 7?
a. 8 b. 3 c. 4 d. 6

ii) Utilizând metoda backtracking se generează toate submuţimile mulţimii {3,6,2,5}.


Primele şase submulţimi generate sunt, în ordine: {3}, {3,6}, {3,6,2}, {3,6,2,5},
{3,6,5}, {3,2}. Care sunt, în ordinea obţinerii, ultimele trei submulţimi, generate după
această regulă?
iii) Folosind modelul combinărilor se generează numerele naturale cu câte trei cifre
distincte din mulţimea {1,2,3,4}, numere cu cifrele în ordine strict crescătoare,
obţinându-se, în ordine: 123, 124, 134, 234. Dacă se utilizează exact aceeaşi tehnică
pentru a genera numerele naturale cu câte patru cifre distincte din mulţimea
{1,2,3,4,5}, câte dintre numerele generate au prima cifră 1 şi ultima cifră 5?
a. 4 b. 2 c. 6 d. 3

3. [Numere cu trei cifre]

i) Utilizând metoda backtracking sunt generate numerele de 3 cifre, având toate


cifrele distincte şi cu proprietatea că cifrele aflate pe poziţii consecutive sunt de
paritate diferită. Ştiind că primele şase soluţii generate sunt, în această ordine, 103,
105, 107, 109, 123, 125, care este a zecea soluţie generată?
a. 145 b. 147 c. 230 d. 149

ii) Utilizând metoda backtracking sunt generate numerele de 3 cifre care au cifrele în
ordine crescătoare, iar cifrele aflate pe poziţii consecutive sunt de paritate diferită.
Ştiind că primele cinci soluţii generate sunt, în această ordine, 123, 125, 127, 129,
145, care este cel de al 8-lea număr generat?
a. 169 b. 149 c. 167 d. 147

iii) Utilizând metoda backtracking, sunt generate toate numerele de 3 cifre, astfel încât
cifrele sunt în ordine crescătoare, iar cifrele aflate pe poziţii consecutive sunt de
paritate diferită. Ştiind că primele trei soluţii generate sunt, în această ordine, 123,
125, 127, câte dintre toate numerele generate au suma cifrelor egală cu 6?

iv) Utilizând metoda backtracking se generează numerele formate din câte 3 cifre
distincte din mulţimea {1, 3, 5, 7}. Dacă primele trei numere generate sunt, în acestă
ordine: 135, 137, 153 care este cel de-al patrulea număr generat?
a. 157 b. 173 c. 315 d. 357

v) Folosind cifrele {1,2,3} se generează, în ordinea crescătoare a valorii, toate


numerele pare formate din trei cifre distincte. Astfel, se obţin în ordine, numerele:
132, 312. Folosind aceeaşi metodă, se generează numerele pare formate din patru
cifre distincte din mulţimea {1,2,3,4}. Care va fi al 4-lea număr generat ?
a. 2134 b. 1432 c. 2314 d. 1423

vi) Folosind cifrele {2,3,4} se generează, în ordinea crescătoare a valorii, toate


numerele impare formate din trei cifre distincte. Astfel se obţin, în ordine, numerele:
243, 423. Folosind aceeaşi metodă, se generează numerele pare formate din patru
cifre distincte din mulţimea {2,3,4,5}. Care va fi al 5-lea număr generat?
a. 3452 b. 3524 c. 2534 d. 3542

vii) Folosind cifrele {1,2,3} se generează, în ordinea crescătoare a valorii, toate


numerele formate din exact trei cifre, în care cifrele alăturate au valori consecutive.
Astfel se obţin în ordine, numerele: 121, 123, 212, 232, 321 şi 323. Folosind aceeaşi
metodă se generează numere de patru cifre din mulţimea {1, 2, 3, 4} care îndeplinesc
aceeaşi condiţie. Care va fi al 5-lea număr generat?
a. 2121 b. 2123 c. 3121 d. 2323
viii) Folosind cifrele {2,3,4} se generează, în ordinea crescătoare a valorii, toate
numerele pare formate din trei cifre distincte. Astfel se obţin, în ordine, numerele:
234, 324, 342, 432. Folosind aceeaşi metodă, se generează numerele impare formate
din patru cifre distincte din mulţimea {2,3,4,5}. Care va fi al 5-lea număr generat?
a. 3425 b. 2543 c. 4235 d. 3245

ix) Folosind cifrele {1,2,3} se generează, în ordinea crescătoare a valorii, toate


numerele impare formate din trei cifre distincte. Astfel se obţin, în ordine, numerele:
123, 213, 231, 321. Folosind aceeaşi metodă, se generează numerele impare formate
din patru cifre distincte din mulţimea {1, 2, 3, 4}. Care va fi al 5-lea număr generat ?
a. 2413 b. 1423 c. 2431 d. 3241

4. [Numere de orice lungime]

i) Folosind tehnica backtracking, in timpul pregatirii examenului, ati scris un program


care generează toate numerele de câte n cifre (0<n≤9), cifrele fiind în ordine strict
crescătoare.

a) Dacă n este egal cu 5, câte numere au fost generate de catre program? __________
b) Pentru n egal cu 5, câte dintre numerele generate au prima cifră 4? ___________

ii) Un algoritm generează în ordine crescătoare toate numerele de n cifre, folosind


doar cifrele 3, 5 şi 7. Dacă pentru n=5, primele 5 soluţii generate sunt 33333, 33335,
33337, 33353, 33355, precizaţi care sunt ultimele 3 soluţii generate, în ordinea
generării. ____________________

iii) Un algoritm generează în ordine descrescătoare toate numerele de 5 cifre, fiecare


dintre ele având cifrele în ordine strict crescătoare. Ştiind că primele 5 soluţii generate
sunt 56789, 46789, 45789, 45689, 45679, precizaţi care sunt ultimele 3 soluţii
generate, în ordinea generării. ______________________

iv) Un algoritm generează în ordine crescătoare, toate numerele de n cifre (n<9), cu


cifre distincte, care nu au două cifre pare alăturate. Dacă pentru n=5, primele 5 soluţii
generate sunt 10325, 10327, 10329, 10345, 10347, precizaţi care sunt următoarele 3
soluţii generate, în ordinea obţinerii lor. ______________________

v) Un algoritm generează în ordine descrescătoare, toate numerele de n cifre (n<9), cu


cifrele în ordine strict crescătoare, care nu au două cifre pare alăturate. Dacă pentru
n=5, primele 5 soluţii generate sunt 56789, 45789, 45679, 45678, 36789, precizaţi
care sunt următoarele 3 soluţii generate, în ordinea obţinerii lor. ______________

vi) Pentru generarea numerelor cu n cifre formate cu elementele mulţimii {0, 2, 8} se


utilizează un algoritm backtracking care, pentru n=2, generează, în ordine, numerele
20, 22, 28, 80, 82, 88. Dacă n=4 şi se utilizează acelaşi algoritm, precizaţi câte numere
generate sunt divizibile cu 100?
a. 601 b. 100 c. 6 d. 10

vii) Pentru generarea numerelor cu n cifre formate cu elementele mulţimii {0, 4, 8} se


utilizează un algoritm backtracking care, pentru n=2, generează, în ordine, numerele
40, 44, 48, 80, 84, 88. Dacă n=4 şi se utilizează acelaşi algoritm, care este numărul
generat imediat după numărul 4008 ?
a. 4040 b. 4004 c. 4080 d. 8004

viii) Un program generează, în ordine crescătoare, numerele naturale de exact 5 cifre


din mulţimea {1, 2, 3, 4, 5}. Fiecare dintre numerele generate are cifrele distincte
două câte două. Primele 3 numere astfel generate sunt: 12345, 12354, 12435. Care
este numărul generat imediat după 12543?
a. 15342 b. 12534 c. 13245 d. 13452

ix) Se generează în ordine crescătoare, toate numerele naturale de 5 cifre distincte,


care se pot forma cu cifrele 2,3,4,5 şi 6. Să se precizeze numărul generat imediat
înaintea şi numărul generat imediat după secvenţa următoare : 34256, 34265, 34526,
34562:
a. 32645 şi 34625
b. 32654 şi 34655
c. 32654 şi 34625
d. 32645 şi 34655

x) Se generează în ordine crescătoare, toate numerele naturale de 5 cifre distincte,


care se pot forma cu cifrele 5,6,7,8 şi 9. Să se precizeze numărul generat imediat
înaintea şi numărul generat imediat după secvenţa următoare :
67589,67598,67859,67895.
a. 65987 şi 67958
b. 65978 şi 67988
c. 65978 şi 67958
d. 65987 şi 67988

xi) Se generează în ordine crescătoare toate numerele de exact 4 cifre care se pot
forma cu elementele mulţimii {0, 1, 2, 3, 4}. Primele 8 soluţii generate sunt, în ordine:
1000, 1001, 1002, 1003, 1004, 1010, 1011, 1012. Care sunt primele trei numere ce se
vor genera imediat după numărul 3443?
a. 4000, 4001, 4002
b. 3444, 4443, 4444
c. 3444, 4444, 4000
d. 3444, 4000, 4001

xii) Se generează în ordine crescătoare toate numerele de 4 cifre, cu cifre distincte,


astfel încât diferenţa în valoare absolută dintre prima şi ultima, respectiv a doua şi a
treia cifră este egală cu 2. Primele 11 soluţii generate sunt, în ordine: 1023, 1203,
1243, 1423, 1463, 1573, 1643, 1683, 1753, 1793, 1863. Care dintre următoarele
numere se va genera imediat înaintea numărului 9317?
a. 9247 b. 9357 c. 9207 d. 8976

xiii) Se generează în ordine crescătoare toate numerele de 4 cifre, cu cifre distincte,


astfel încât diferenţa în valoare absolută dintre ultimele două cifre ale fiecărui număr
generat este egală cu 2. Primele opt soluţii generate sunt, în ordine: 1024, 1035, 1042,
1046, 1053, 1057, 1064, 1068. Care dintre următoarele numere se va genera imediat
după numărul 8975?
a. 8979 b. 9013 c. 8957 d. 9024
xiv) Având la dispoziţie cifrele 0, 1 şi 2 se pot genera, în ordine crescătoare, numere
care au suma cifrelor egală cu 2. Astfel, primele 6 soluţii sunt 2, 11, 20, 101, 110,
200. Folosind acelaşi algoritm, se generează numere cu cifrele 0, 1, 2 şi 3 care au
suma cifrelor egală cu 4. Care va fi al 7-lea număr din această generare?
a. 130 b. 301 c. 220 d. 103

5. [Siruri de biti]

i) Un algoritm de tip backtracking generează, în ordine lexicografică, toate şirurile de


5 cifre 0 şi 1 cu proprietatea că nu există mai mult de două cifre 0 pe poziţii
consecutive. Primele 7 soluţii generate sunt: 00100, 00101, 00110, 00111, 01001,
01010, 01011. Care este a 8-a soluţie generată de acest algoritm?
a. 01110 b. 01100 c. 01011 d. 01101

ii) Un algoritm generează, în ordine lexicografică, toate şirurile alcătuite din câte n
cifre binare (0 şi 1). Ştiind că pentru n=5, primele 4 soluţii generate sunt 00000,
00001, 00010, 00011, precizaţi care sunt ultimele 3 soluţii generate, în ordinea
obţinerii lor. _____________________

iii) Un program citeşte o valoare naturală nenulă pentru n şi apoi generează şi


afişează, în ordine crescătoare lexicografic, toate combinaţiile formate din n cifre care
aparţin mulţimii {0,1}. Astfel, pentru n=2, combinaţiile sunt afişate în următoarea
ordine: 00, 01, 10, 11. Dacă se rulează acest program şi se citeşte pentru n valoarea 9,
imediat după combinaţia 011011011 va fi afişată combinaţia:
a. 011100100 b. 011011100 c. 011011011 d. 011100000

iv) Un program citeşte o valoare naturală nenulă pentru n şi apoi generează şi


afişează, în ordine descrescătoare lexicografic, toate combinaţiile de n cifre care
aparţin mulţimii {0,1}. Astfel, pentru n=2, combinaţiile sunt afişate în următoarea
ordine: 11, 10, 01, 00. Dacă se rulează acest program şi se citeşte pentru n valoarea 8,
imediat după combinaţia 10101000 va fi afişată combinaţia:
a. 01010111 b. 10100111 c. 01010100 d. 10100100

6. [Secvente de suma data]

i) Pentru a scrie valoarea 10 ca sumă de numere prime se foloseşte metoda


backtracking şi se generează, în această ordine, sumele distincte: 2+2+2+2+2,
2+2+3+3, 2+3+5, 3+7, 5+5. Folosind exact aceeaşi metodă, se scrie valoarea 9 ca
sumă de numere prime. Care sunt primele trei soluţii, în ordinea generării lor? ______

ii) Folosind un algoritm de generare putem obţine numere naturale de k cifre care au
suma cifrelor egală cu un număr natural s. Astfel, pentru valorile k=2 şi s=6 se
generează, în ordine, numerele: 15, 24, 33, 42, 51, 60. Care va fi al treilea număr
generat pentru k=4 şi s=5?
a. 1301 b. 1022 c. 2201 d. 1031

iii) Utilizăm metoda backtracking pentru generarea tuturor modalităţilor de a scrie


numărul 9 ca sumă a cel puţin două numere naturale nenule distincte. Termenii
descompunerii sunt în ordine strict crescătoare. Soluţiile se generează în ordinea:
1+2+6, 1+3+5, 1+8, 2+3+4, 2+7, 3+6 şi 4+5. Se aplică exact aceeaşi metodă pentru
scrierea lui 12. Scrieţi, în ordinea generării, toate soluţiile de forma 2+...

iv) Utilizăm metoda backtracking pentru generarea tuturor modalităţilor de a scrie


numărul 9 ca sumă a cel puţin două numere naturale nenule distincte. Termenii
fiecărei sume sunt în ordine strict crescătoare. Soluţiile se generează în ordinea:
1+2+6, 1+3+5, 1+8, 2+3+4, 2+7, 3+6 şi 4+5. Se aplică exact aceeaşi metodă pentru
scrierea lui 8. Câte soluţii vor fi generate?
a. 3 b. 4 c. 6 d. 5

v) Utilizăm metoda backtracking pentru generarea tuturor modalităţilor de a scrie


numărul 6 ca sumă a cel puţin două numere naturale nenule. Termenii fiecărei sume
sunt în ordine crescătoare. Soluţiile se generează în ordinea: 1+1+1+1+1+1,
1+1+1+1+2, 1+1+1+3, 1+1+4, 1+5, 2+2+2, 2+4 şi 3+3. Se aplică exact aceeaşi
metodă pentru scrierea lui 9. Care este penultima soluţie?
a. 3+3+3 b. 3+6 c. 4+5 d. 2+7

vi) Utilizăm metoda backtracking pentru generarea tuturor modalităţilor de a scrie


numărul 6 ca sumă a cel puţin două numere naturale nenule. Termenii fiecărei sume
sunt în ordine crescătoare. Soluţiile se generează în ordinea: 1+1+1+1+1+1,
1+1+1+1+2, 1+1+1+3, 1+1+4, 1+5, 2+2+2, 2+4 şi 3+3. Se aplică exact aceeaşi
metodă pentru scrierea lui 9. Câte soluţii de forma 2+... vor fi generate?
a. 2 b. 3 c. 4 d. 5

vii) Utilizând metoda backtracking, se generează numerele naturale formate din exact
3 cifre şi care au suma cifrelor egală cu 4, în această ordine: 103, 112, 121, 130, 202,
211, 220, 301, 310, 400. Dacă utilizăm acelaşi algoritm pentru a genera toate
numerele de 4 cifre care au suma cifrelor egală cu 7, precizaţi care este numărul
generat imediat după 1222.
a. 1231 b. 1223 c. 1213 d. 1321

ix) Utilizând metoda backtracking pentru afişarea tuturor modalităţilor de


descompunere a unui număr natural ca o sumă de numere naturale nenule, pentru n=3
se obţin, în ordine, soluţiile: 1+1+1; 1+2; 2+1; 3. Ordinea de scriere a termenilor
dintr-o descompunere este semnificativă. Folosind aceeaşi metodă pentru n=10, care
este soluţia generată imediat după 1+1+3+5?
a. 1+1+4+1+1+1+1 b. 1+1+7+1 c. 1+2+7 d. 1+1+4+4

7. [Permutari]

i) In timpul procesului de generare a permutărilor mulţimii {1,2,…,n} prin metoda


backtracking, în tabloul unidimensional x este plasat un element x[k] (1≤k≤n). Acesta
este considerat valid dacă este îndeplinită condiţia:
a. x[k]∉{x[1], x[2], …, x[k-1]} b. x[k]≠x[k-1]
c. x[k]∉{x[1], x[2], …, x[n]} d. x[k]≠x[k-1] şi x[k]≠x[k+1]

ii) Se utilizează un algoritm pentru a genera în ordine lexicografică inversă toate


permutările mulţimii {1,2,3,4,5}. Primele patru permutări generate sunt: 54321,
54312, 54231, 54213. A cincea permutare este:
a. 53421 b. 54321 c. 54132 d. 54123
iii) Utilizând metoda backtracking se generează toate permutările mulţimii {1,2,3,4}.
Dacă primele trei permutări generate sunt, în acestă ordine: 1234, 1243, 1324
precizaţi care este permutarea generată imediat după 3412.
a. 3421 b. 3413 c. 4123 d. 3214

iv) Utilizând metoda backtracking se generează permutările cuvântului info. Dacă


primele trei soluţii generate sunt: fino, fion, fnio care este cea de-a cincea soluţie?
a. foin b. fnoi c. foni d. ifon

v) Dacă se utilizează metoda backtracking pentru a genera toate permutările de 4


obiecte şi primele 5 permutări generate sunt, în această ordine, 4 3 2 1, 4 3 1 2, 4 2 3
1, 4 2 1 3, 4 1 3 2, atunci a 6-a permutare este:
a. 3 2 1 4 b. 3 4 2 1 c. 1 4 3 2 d. 4 1 2 3

8. [Recunoasterea mecanismelor enumerative]

i) Algoritmul de generare a tuturor numerelor de 5 cifre nenule, fiecare având cifrele


ordonate strict crescător, este echivalent cu algoritmul de generare a:
a. submulţimilor unei mulţimi cu 5 elemente
b. produsului cartezian a unor mulţimi decifre
c. aranjamentelor de 9 elemente luate câte 5
d. combinărilor de 9 elemente luate câte 5

ii) Generarea tuturor cuvintelor de trei litere mici, nu neapărat distincte, ale alfabetului
englez, se poate realiza cu ajutorul unui algoritm echivalent cu cel de generare a:
a. produsului cartezian
b. combinărilor
c. aranjamentelor
d. permutărilor

iii) Pentru generarea tuturor mulţimilor de câte 5 cifre, având la dispoziţie cifrele de la
1 la 9, se poate utilza un algoritm echivalent cu algoritmul de generare a:
a. permutărilor de 5 elemente
b. submulţimilor mulţimii {1,2,3,4,5,6,7,8,9}
c. combinărilor de 9 elemente luate câte 5
d. aranjamentelor de 9 elemente luate câte 5

iv) Pentru a genera toate numerele naturale cu exact 4 cifre şi care au cifrele în ordine
strict descrescătoare, se poate utiliza un algoritm echivalent cu cel pentru generarea:
a. aranjamentelor de 4 obiecte luate câte 10
b. combinărilor de 10 obiecte luate câte 4
c. permutărilor a 10 obiecte
d. permutărilor a 4 obiecte

v) Generarea matricelor pătratice de ordinul n, cu elemente 0 şi 1, cu proprietatea că


pe fiecare linie şi pe fiecare coloană există un singur element egal cu 1, se poate
realiza utilizând metoda backtracking. Algoritmul utilizat este echivalent cu
algoritmul de generare a:
a. combinărilor
b. permutărilor
c. aranjamentelor
d. produsului cartezian

vi) Pentru rezolvarea cărei probleme dintre cele enumerate mai jos se poate utiliza
metoda backtracking ?
a. determinarea reuniunii a 3 mulţimi
b. determinarea tuturor divizorilor unui număr din 3 cifre
c. determinarea tuturor elementelor mai mici decât 30000 din şirul lui Fibonacci
d. având 3 culori (”roşu”, ”galben”, ”albastru”), determinarea tuturor variantelor în
care se pot genera toate steagurile cu 3 culori având la mijloc culoarea ”galben”

vii) La un concurs sportiv sunt 5 echipe, iar în fiecare echipă sunt câte 10 elevi.
Problema determinării tuturor grupelor de câte 5 elevi, câte unul din fiecare echipă,
este similară cu generarea tuturor:
a. elementelor produsului cartezian AxAxAxAxA, unde A={1,2,…,10}
b. submulţimilor cu 5 elemente ale mulţimii {1,2,…,10}
c. permutărilor mulţimii {1,2,3,4,5}
d. partiţiilor mulţimii {1,2,…,10}

viii) Problema generării tuturor codurilor formate din exact 4 cifre nenule, cu toate
cifrele distincte două câte două, este similară cu generarea tuturor:
a. aranjamentelor de 9 elemente luate câte 4
b. permutărilor elementelor unei mulţimi cu 4 elemente
c. elementelor produsului cartezian AxAxAxA unde A este o mulţime cu 9 elemente
d. submulţimilor cu 4 elemente ale mulţimii {1,2,3,4,5,6,7,8,9}

ix) Cu studentii unei grupe 28 de studenti se doreşte formarea unei echipă de 4


persoane. Ordinea studentilor în cadrul echipei nu are importanţă. Algoritmul de
generare a tuturor posibilităţilor de a forma o asfel de echipă este similar cu
algoritmul de generare a tuturor:
a. aranjamentelor de 28 de elemente luate câte 4
b. combinărilor de 28 de elemente luate câte 4
c. partiţiilor unei mulţimi
d. elementelor produsului cartezian AxAxAxA, A fiind o mulţime cu 28 de elemente

x) La examenul de licenta, un absolvent primeşte un test format dintr-un subiect de tip


I, unul de tip II şi unul de tip III. Stiind că pentru fiecare tip de subiect sunt elaborate
exact 50 de variante, algoritmul de generare a tuturor posibilităţilor de a forma un test
este similar cu algoritmul de generare a:
a. elementelor produsului cartezian
b. aranjamentelor
c. permutărilor
d. submulţimilor

xi) Trei studenti vor să înfiinţeze o echipa pentru a participa la un concurs de


informatica formată dintr-un programator PHP, un specialist multimedia şi un
specialist in baze de date. Toţi trei ştiu să cânte atât la PHP, cât şi la multimedia, şi se
pricep cu toţii şi la baze de date. Algoritmul de generare a tuturor posibilităţilor de a
forma echipa este similar cu algoritmul de generare a:
a. aranjamentelor
b. permutărilor
c. elementelor produsului cartezian
d. submulţimilor

9. [Siruri de caractere]

i) Generând şirurile de maximum 3 caractere distincte din mulţimea {A,B,C,D,E},


ordonate lexicografic, obţinem succesiv: A, AB, ABC, ABD,…. Ce şir va fi generat
după BAE?
a. BCA
b. CAB
c. BC
d. BEA

ii) Utilizând metoda backtracking se generează toate cuvintele de câte 3 litere din
mulţimea {a,b,c}. Dacă primele patru cuvinte generate sunt, în acestă ordine: aaa, aab,
aac, aba, care este cel de-al optulea cuvânt generat?
a. acb b. acc c. aca d. bca

iii) Se utilizează metoda backtracking pentru a genera toate cuvintele de câte patru
litere distincte din mulţimea {d,a,n,s}. Ştiind că al doilea cuvânt generat este dans, iar
al treilea este dsan, care va fi ultimul cuvânt obţinut?
a. nsad b. snad c. snda d. dans

iv) Se utilizează metoda backtracking pentru a genera toate cuvintele de câte trei litere
distincte din mulţimea {i,n,f,o}. Ştiind că ultimele trei cuvinte generate sunt, în
ordine, ion, inf şi ino, care este cel de-al doilea cuvânt obţinut?
a. ofn b. ifo c. foi d. nif

v) Se utilizează metoda backtracking pentru a genera toate cuvintele care conţin toate
literele din mulţimea {i,n,f,o}, astfel încât fiecare literă să apară exact o dat într-un
cuvânt. Ştiind că al doilea cuvânt generat este info iar al treilea este ionf, care este
ultimul cuvânt obţinut?
a. nifo b. ofni c. ofin d. foni

vi) Se utilizează metoda backtracking pentru a genera toate cuvintele care conţin toate
literele din mulţimea {i,n,f,o}, astfel încât fiecare literă să apară exact o dat într-un
cuvânt şi literele n şi o să nu se afle pe poziţii vecine. Ştiind că primul cuvânt generat
este info, iar al treilea este nifo care este cel de-al doilea cuvânt obţinut?
a. iofn b. inof c. ionf d. niof

10 [Numarare …]
i) Câte numere cu exact 3 cifre pot fi construite folosind doar cifre pare?
a. 125 b. 100 c. 64 d. 128

ii) În câte dintre permutările elementelor mulţimii {‘I’,’N’,’F’,’O’} vocalele apar pe


poziţii consecutive?
a. 24 b. 6 c. 12 d. 4
iii) Se consideră un număr natural nenul x având exact 8 cifre, distincte două câte
două; printer cifrele sale se găseşte şi cifra 0. Permutând cifrele lui x se obţin alte
numere naturale. Câte dintre numerele obţinute, inclusiv x, au exact 8 cifre? ______

iv) Utilizând metoda backtracking, se generează în ordine lexicografică toate


anagramele cuvântului caiet ( cuvinte formate din aceleaşi litere, eventual în altă
ordine). Câte cuvinte vor fi generate?
a. 60 b. 100 c. 200 d. 120

v) Se generează, prin metoda backtracking, toate partiţiile mulţimii A={1,2,3}


obţinându-se următoarele soluţii: {1}{2}{3}; {1}{2,3}; {1,3}{2}; {1,2}{3}; {1,2,3}.
Se observă că dintre acestea, prima soluţie e alcătuită din exact trei submulţimi. Dacă
se foloseşte aceeaşi metodă pentru a genera partiţiile mulţimii {1,2,3,4} stabiliţi câte
dintre soluţiile generate vor fi alcătuite din exact trei submulţimi.
a. 3 b. 12 c. 6 d. 5

vi) Se utilizează metoda backtracking pentru a genera toate submulţimile cu p


elemente ale unei mulţimi cu m elemente. Dacă m=7 şi p=4 atunci numărul de
submulţimi generate este:
a. 60 b. 35 c. 5 d. 15

vii) Prin metoda backtracking se generează toate anagramele (cuvintele obţinute prin
permutarea literelor) unui cuvânt dat. Ştiind că se aplică această metodă pentru
cuvântul pescar, precizaţi câte cuvinte se vor genera astfel încât prima şi ultima literă
din fiecare cuvânt generat să fie vocală (sunt considerate vocale caracterele a, e, i , o,
u)?
a. 96 b. 24 c. 48 d. 12

viii) În câte dintre permutările elementelor mulţimii {‘I’,’N’,’F’,’O’} vocala ‘I’ apare
pe prima poziţie?
a. 1 b. 24 c. 6 d. 12

11. [Probleme diverse]

i) Un program citeşte o valoare naturală nenulă impară pentru n şi apoi generează şi


afişează în ordine crescătoare lexicografic toate combinaţiile formate din n cifre care
îndeplinesc următoarele proprietăţi:
- conţin doar valori pozitive sau nule;
- încep şi se termină cu 0;
- modulul diferenţei între oricare două cifre alăturate dintr-o combinaţie este 1.
Astfel, pentru n=5, combinaţiile afişate sunt, în ordine, următoarele: 01010, 01210.
Dacă se executa acest program şi se citeşte pentru n valoarea 7, imediat după
combinaţia 0101210 va fi afişată combinaţia:
a. 0121210 b. 0123210 c. 0111210 d. 0121010

ii) Pentru generarea numerelor cu n cifre formate cu elementele mulţimii {0, 2, 8} se


utilizează un algoritm backtracking care, pentru n=2, generează, în ordine, numerele
20,22,28,80,82,88. Dacă n=4 şi se utilizează acelaşi algoritm, care este numărul
generat imediat după numărul 2008 ?
a. 2002 b. 2020 c. 2080 d. 8002
iii) Având la dispoziţie cifrele 0, 1 şi 2 putem genera, în ordine crescătoare, numere
care au suma cifrelor egală cu 2 astfel încât primele 6 numere generate sunt, în această
ordine: 2, 11, 20, 101, 110, 200. Folosind acelaşi algoritm se generează numere cu
cifrele 0, 1, 2 şi 3 care au suma cifrelor egală cu 4. Care va fi al 7-lea număr din
această generare ?
a. 103 b. 301 c. 220 d. 130

iv) Completarea unui bilet de LOTO presupune colorarea a 6 numere dintre cele 49,
înscrise pe bilet. O situaţie statistică pe o anumită perioadă de timp arată că cele mai
frecvente numere care au fost extrase la LOTO sunt: 2, 20, 18, 38, 36, 42, 46, 48. Câte
bilete de 6 numere se pot completa folosind doar aceste valori, ştiind că numărul 42
va fi colorat pe fiecare bilet?
a. 21 b. 6! c. 42 d. 56

v) Se generează prin metoda backtracking mulţimile distincte ale căror elemente sunt
numere naturale nenule şi care au proprietatea că suma elementelor fiecărei mulţimi
este egală cu 7. Astfel, sunt generate, în această ordine, mulţimile: {1, 2, 4}, {1, 6},
{2, 5}, {3, 4}, {7}. Folosind aceeaşi metodă pentru a genera mulţimile distincte ale
căror elemente sunt numere naturale nenule şi care au proprietatea că suma
elementelor fiecărei mulţimi este egală cu 9, stabiliţi în ce ordine sunt generate
următoarele mulţimi: M1={2, 3, 4}; M2={3, 6}; M3={2, 7}; M4={4, 5}.

vi) Se generează în ordine strict crescătoare numerele de câte şase cifre care conţin:
cifra 1 o singură dată, cifra 2 de două ori şi cifra 3 de trei ori. Se obţin, în această
ordine, numerele: 122333, 123233, 123323, …, 333221. Câte numere generate prin
această metodă au prima cifră 1 şi ultima cifră 2? ________________

vii) Se generează în ordine strict crescătoare toate numerele de câte şase cifre care
conţin: cifra 1 o singură dată, cifra 2 de două ori şi cifra 3 de trei ori. Se obţin, în
această ordine, numerele: 122333, 123233, 123323, …, 333221. Ce număr se
generează imediat după 332312?

viii) Utilizând metoda backtracking se generează în ordine lexicografică toate


anagramele cuvântului caiet ( cuvinte formate din aceleaşi litere, eventual în altă
ordine). Care este a şasea soluţie?
a. catei
b. actie
c. actei
d. catie

ix) Utilizând metoda backtracking se generează toate matricele pătratice de ordinul 4


ale căror elemente aparţin mulţimii {0,1}, cu proprietatea că pe fiecare linie şi pe
fiecare coloană există o singură valoare 1. Primele 3 soluţii generate sunt, în această
ordine:

1000
0100
0010
0001
1000
0100
0001
0010

1000
0010
0100
0001
Care este penultima soluţie?
a.
0001
0010
1000
0100
b.
0100
1000
0010
0001
c.
0001
0100
0010
1000
d.
0010
1000
0100
0001

x) Se generează, prin metoda backtracking, toate modalităţile de aşezare a numerelor


naturale de la 1 la 5, astfel încât oricare 2 numere consecutive să nu se afle pe poziţii
alăturate. Dacă primele două soluţii sunt: (1,3,5,2,4) şi (1,4,2,5,3), care este prima
soluţie generată în care primul număr este 4?
a. (4, 1, 3, 2, 5) b. (4, 2, 5, 1, 3) c. (4, 3, 5, 3, 1) d. (4, 1, 3, 5, 2)

xi) Se generează, prin metoda backtracking, toate modalităţile de aşezare a numerelor


naturale de la 1 la 5 astfel încât oricare două numere consecutive să nu se afle pe
poziţii alăturate. Dacă primele două soluţii sunt: (1,3,5,2,4) şi (1,4,2,5,3), care este
prima soluţie generată care începe cu 2?
a. (2, 4, 1, 3, 5) b. (2, 5, 4, 3, 1) c. (2, 4, 1, 3, 1) d. (2, 3, 5, 4, 1)

xii) Construim anagramele unui cuvânt c1c2c3c4 prin generarea în ordine lexicografică
a permutărilor indicilor literelor cuvântului şi obţinem c1c2c3c4 c1c2c4c3 c1c3c2c4 …
c4c3c1c2 c4c3c2c1. Pentru anagramele cuvântului rateu, după şirul raetu, raeut, raute
cuvintele imediat următoare sunt:
a. rauet şi rtaeu
b. rtaeu şi rtaue
c. rauet şi rtaue
d. rtaeu şi ratue

xiii) Trei baieti, Alin, Bogdan şi Ciprian, şi trei fete, Delia, Elena şi Felicia, trebuie să
formeze o echipă de 3 studenti, care să participe la un concurs de prezentare software.
Echipa trebuie să fie mixtă (adică să conţină cel puţin o fată şi cel puţin un băiat).
Ordinea copiilor în echipă este importantă deoarece aceasta va fi ordinea de intrare a
studentilor în concurs (de exemplu echipa Alin, Bogdan, Delia este diferită de echipa
Bogdan, Alin, Delia). Câte echipe se pot forma, astfel încât din ele să facă parte
simultan Alin şi Bogdan? _______________

xiv) În vederea participării la un concurs de programare, studentii din anii 2 si 3 au


dat o probă de selecţie, în urma căreia primii 6 au obţinut punctaje egale. În câte
moduri poate fi formată echipa selecţionată ştiind că poate avea doar 4 membri, aleşi
dintre cei 6, şi că ordinea acestora în cadrul echipei nu contează?
a. 24 b. 30 c. 15 d. 4

xv) Un program construieşte şi afişează elementele produsului cartezian AxBxC


pentru mulţimile A={1,2,3,4}, B={1,2,3}, C={1,2}. Care dintre următoarele triplete
NU va fi afişat?
a. (3,2,1) b. (1,3,2) c. (1,2,3) d. (2,2,2)
Probleme diverse
(recursivitate, greedy, divide et impera, programare dinamica)
1. [Permutari] Sa se genereze permutarile multimii {1, 2, …, n}, cu n fixat. Se vor
considera algoritmi iterative si recursive.
2. [Recursivitate] Se considera o fotografie specificata printr-o matrice patratica care
contine ‘0’ si ‘1’ (0 – pentru puncte albe si 1 – pentru puncte negre). Se considera
fondul alb, obiectele negre, iar daca punctele negre sint vecine pe linie, coloana sau
diagonala atunci apartin aceluiasi obiect. Sa se numere cite obiecte distincte apar in
fotografie.
3. Sa se plaseze pe o tabla de sah (cu n randuri si n coloane) n dame astfel incit san u
se atace reciproc. Sa se genereze toate solutiile.
4. Sa se genereze toate partitiile multimii {1, 2, …, n} cu n fixat.
5. Se dau n tipuri de monezi. Sa se plateasca o suma data s, folosind un numar minim
de monezi din tipurile date. Se considera ca exista un numar sufficient de monezi din
fiecare tip.
6. Se considera trei tije, pe prima dintre ele fiind asezate n discuri de dimensiuni
diferite, astfel incit cel mai mare este jos, si, peste el, in ordine descrescatoare a
dimensiunii, celelalte discuri. Discurile pot fi mutate de pe o tija pe alta, luind
intotdeauna un disc care nu are deasupra sa alte discuri si avind grija ca niciodata sa
nu fie asezat un disc mai mare peste unul mai mic. Sa se scrie un program care
genereaza o secventa de mutari prin care discurile sint mutate de pe tija 1 pe tija 2.
7. Se considera un numar natural n (3<n<15). Sa se genereze cel mai scurt sir format
din 0 si 1 de lungime 2n+n-1, astfel incit oricare ar fi doua secvente de lungime n ale
acestui sir, acestea sa fie diferite.
8. Se considera o stiva de case de bani sub forma de piramida. Astfel, pe primul nivel
vor fi n case, pe urmatorul n-1 case, apoi n-2, …, pina la ultimul nivel unde va fi o
singura casa de bani. Deci, fiecare casa se va sprijini pe doua case de bani din etajul
anterior, mai putin cele din etajul de baza care se vor sprijini direct pe podea. Se dau
n(n+1)/2 saci cu bani, fiecare continind o anumita suma. Sa se distribuie acesti saci,
fiecare intr-o casa, astfel incit in fiecare casa sa fie exact atitia bani cit in cele doua
cutii pe care se sprijina la un loc. Pentru casele din stratul de baza nu exista nicio
conditie. Ca date de intrare se utilizeaza: n (numarul de cutii din stratul de baza) si s1,
s2, …., unde si reprezinta suma de bani din sacul i (i= 1, 2, …, n(n+1)/2).
9. Se dau doua siruri de caractere. Sa se determine daca ele sint anagrame, adica sint
formate din aceleasi litere, dar puse in alta ordine.
10. Se da o lista cu n cuvinte. Sa se formeze un cel mai lung sir in care fiecare cuvint
incepe cu litera cu care se termina predecesorul sau (mai putin primul cuvint care
poate fi oricare).
11. Intr-un grup de n personae se precizeaza perechi de personae care se considera
prietene. Folosind principiul ca “prietenul prietenului meuimi este prieten” sa se
determine grupurile cu numar maxim de persoane intre care se pot stabili relatii de
prietenie directe sau indirecte.
12. Intr-o statiune de odihna s-au intilnit n sahisti, codificati prin numare naturale de
la 1 la n, care au jucat p partied de sah. Pentru fiecare partida se precizeaza cei doi
jucatori. Sa se determine un grup cit mai mare de jucatori, care prin jocurile pe care
le-au desfasurat, au format o grupare de tip turneu (in care fiecare a jucat cu fiecare).
13. Reteaua de distribuire a apei calde pentru o centrala termica zonala este formata
dintr-un sistem de conducte care leaga centrala de blocuri si blocurile intre ele.
Blocurile din retea sunt numerotate cu numere intregi de la 1 la n, centrala fiind
etichetata cu 0. Se cunosc distantele de la centrala la blocuri precum si distantele
dintre blocuri. Sa se determine perechile de numere desemnand punctele de distribuire
intre care trebuie sa se monteze conducte astfel incat fiecare bloc sa fie alimentat cu
apa calda (nu neaparat direct de la centrala) si lungimea totala a conductelor necesare
sa fie minima.
14. Un student doreste sa calatoreasca din localitatea X in localitatea Y. Daca in tara
exista n localitati si timpul necesar pentru a ajunge dintr-o localitate in alta (in cazul
in care se poate ajunge direct) se cere sa se determine timpul minim in care studentul
poate sa ajunga din X in Y.
15. Fiind date n orase si costurile tuturor drumurilor directe care exista intre orase, sa
se afle costul minim pentru a ajunge dint-un oras in oricare altul.
16. Fie un arbore cu n varfuri. In fiecare nod al arborelui se afla cate un bec. Prin
atingerea unui bec acesta isi schimba starea (din stins in aprins si invers), la fel si
vecinii lui directi. Considerand ca initial toate becurile sunt stinse, sa se scrie un
program care genereaza o secventa de “atingeri” prin care pomul este aprins complet.
Datele de intrare se citesc din fisierul craciun.in cu formatul: pe prima linie numarul
de noduri, sip e urmatoarele n-1 linii se afla muchiile arborelui.
17. Sa presupunem, ca un graf neorientat este format din n varfuri si m arce. Se stie ca
sunt folosite toate muchiile pentru a lega varfuri si ca doua varfuri pot fi legate direct
prin cel mult o muchie. Sa se determine valoarea maxima a expresiei S = suma
patratelor gradelor varfurilor, unde gradul unui varf este reprezentat de numarul de
muchii legate de acesta.
18. Un om doreste sa urce o scara cu N trepte (N dat initial). El poate urca una sau
doua trepte la un moment. Fiind o fire curioasa, acest om isi pune problema in cate
moduri poate urca aceasta scara.
19. Se da un sir de numere a[0], a[1], …, a[n-1], cu n dat initial. Sa se afle un cel mai
lung subsir crescator.
20. Se dau doua siruri a[0], a[1], …, a[n-1] si b[0], b[1], …, b[m-1]. Sa se gaseasca un
cel mai lung subsir comun al acestora.
21. Se considera o tabla de sah, de dimensiune standard 8x8, pe care se afla diverse
piese de sah. Un cal se afla pe pozitia (i,j). Se doreste mutarea calului in pozitia (k,l) –
daca acest lucru este posibil – folosind oricare dintre cele 8 mutari posibile ale calului
in orice succesiune, astfel incat numarul de mutari sa fie minim.
22. Se considera o pereche de numere de forma (a, b) asupra careia se pot efectua
urmatoarele operatii: (a, b) -> (a-b, b); (a, b) -> (a+b, b), (a, b) -> (b, a). Fiind date
valorile (a, b) sa se determine numarul minim de operatii pe care trebuie sa le facem
astfel incat sa se ajunga la perechea (c, d), daca acest lucru este posibil.
23. Se considera o bara de lungime n (n >0, n<40001). Se doreste taierea acesteia in
bucati de dimensiuni prestabilite. Se dau m astfel de dimensiuni in care putem taia
bara. Problema consta in ataia asemenea bucati din bara, de dimensiuni date, astfel
incat sa ramana cat mai putin material care nu mai poate fi folosit (nu corespunde nici
unei dimensiuni).
24. Inmultirea optimala a matricelor si rezolvarea recurentei f(n)=7f(n/2)+18(n/2)2 [1,
pag. 216]
25. Problema colorarii hartilor [1, pag. 219].
26. Problema comis-voiajorului [1, pag. 220]
27. Generarea obiectelor combinatoriale [1, pag. 221]
28. Problema rucsacului [1, pag. 225]
29. Executarea lucrarilor cu termen final [1, pag. 226]
30. Prelucrarea expresiilor cu ajutorul structurilor arborescente [1, 190]

Potrebbero piacerti anche