Documente Academic
Documente Profesional
Documente Cultură
INFORMATICĂ INTENSIV
(filiera teoretică, profilul real, specializarea
matematică-informatică, intensiv informatică)
Referenţi ştiinţifici:
Cuprins
3.3. Fractali…………………………………………………………………………………………. 75
3.3.1. Elemente de grafică………………………………………………………………… 75
3.3.1.1. Generalităţi (varianta Pascal).......................................................... 75
3.3.1.2. Generalităţi (varianta C++).............................................................. 77
3.3.1.3. Setarea culorilor şi procesul de desenare (Pascal şi C++)............ 78
3.3.2. Curba lui Koch pentru un triunghi echilateral..…………………………………... 80
3.3.3. Curba lui Koch pentru un pătrat..…………………………………………………. 83
3.3.4. Arborele…………………………..………………………………………………….. 85
Probleme propuse………………………………………………………………………………….. 87
Răspunsuri………………………………………………………………………………………….. 88
Capitolul 1
1.1. Generalităţi
După cum ştiţi, fiecărui program i se alocă trei zone distincte în memoria
internă, zone în care se găsesc memorate variabilele programului. În acest capitol,
vom învăţa să alocăm variabile în Heap. De asemenea, vom învăţa să accesăm
conţinuturile variabilelor, atât cele din segmentul de date, cât şi cele din Heap,
pornind de la adresa lor din memorie.
segment de date
segment de stivă
Heap
Definiţia 1.1. Prin pointer înţelegem adresa unei variabile, iar printr-o
variabilă de tip pointer vom înţelege o variabilă care poate reţine adresele
altor variabile.
⇒ Ele nu pot fi citite, nu pot fi tipărite în mod direct şi, cu o excepţie, conţinutul
lor nu poate fi modificat în urma unor operaţii aritmetice (de exemplu, nu
putem incrementa o astfel de variabilă).
Limbajul Pascal face distincţie între natura adreselor care pot fi memorate.
Astfel, există adrese ale variabilelor de tip integer (formând un tip identificat prin
sintagma uzuală “pointer către variabile de tip integer”), adrese ale variabilelor
de tip real (”pointer către variabile de tip real”), adrese ale variabilelor de tip
string (”pointer către variabile de tip string”). Din acest motiv, tipul pointer
este variat.
1
În ultimele versiuni ale celor două limbaje, memoria din segmentul de date nu mai este
limitată, ci se poate folosi întreaga memorie disponibilă. Din acest punct de vedere, dacă se
folosesc aceste versiuni, avantajul "memoriei în plus" nu mai poate fi luat în considerare.
Manual de informatică pentru clasa a XI-a 9
type nume=^tip.
3. Variabile de tip pointer către variabile de tip inreg. Tipul inreg este tip
record. Variabila adr_inr, poate reţine adrese ale variabilelor de
tipul inreg.
type inreg=record
nume:string[10];
prenume:string[10];
varsta:byte;
end;
type adr_inreg=^inreg;
var adr_inr:adr_inreg;
Observaţii
Între variabilele de tip pointer sunt permise atribuiri doar în cazul în care au
acelaşi tip pointer (reţin adrese către acelaşi tip de variabile).
Exemplu:
adr1, adr2:^integer;
adr3: ^real;
Chiar şi în cazul în care avem tipurile identice, dar descrise diferit, atribuirea
nu este posibilă. Atribuirea din exemplul de mai jos este eronată.
Exemplu:
type adrese=^integer;
var adr1:adrese;
adr2:^integer;
...
adr2:=adr1;
10 Capitolul 1. Alocarea dinamică a memoriei
type adrint=^integer;
var a:adrint;
x:integer;
begin
x:=3;
a:=@x;
writeln(a^);
end.
type adrInreg=^inreg;
Inreg=record
nume:string;
varsta:integer;
end;
var v:Inreg;
adresa:adrInreg;
begin
v.nume:='Popescu';
v.varsta:=17;
adresa:=@v;
writeln(adresa^.nume,' ', adresa^.varsta);
end.
Manual de informatică pentru clasa a XI-a 11
Observaţii
Limbajul C++ face distincţie între natura adreselor care pot fi memorate.
Astfel, există adrese ale variabilelor de tip int, adrese ale variabilelor de tip
float, adrese ale variabilelor de tip char, etc. Din acest motiv şi tipul variabilelor
de tip pointer este diferit.
tip *nume.
float* adresa;
12 Capitolul 1. Alocarea dinamică a memoriei
3. Variabile de tip pointer către variabile de tip elev, care la rândul lor
sunt de tip struct. Variabilele a şi b, pot reţine adrese ale variabilelor de
tipul elev.
struct elev
{ char nume[20], prenume[20];
float nota_mate, nota_info;
int varsta;
};
elev *a,*b;
Observaţii
Caracterul “*“ poate fi aşezat în mai multe feluri, după cum se observă:
int* adr1; int * adr1; int *adr1;
Pentru a declara mai multe variabile de acest tip, caracterul “*“ se trece de
fiecare dată:
int *adr1, *adr2, *adr3;
⇒ Fiind dată o variabilă de tip pointer către variabile de un anume tip, care
memorează o adresă a unei variabile de acel tip, pentru a obţine conţinutul
variabilei a cărei adresă este memorată, se utilizează operatorul unar “*“,
numit şi operator de dereferenţiere.
2. Variabila a de tip elev este iniţializată, iar variabila adra, de tip pointer
către variabile de tip elev este iniţializată cu adresa variabilei a. Secvenţa
următoare tipăreşte conţinutul variabilei a:
Manual de informatică pentru clasa a XI-a 13
...
struct elev
{
char nume[20], prenume[20];
};
...
elev a, *adra=&a;
strcpy(a.nume,"Bojian");
strcpy(a.prenume, "Andronache");
cout<<(*adra).nume<<" "<<(*adra).prenume<<endl;
cout<<adra->nume<<" "<<adra->prenume;
Între variabile de tip pointer sunt permise atribuiri doar în cazul în care au
acelaşi tip pointer (reţin adrese către acelaşi tip de variabile).
Exemplu:
int *adr1, *adr2;
float *adr3;
... // initializari
Atunci când variabila respectivă nu mai este utilă, spaţiul din memorie este
eliberat, pentru a fi rezervat, dacă este cazul, pentru alte variabile.
Adresa variabilei
Variabila alocată dinamic
alocată dinamic
1
Dacă nu există spaţiu în HEAP, P reţine nil (nici o valoare). În practică, întotdeauna se face testul
existenţei spaţiului. Din motive didactice, pentru a nu complica programele, în exemplele pe care le vom
da nu vom testa existenţa spaţiului în HEAP.
Manual de informatică pentru clasa a XI-a 15
⇒ Fiind dată o variabilă de tip pointer către variabile de un anumit tip, pentru a
accesa conţinutul variabilei a cărei adresă este memorată, se utilizează
numele variabilei de tip pointer urmat de operatorul “^“.
3. Variabile de tip pointer către variabile de tip inreg, care la rândul lor,
sunt de tip record. Variabila adr_inr, poate reţine adrese ale
variabilelor de tipul inreg.
type inreg=record
nume:string[10];
prenume:string[10];
varsta:byte;
end;
adr_inreg=^inreg;
var adr:adr_inreg;
begin
readln(adr^.nume);
readln(adr^.prenume);
readln(adr^.varsta);
writeln(adr^.nume);
writeln(adr^.prenume);
writeln(adr^.varsta);
...
16 Capitolul 1. Alocarea dinamică a memoriei
5. Este cunoscut faptul că funcţiile nu pot întoarce decât tipuri simple. Prin
urmare, o funcţie nu poate întoarce o matrice care este descrisă de un tip
structurat. Dar tipul pointer este simplu. Aceasta înseamnă că o funcţie
poate întoarce un pointer. În cazul în care avem un pointer către o matrice
(reţinută în HEAP) se poate spune, prin abuz de limbaj, că o funcţie
întoarce o matrice. Programul următor citeşte două matrice şi afişează
suma lor. Matricele sunt rezervate în HEAP.
type matrice=array[1..10,1..10]of integer;
adr_mat=^matrice;
var adr1,adr2,adr3:adr_mat;
m,n:integer;
function Cit_Mat(m,n:integer):adr_mat;
var i,j:integer;
adr:adr_mat;
begin
new(adr);
for i:=1 to m do
for j:=1 to n do readln(adr^[i,j]);
Cit_Mat:=adr;
end;
function Suma_Mat(adr1,adr2:adr_mat):adr_mat;
var i,j:integer;
adr:Adr_mat;
begin
new(adr);
for i:=1 to m do
for j:=1 to n do adr^[i,j]:=adr1^[i,j]+adr2^[i,j];
Suma_Mat:=adr;
end;
Manual de informatică pentru clasa a XI-a 17
procedure Tip_Mat(adr:adr_mat);
var i,j:integer;
begin
for i:=1 to m do
begin
for j:=1 to n do write(adr^[i,j]:4);
writeln;
end
end;
begin
write('m='); readln(m);
write('n='); readln(n);
adr1:=Cit_Mat(m,n); adr2:=Cit_Mat(m,n);
adr3:=Suma_Mat(adr1,adr2);
Tip_Mat(adr3);
end.
1. Variabile de tip pointer către variabile de tip int. Variabila adr1 poate
reţine adrese ale variabilelor de tip int.
...
int* adr1;
adr1=new int; // aloc spatiu in HEAP pentru o var. de tip int
*adr1=7; //variabila alocata retine 7
cout<<*adr1; // tiparesc continutul variabilei
delete adr1; // eliberez spatiul
1
Dacă nu există spaţiu în HEAP, P reţine 0 (nici o valoare). În practică, întotdeauna se face testul
existenţei spaţiului. Din motive didactice, pentru a nu complica programele, în exemplele pe care le vom
da nu vom testa existenţa spaţiului în HEAP.
18 Capitolul 1. Alocarea dinamică a memoriei
3. Variabile de tip pointer către variabile de tip inreg, care la rândul lor,
sunt de tip struct. Variabila adr_inr, poate reţine adrese ale
variabilelor de tipul inreg.
#include <iostream.h>
struct inreg
{ char nume[20], prenume[20];
int varsta;
};
main()
{ inreg* adr;
adr=new inreg;
cin>>adr->nume>>adr->prenume>>adr->varsta;
cout<<adr->nume<<endl<<adr->prenume<<endl<<adr->varsta;
}
Exemple:
- variabila de tip pointer, numită p, care poate reţine pe A, se declară:
float (*p)[4][2];
- variabila de tip pointer, numită q, care poate reţine pe b, se declară:
long (*q)[7][8][5];
⇒ Întrucât numele unui masiv p dimensional este pointer către un masiv p-1
dimensional, pentru a aloca dinamic un masiv se va utiliza un pointer către
masive p-1 dimensionale (ultimele p-1 dimensiuni).
Aplicaţia 1.1. Programul următor citeşte şi afişează o matrice. Nou este faptul
că matricea este alocată în HEAP.
#include <iostream.h>
main()
{ int m,n,i,j,(*adr)[10];
adr=new int[10][10];
cout<<"m="; cin>>m;
cout<<"n="; cin>>n;
for(i=0;i<m;i++)
for (j=0;j<n;j++)
cin>>adr[i][j];
for(i=0;i<m;i++)
{ for (j=0;j<n;j++) cout<<adr[i][j]<<" ";
cout<<endl;
}
}
Programul următor citeşte două matrice şi afişează suma lor. Matricele sunt
alocate în HEAP.
#include <iostream.h>
void* Cit_Mat(int m, int n)
{ int i,j,(*adr1)[10]=new int[10][10];
for(i=0;i<m;i++)
for (j=0;j<n;j++) cin>>adr1[i][j];
return adr1;
}
void Tip_Mat( int m,int n,int(*adr1)[10])
{ int i,j;
for(i=0;i<m;i++)
{ for (j=0;j<n;j++) cout<<adr1[i][j]<<" ";
cout<<endl; }
}
void* Suma_Mat( int m, int n,int(*adr1)[10],int(*adr2)[10])
{ int i,j,(*adr)[10]=new int[10][10];
for (i=0;i<m;i++)
for (j=0;j<n;j++)
adr[i][j]=adr1[i][j]+adr2[i][j];
return adr;
}
Manual de informatică pentru clasa a XI-a 21
main()
{ int m,n,i,j,(*adr)[10],(*adr1)[10],(*adr2)[10];
cout<<"m="; cin>>m;
cout<<"n="; cin>>n;
adr1=(int(*)[10])Cit_Mat(m,n);
adr2=(int(*)[10])Cit_Mat(m,n);
adr=(int(*)[10])Suma_Mat(m,n,adr1,adr2);
Tip_Mat(m,n,adr);
}
Probleme propuse
1. O variabilă de tip pointer către tipul integer/int poate memora:
a) un număr întreg;
b) conţinutul unei variabile de tipul integer/int;
c) adresa unei variabile de tipul integer/int.
a) de a crea o adresă;
b) de a aloca spaţiu pentru o variabilă la o adresă dată;
c) de a aloca spaţiu în HEAP pentru o variabilă de tip pointer;
d) de a aloca spaţiu în HEAP pentru o variabilă de un tip oarecare.
22 Capitolul 1. Alocarea dinamică a memoriei
a) 4; b) 3; c) Eroare de sintaxă.
a) 7 7; b) 8 8; c) 7 8; d) 8 7.
Răspunsuri
1. c); 2. d); 3. a); 4. d); 5. c); 6. b); 7. d).
23
Capitolul 2
Liste liniare
Definiţia 2.1. O listă liniară este o colecţie de n≥0 noduri, X1, X2, ..., Xn
aflate într-o relaţie de ordine. Astfel, X1 este primul nod al listei, X2 este
al doilea nod al listei, ..., Xn este ultimul nod. Operaţiile permise sunt:
- accesul la oricare nod al listei în scopul citirii sau modificării
informaţiei conţinute de acesta;
- adăugarea unui nod, indiferent de poziţia pe care o ocupă în listă;
- ştergerea unui nod, indiferent de poziţia pe care o ocupă în listă;
- schimbarea poziţiei unui nod în cadrul listei.
⇒ Un vector poate fi privit ca o listă liniară. Oricare din operaţiile de mai sus se
poate efectua şi pe un vector. Astfel, relaţia de ordine dintre elementele listei
este cea a componentelor vectorului. Accesul la un nod Xi este imediat
pentru că se accesează componenta i a vectorului. Adăugarea unui nod se
face mai greu, pentru că nodurile care îi urmează în listă trebuie deplasate
către dreapta, pentru a face loc noului nod. Ştergerea unui nod necesită, de
asemenea, efort de calcul, pentru că nodurile care urmează trebuie
deplasate către stânga, pentru a ocupa spaţiul lăsat liber de nodul şters. Tot
aşa, schimbarea poziţiei unui nod necesită efort de calcul. Puteţi arăta,
pentru această situaţie, în ce constă efortul de calcul?
⇒ Dacă o listă este memorată cu ajutorul unui vector, spunem că lista este
alocată secvenţial.
Din câte observaţi, majoritatea operaţiilor permise asupra unei liste liniare
alocate secvenţial necesită un efort mare de calcul. Din acest motiv, s-a
simţit nevoia unei alte modalităţi de alocare a unei liste liniare, şi anume
1
alocarea înlănţuită .
1
A nu se face confuzie între modul de alocare a unei structuri de date, în cazul de faţă liste
liniare şi structura propriu-zisă. Aceeaşi structură, în exemplu lista, poate fi alocată
secvenţial sau înlănţuit, dar structura, conform definiţiei ei, rămâne aceeaşi.
24 Capitolul 2. Liste liniare
in1, in2, ..., inn reprezintă informaţiile conţinute de noduri, de altă natură
decât cele de adresă;
2
Un exemplu de alocare statică a listelor liniare simplu înlănţuite îl veţi întâlni atunci când
veţi studia teoria grafurilor.
Manual de informatică pentru clasa a XI-a 25
În cazul listelor, prin acel pointer se poate accesa numai primul element al
listei. Apoi, pornind de la acesta se poate accesa al doilea element al listei,
ş.a.m.d.
Crearea listelor
Presupunem că, la un moment dat, lista este cea de mai jos, iar v reţine
adresa primului element (adr1):
3 adr2 7 adr3 9 0
v
adr1 adr2 adrn
Dacă se citeşte un nou număr (de exemplu 4), atunci acesta se adaugă
într-o înregistrare aflată la începutul listei, în următoarele etape:
3 adr2 7 adr3 9 0
v
adr1 adr2 adrn
4 adr1
adrn+1
3 adr2 7 adr3 9 0
adrn+1
este greşită. Câmpul adr_urm face parte din tipul Nod şi este definit ca
pointer către acelaşi tip (Nod). Convenţia de limbaj este să se declare pe
rând cele două tipuri, ca în program.
Problema 2.1. Fiind dată o listă liniară simplu înlănţuită, cu adresa de început v,
se cere să se inverseze legăturile din listă, adică dacă în lista iniţială, după nodul i
urmează nodul i+1, atunci, în noua listă, după nodul i+1, urmează nodul i.
Rezolvare. Funcţia inv, rezolvă problema dată. Ea are doi parametri: adresa
primului element al listei (pred) si adresa următorului element din listă (curent).
Practic, la fiecare pas, partea de adresă a nodului referit de curent va reţine adresa
referită de pred (adică adresa nodului precedent). Funcţia returnează adresa primului
nod al liste inversate (adică a ultimului nod în cazul listei neinversate). Înainte de
apelul funcţiei, partea de adresă a primului nod listei va trebui să reţină nil/0.
Orice listă va fi reţinută prin două informaţii de adresă: a primului nod (v) şi a
ultimului nod (sf). Precizăm faptul că, în general, numai prima informaţie este
indispensabilă. Pentru simplitate şi pentru rapiditatea executării vom reţine şi
adresa ultimului nod. Structura unui nod al listei este:
Manual de informatică pentru clasa a XI-a 29
3 0
v sf
Fie lista:
3 adr2 7 adr3 9 0
v sf
3 7 9 0 6 0
v sf c
Câmpul de adresă al ultimului nod, cel care are adresa în sf, va reţine adresa
nodului nou creat, după care şi sf va reţine aceeaşi valoare.
3 7 9 6 0
v sf
30 Capitolul 2. Liste liniare
Fie lista din figura anterioară. Dorim să adăugăm după nodul cu informaţia 3,
un altul, cu informaţia 5.
3 7 9 6 0
v sf
3 7 6 0
9
v sf
5
Manual de informatică pentru clasa a XI-a 31
Un caz aparte apare atunci când nodul de informaţie val este ultimul în
listă. În acest caz sf va reţine adresa nodului nou creat pentru că acesta va
fi ultimul.
3 5 7 9 0
3 7 9 0
v 3 5 7 9 0
v 3 5 7 9 0
v 5 7 9 0
else else
begin { c=v;
c:=v; while (c->adr_urm->info
while c^.adr_urm^.info<>val !=val) c=c->adr_urm;
do c:=c^.adr_urm; man=c->adr_urm;
man:=c^.adr_urm; c->adr_urm=man->adr_urm;
c^.adr_urm:=man^.adr_urm; if (man==sf) sf=c;
if man=sf then sf:=c; }
end; delete man;
dispose(man); }
end;
Problema se reduce la inserţia unui număr într-o listă deja sortată. Mai întâi
se alocă spaţiu în HEAP pentru o valoare, apoi aceasta este citită. Se disting două
cazuri:
1. Valoarea citită este mai mică decât prima valoare a listei. Aceasta înseamnă
că ea este cea mai mică din listă şi va fi introdusă prima în listă.
v 3 7 9 MaxInt
Noua înregistrare va conţine adresa nodului 3, iar v conţine adresa noii înregistrări:
v 3 7 9 MaxInt
2. Valoarea citită nu este cea mai mică din listă. În mod sigur, nu este cea mai
mare, pentru că am introdus MaxInt în listă. Dacă nu a fost îndeplinită
condiţia de la cazul 1, înseamnă că valoarea nu este nici cea mai mică.
Aceasta înseamnă că ea va trebui introdusă în interiorul listei.
Manual de informatică pentru clasa a XI-a 35
v 3 7 9 MaxInt
8 c c1
v 3 7 9 MaxInt
Procedeul generării unei valori mai mari sau mai mici decât toate cele
posibile este deseori folosit în programare. Realizaţi cât de mult a simplificat
algoritmul?
De aici tragem concluzia că o astfel de problemă poate avea mai multe soluţii.
În concluzie, problema poate avea sau nu soluţie, iar dacă are, poate fi
unică sau nu.
Manual de informatică pentru clasa a XI-a 37
a 0 0 al3 al4
4 2
38 Capitolul 2. Liste liniare
4 2
2 4 2
2 4 2
1 1
Astfel:
2 4 2
1 1
2 4 2
1 1
2 4 2
1 1
- ordonarea unor activităţi, atunci când ele sunt condiţionate una de alta;
- ordonarea unor termeni care se cer explicaţi, pentru a-i putea explica prin
alţii deja prezentaţi.
În acest paragraf vom prezenta modul în care se pot programa operaţii precum
adunarea, scăderea, înmulţirea şi împărţirea polinoamelor cu coeficienţi reali.
Să observăm că, în acest caz, utilizarea listelor liniare simplu înlănţuite este
necesară şi, exemplul în sine, constituie un argument pentru utilizarea acestora.
De ce?
Atunci?
42 Capitolul 2. Liste liniare
Pentru memorarea unui polinom (prin coeficienţii lui) vom utiliza o listă liniară
simplu înlănţuită. Fiecare nod al listei va reţine, în această ordine,
coeficientul şi gradul unui monom. Pentru simplificarea operaţiilor cu
polinoame, un polinom va fi reţinut în ordinea descrescătoare a gradelor. De
exemplu, pentru polinomul 3×X1456+ X100+2, vom avea:
3 1456 1 100 0 2
1. Pentru a adăuga un nod listei liniare simplu înlănţuite, atunci când aceasta se
creează, vom utiliza subprogramul următor, adaug. Parametrii de intrare sunt
adresele de început şi de sfârşit ale listei. Desigur, se putea evita parametrul prin
care se transmite adresa de sfârşit a listei, dar, prin transmiterea acestui
parametru, adăugarea unui nod la sfârşitul listei se poate face cu mult mai repede,
pentru că nu mai este necesară parcurgerea întregii liste.
2. Pentru a crea lista asociată unui polinom, vom utiliza funcţia următoare, care
după ce creează lista, returnează adresa ei de început. Pentru a putea introduce
datele, se cere, de la început, numărul de "termeni" (monoame) ai polinomului.
Este foarte important ca monoamele să fie introduse în ordinea descrescătoare a
gradelor, pentru că funcţia nu realizează ordonarea acestora. Pentru a crea lista se
utilizează subprogramul prezentat anterior, adaug. Exerciţiu. Modificaţi funcţia
astfel încât datele de intrare să se găsească într-un fişier text. De asemenea, se
cere ca funcţia să sorteze monoamele în ordinea descrescătoare a gradului.
X3+6×X2+3×X şi 2×X2-3×X+1.
1 3 6 2 3 1
c1
2 2 -3 1 1 0
c2
b) dacă gradul monomului indicat de c1 este strict mai mare decât gradul
monomului indicat de c2, se adaugă la lista polinomului rezultat monomul indicat
de c1, avansează c1;
c) dacă gradul monomului indicat de c1 este strict mai mic decât gradul
monomului indicat de c2, se adaugă la lista polinomului rezultat monomul indicat
de c2, avansează c2.
Testele de mai sus, se repetă cât timp nu s-a parcurs integral nici o listă de
intrare. Dacă s-a ajuns la sfârşitul listei pointată de c1, atunci lista pointată de c2
este copiată, începând de la c2 până la sfârşit în lista rezultat. Dacă s-a ajuns la
sfârşitul listei pointată de c2, atunci lista pointată de c1 este copiată, începând de
la c2 până la sfârşit în lista rezultat.
- Gradul monomului reţinut de nodul pointat de c1 este mai mare decât gradul
monomului reţinut de nodul pointat de c2. Primul nod al listei polinomului
rezultat va fi X3. c1 va pointa către nodul care reţine monomul 6×X2.
- Gradele celor două monoame sunt egale şi suma coeficienţilor este diferită de
0. Al doilea nod al listei polinomului rezultat va fi 8×X2. c1 va pointa către
nodul care reţine monomul 3×X, iar c2 va pointa către nodul care reţine
monomul -3×X.
- Gradele celor două monoame sunt egale şi suma coeficienţilor este egală cu
0. Prin avansul lui c1, prima listă a fost parcursă integral.
adaug(factor,sfactor,
gradul,coef); Nod* pol=mul(imp,factor);
pol:=mul(imp,factor); sterg(factor);
sterg(factor); Nod* neg=negativ(pol);
neg:=negativ(pol); sterg(pol);
sterg(pol); rest=adun(rest,neg);
rest:=adun(rest,neg); sterg(neg);
sterg(neg); }
end; }
end;
Avantajul utilizării listei alocate dublu înlănţuit este dat de faptul că o astfel
de listă poate fi parcursă în ambele sensuri.
1) creare;
2) adăugare la dreapta;
3) adăugare la stânga;
4) adăugare în interiorul listei;
5) ştergere din interiorul listei;
6) ştergere la stânga listei;
7) ştergere la dreapta listei;
8) listare de la stânga la dreapta;
9) listare de la dreapta la stânga.
0 7 0
b s
0 7 3 0
b s
0 3 7 0
b s
0 5 0
0 3 7 0
b s
0 3 5 7 0
b s
0 3 5 7 0
b s
0 3 7 0
b s
Definiţia 2.4. Stiva este o listă pentru care singurele operaţii permise sunt:
• adăugarea unui element în stivă;
• eliminarea, consultarea sau modificarea ultimului element introdus în
stivă.
Definiţia 2.5. O coadă este o listă pentru care toate inserările sunt făcute
la unul din capete, iar toate ştergerile (consultările, modificările) la celălalt
capăt.
v sf
7 3 5 2
Probleme propuse
1. Asociaţi fiecărui tip de listă denumit în coloana din stânga desenul
corespunzător din coloana aflată în partea dreaptă a tabelului următor, scriind cifra
asociată fiecărei litere:
2. Care dintre nodurile listei următoare (identificate prin numere între 1 şi 4) este
primul element al listei?
1 2 3 4
3. Dacă o listă formată din două noduri (identificate prin numerele 1 şi 2) are
proprietatea că elementul următor nodului 2 este nodul 1 şi nu există un element
următor nodului 1, atunci spunem că lista:
8 4 2 3
8 4 2 3
6. Ştiind că există o listă liniară simplu înlănţuită nevidă, fiecare nod reţinând în
câmpul ref adresa elementului următor al listei, şi ştiind că variabilele v şi s reţin
adresa primului şi respectiv adresa ultimului element al listei, explicaţi care este
efectul instrucţiunii:
7. Ştiind că există o listă liniară simplu înlănţuită cu cel puţin două noduri, fiecare
nod reţinând în câmpul urm adresa elementului următor al listei, şi ştiind că
variabilele ini şi fin reţin adresa primului şi respectiv adresa ultimului element al
listei, explicaţi care este efectul instrucţiunii:
60 Capitolul 2. Liste liniare
numele: 20 de caractere;
prenumele: 20 de caractere;
un vector de numere reale cu 3 componente care reţin notele elevului.
10. Scrieţi o funcţie care returnează media generală a elevilor care se găsesc în listă.
11. Scrieţi un program care creează şi afişează o listă liniară simplu înlănţuită.
Fiecare nod al listei conţine, pe lângă informaţia de adresă, un număr natural mai
mic sau egal cu 100000. Numerele se găsesc, toate pe o linie, în ordine, separate
prinr-un spaţiu (blank) în fişierul text ”lista.in”.
12. Scrieţi un program care creează şi afişează două liste liniare simplu înlănţuite.
Prima listă va conţine, în ordinea citirii, numere pare, iar a doua va conţine, în
aceeaşi ordine, numere impare. Numerele se citesc din fişierul text ”numere.in”.
Ele se găsesc toate pe o linie şi sunt separate prin cel puţin un spaţiu. De exemplu,
dacă fişierul text conţine numerele: 0 2 5 9 8 3 6 7 1, atunci listele vor fi:
0 2 8 6
5 9 3 7 1
Linia 1 0 2 8 6
Linia 2 5 9 3 7 1
14. Scrieţi un subprogram care adaugă un nod la sfârşitul unei liste liniare simplu
înlănţuite. Fiecare nod al listei conţine, pe lângă informaţia de adresă, un număr
real. Subprogramul are ca parametri formali adresa primului element al listei şi
valoarea reală care se adaugă.
Manual de informatică pentru clasa a XI-a 61
15. Scrieţi o funcţie care adaugă un nod la începutul unei liste liniare simplu
înlănţuite. Fiecare nod al listei conţine, pe lângă informaţia de adresă, un număr
real. Funcţia are ca parametri formali adresa primului element al listei şi valoarea
reală care se adaugă. Ea returnează noua adresă de început a listei.
16. Se dă o listă liniară simplu înlănţuită ale cărei noduri reţin, pe lângă informaţiile
de adresă, numere naturale cu o singură cifră. Lista are cel puţin un nod şi cel mult
6 noduri. Se cere să se scrie o funcţie care calculează şi afişează valoarea
întreagă obţinută prin lipirea cifrelor memorate în listă în ordinea citirii. Funcţia va
primi ca parametru de intrare vârful listei. Exemplu: pentru lista de mai jos se
afişează valoarea 956:
0 9 5 6
17. Prin operaţia de concatenare a două liste liniare simplu înlănţuite se obţine o a
treia listă liniară simplu înlănţuită care conţine, în ordine, nodurile primei liste,
urmate de nodurile celei de-a doua liste. Să se scrie o funcţie care concatenează
două liste date prin adresele nodurilor de început. Funcţia va returna adresa
primului nod al noii liste.
18. Se citeşte un fişier text cifre.in care conţine, pe o unică linie, numai numere
naturale între 0 şi 9. Numerele nu sunt separate prin spaţii. Se cere să se formeze
o listă liniară simplu înlănţuită în care fiecare nod reţine o cifră. Exemplu: Pentru
01396 se obţine lista:
0 1 3 9 6
19. Să se scrie o funcţie care primeşte ca parametru de intrare adresa unei liste
liniare simplu înlănţuite şi are rolul de a inversa nodurile aflate pe prima şi ultima
poziţie. Funcţia va returna adresa primului nod al listei.
20. Scrieţi un subprogram care eliberează spaţiul ocupat de o listă liniară simplu
înlănţuită.
21. Scrieţi un subprogram care memorează un tablou bidimensional cu m linii şi n
coloane ca m liste liniare simplu înlănţuite, unde fiecare listă memorează, în ordine,
elementele unei linii.
1 2 3 7 1 2 3 7
4 1 5 9
4 1 5 9
22. Se dă o listă liniară simplu înlănţuită în care fiecare nod reţine un caracter. Să
se scrie o funcţie care depistează dacă lista conţine caractere distincte sau nu. Ce
valoare trebuie să întoarcă o astfel de funcţie?
62 Capitolul 2. Liste liniare
23. Se dă o listă liniară simplu înlănţuită în care fiecare nod reţine o literă. Se cere
să se scrie o funcţie care depistează dacă cuvântul format prin alăturarea literelor
citite este sau nu palindrom (se obţine acelaşi rezultat dacă cuvântul se citeşte
direct sau invers). De exemplu, lista următoare conţine un cuvânt palindrom.
a b c b a
24. În cazul în care pentru o listă liniară simplu înlănţuită câmpul de adresă al
ultimului nod reţine adresa primului nod, se obţine o listă circulară:
Creaţi o listă circulară în care fiecare nod reţine un număr natural. De asemenea,
scrieţi subprograme de inserare şi ştergere a unui nod al listei create.
25. Se citeşte o permutare a numerelor 1, 2, ..., n. Se cere ca, prin utilizarea unei
liste circulare, să se afişeze toate permutările circulare ale acesteia.
Exemplu: Se citeşte 1 2 3. Se va afişa: 1 2 3, 2 3 1, 3 1 2.
26. Scrieţi un subprogram care transformă o listă liniară simplu înlănţuită în una
dublu înlănţuită.
27. După cum ştiţi, nu se poate lucra în mod direct cu numere naturale oricât de
mari. Din acest motiv, vom memora un număr natural ca o listă liniară simplu
înlănţuită. De exemplu, numărul 5610 se va memora sub forma de mai jos. Scrieţi
un subprogram care citeşte de la tastatură cifrele unui număr natural, începând cu
cifra cea mai semnificativă, şi-l memorează ca listă liniară simplu înlănţuită.
0 1 6 5
29. Scrieţi o funcţie care adună două numere naturale memorate ca mai sus şi
returnează adresa de început a numărului sumă, memorat ca listă. Exemplu: din
primele liste rezultă a treia listă:
0 1 6 5
5 9 4 7 9
5 0 1 3 0 1
Manual de informatică pentru clasa a XI-a 63
30. Scrieţi o funcţie care calculează produsul dintre un număr natural memorat sub
formă de listă şi un altul, cu o singură cifră, transmis ca parametru. Funcţia
returnează adresa primului nod al listei care conţine rezultatul. Exemplu: numărul
de mai jos se înmulţeşte cu 3 şi rezultă:
0 1 6 5
0 3 8 6 1
31. Scrieţi o funcţie care înmulţeşte două numere naturale memorate sub formă de
liste şi returnează adresa de început a listei rezultat.
32. Lucrare în echipă. Scrieţi un ansamblu de subprograme (numim ansamblul
NUMERE_MARI) care să ne ajute să lucrăm cu numere întregi, oricât de mari,
memorate sub formă de liste. Utilizatorul poate efectua adunarea, scăderea,
înmulţirea şi împărţirea a două astfel de numere. De asemenea, vor exista funcţii
care să permită efectuarea comparaţiilor între două numere: mai mare, mai mic,
egal, mai mare sau egal, mai mic sau egal.
33. Prin utilizarea asamblului numit NUMERE_MARI, calculaţi maximul a n numere
întregi, citite de la tastatură.
34. Prin utilizarea asamblului numit NUMERE_MARI, sortaţi crecător n numere întregi,
citite de la tastatură.
35. Prin utilizarea asamblului numit NUMERE_MARI, calculaţi n!, unde n este citit
de la tastatură.
n
36. Prin utilizarea asamblului numit NUMERE_MARI, calculaţi: ∑ k!
k =1
1.2 0 0 0 1.2 5 3
A= 0 5 0 0
0 3
0 0 1 6 12
Capitolul 3
3.2. Aplicaţii
0, n = 1;
T(n) = n
2T + n, altfel.
2
Avem:
Rezolvare. Este necesară o funcţie POZ care tratează o porţiune din vector,
cuprinsă între indicii daţi de li (limita inferioară) şi ls (limita superioară). Rolul
acestei funcţii este de a poziţiona prima componentă a[li] pe o poziţie k cuprinsă
între li şi ls, astfel încât toate componentele vectorului cuprinse între li şi k-1
să fie mai mici sau egale decât a[k] şi toate componentele vectorului cuprinse
între k+1 şi ls să fie mai mari sau egale decât a[k].
• i=1, j=5;
• a[1]>a[5], deci se inversează elementele aflate pe poziţiile 1 şi 5,
deci a=(2,9,3,1,6) şi programul trece la modul de lucru b);
• i=2, j=5;
• a[2]>a[5], deci a=(2,6,3,1,9) şi se revine la modul de lucru a);
• i=2, j=4;
• a[2]>a[4], deci a=(2,1,3,6,9); se trece la modul de lucru b);
• i=3, j=4;
• funcţia se încheie, elementul aflat iniţial pe poziţia 1 se găseşte
acum pe poziţia 4, toate elementele din stânga lui fiind mai mici
decât el, totodată toate elementele din dreapta lui fiind mai mari
decât el (k=4).
După aplicarea funcţiei POZ, este evident că elementul care se află iniţial în
poziţia li va ajunge pe o poziţie k şi va rămâne pe acea poziţie în cadrul
vectorului deja sortat, fapt care reprezintă esenţa algoritmului.
• se apelează POZ;
• se apelează QUICK pentru li şi k-1;
• se apelează QUICK pentru k+1 şi ls.
Rezolvare.
Dacă n=1, se face mutarea ab, adică se mută discul de pe tija a pe tija b.
ab, n =1
H(n, a, b, c) =
H(n − 1, a, c, b), ab, H(n − 1, c, b, a), n > 1
h
• xv(i),yv(i)
x,y l
Pentru a se afla în interiorul dreptunghiului, gaura trebuie să îndeplinească
simultan condiţiile:
1) xv(i)>x;
2) xv(i)<x+l;
3) yv(i)>y;
4) yv(i)<y+h.
Dacă facem o tăietură verticală prin această gaură, obţinem două dreptunghiuri:
1) x, y, xv(i)-x, h;
2) xv(i), y, l+x-xv(i), h.
if gasit else
then if (l*h>lf*hf)
begin { xf=x;
dimp(x,y,xv[i]-x, yf=y;
h,xf,yf,lf,hf,xv,yv); lf=l;
dimp(xv[i],y,l+x-xv[i], hf=h;
h,xf,yf,lf,hf,xv,yv); }
dimp(x,y,l,yv[i]-y, }
xf,yf,lf,hf,xv,yv);
dimp(x,yv[i],l,h+y-yv[i], main()
xf,yf,lf,hf,xv,yv) { cout<<"n="; cin>>n;
end for (int i=1;i<=n;i++)
else { cout<<"x["<<i<<"]=";
if (l*h)>(lf*hf) cin>>xv[i];
then cout<<"y["<<i<<"]=";
begin cin>>yv[i];
xf:=x; }
yf:=y; cout<<"l="; cin>>l;
lf:=l; cout<<"h="; cin>>h;
hf:=h dimp(0,0,l,h,xf,yf,lf,
end hf,xv,yv);
end; cout<<"x="<<xf<<" y="<<yf
<<" l="<<lf<<" h="<<hf;
begin }
write('n=');
readln(n);
for i:=1 to n do
begin
write('x[',i,']=');
readln(xv[i]);
write('y[',i,']=');
readln(yv[i])
end;
write('l=');
readln(l);
write('h=');
readln(h);
lf:=0;
hf:=0;
dimp(0,0,l,h,xf,yf,
lf,hf,xv,yv);
writeln('x=',xf,' y=',yf,'
l=',lf,' h=',hf)
end.
Manual de informatică pentru clasa a XI-a 75
3.3. Fractali
initgraph(gdriver,gmode,‘cale’);.
2) prin indicarea cu ajutorul primilor doi parametri a unui driver şi a unui mod
de lucru solicitate de programator (în acest caz, nu se poate executa programul pe
un calculator ce nu este dotat cu placa grafică specificată):
gdriver := VGA;
gmode := VGAHI;
initgraph(gdriver,gmode,’c:\tp\bgi’);
if graphresult<>0 then
begin
writeln(“Tentativa esuata!”);
halt
end;
Tentativa de iniţializare grafică poate eşua din diverse motive, cum ar fi: lipsa
unităţii GRAPH, calea indicată greşit, etc. Testarea se realizează cu funcţia întreagă
graphresult care returnează 0 în caz afirmativ şi o valoare diferită de 0, în
caz contrar.
Limbajul C++ (în varianta Borland), conţine o serie de funcţii care permit
realizarea unor aplicaţii grafice. Acestea sunt reunite în fişierul GRAPHICS.H, ce se
găseşte în folderul INCLUDE.
initgraph(&gdriver,&gmode,"cale");.
2) prin indicarea cu ajutorul primilor doi parametri a unui driver şi a unui mod
de lucru solicitate de programator (în acest caz, nu se poate executa programul pe
un calculator ce nu este dotat cu placa grafică specificată):
Tentativa de iniţializare grafică poate eşua din diverse motive, cum ar fi: lipsa
unităţii GRAPHICS, calea indicată greşit, etc. Testarea se realizează cu funcţia
întreagă graphresult() care returnează 0 în caz afirmativ şi o valoare diferită
de 0, în caz contrar.
Odată intraţi în modul grafic, nu se mai poate scrie pe monitor ca până acum
(de exemplu, cu cout). Ieşirea din modul grafic se face prin utilizarea
procedurii closegraph().
Atenţie! Pentru a putea scrie şi rula programe C++ ce utilizează modul grafic al
limbajului, trebuie bifată următoarea opţiune, din meniu:
Aceste culori sunt cele implicite. Pentru a utiliza mai multe culori (dar nu în
acelaşi timp), se poate schimba setul (paleta) de culori. Întrucât în acest
moment nu sunt necesare o multitudine de culori, nu vom prezenta în detaliu
acest aspect.
Manual de informatică pentru clasa a XI-a 79
Observaţii
Operaţia de desenare
Fiecare latură a acestui poligon se transformă din nou, după aceeaşi regulă.
Să se vizualizeze figura obţinută după ls paşi (număr citit de la tastatură).
begin if (n<ls)
rotplan((2*x1+x2) div 3, {generator(x1,y1,div((2*x1+x2),
(2*y1+y2) div 3,(x1+2*x2) div 3).quot,div((2*y1+y2),
3,(y1+2*y2) div 3,x,y,pi/3); 3).quot,n+1,ls);
if n<ls then generator(div((2*x1+x2),
begin 3).quot,div((2*y1+y2),
generator(x1,y1,(2*x1+x2) 3).quot,x,y,n+1,ls);
div 3,(2*y1+y2) div 3, generator(x,y,div((x1+2*x2),
n+1,ls); 3).quot,div((y1+2*y2),
generator((2*x1+x2) div 3, 3).quot,n+1,ls);
(2*y1+y2) div 3, generator(div((x1+2*x2),
x,y,n+1,ls); 3).quot,div((y1+2*y2),
generator(x,y,(x1+2*x2) div 3).quot,x2,y2,n+1,ls);
3,(y1+2*y2) div 3,n+1,ls); }
generator((x1+2*x2) div 3, else desenez(x1,y1,x2,y2,x,y);
(y1+2*y2) div 3, }
x2,y2,n+1,ls);
end main()
else desenez(x1,y1,x2,y2,x,y); { cout<<"ls= "; cin>>ls;
end; init();
setcolor(6);
begin L = getmaxx()-320;
write('ls= '); readln(ls); generator(160,getmaxy()-150,
initg; 160+L,getmaxy()-150,1,ls);
setcolor(red); generator(160+L,getmaxy()-
L:=getmaxx-320; 150,160+div(L,2).quot,
generator(160,getmaxy-150, getmaxy()-150-
160+L,getmaxy-150,1,ls); ceil(L*(sqrt(3)/2)),1,ls);
generator(160+L,getmaxy-150, generator(160+div(L,2).quot,
160+L div 2,getmaxy-150 – getmaxy()-150-
L*round(sqrt(3)/2),1,ls); ceil(L*(sqrt(3)/2)),160,
generator(160+L div 2,getmaxy- getmaxy()-150,1,ls);
150-L*round(sqrt(3)/2),160, setfillstyle(1,4);
getmaxy-150,1,ls); floodfill(div(getmaxx(),2)
setfillstyle(1,blue); .quot,div(getmaxx(),
floodfill(getmaxx div 2, 2).quot,6);
getmaxy div 2, red); getch();
readln closegraph();
end. }
Priviţi mai jos rezultatele obţinute pentru diferite valori ale lui ls:
ls = 2 ls = 3 ls = 4
Figura 3.2. Exemple de fractali formaţi cu ajutorul curbei lui Koch (triunghi echilateral)
Manual de informatică pentru clasa a XI-a 83
Fiecare segment al liniei frânte astfel formate se transformă din nou după
aceeaşi regulă. Se cere să se vizualizeze curba după ls transformări (valoare
citită de la tastatură). Transformarea şi desenarea unui segment sunt realizate de
procedura desen. Aceasta are ca parametri de intrare coordonatele punctului care
determină segmentul, numărul de transformări efectuate (n) şi numărul de
transformări cerut (ls). Procedura conţine următorul algoritm:
• dacă nu a fost efectuat numărul de transformări necesar, se
calculează coordonatele punctelor care determină linia frântă obţinută
pornind de la segment şi pentru fiecare segment din această linie se
reapelează procedura desen;
• contrar, se desenează linia frântă obţinută.
În final, figura se colorează. Programul este prezentat în continuare:
Sunt prezentate mai jos imaginile obţinute în urma rulării programului, pentru
diferite valori ale lui ls:
3.3.4. Arborele
Se dă un segment AB. Cu ajutorul lui se construieşte un arbore, aşa cum se
vede în figura de mai jos:
Pentru diverse valori ale parametrului de intrare ls, vom obţine arborii:
ls = 3 ls = 5 ls = 7
Figura 3.6. Exemple de fractali de tip arbore
Manual de informatică pentru clasa a XI-a 87
Observaţii
Exemplele grafice prezentate au fost generate pentru valori mici ale lui ls
deoarece la tipărire, detaliile sunt greu de observat peste o anumită limită.
Generarea fractalilor reprezintă o aplicaţie a recursivităţii, tehnica aplicată
fiind DIVIDE ET IMPERA. Pentru valori mari ale lui ls, timpul de efectuare al
calculelor poate fi şi de ordinul zecilor de secunde, ceea ce poate fi
considerat un inconvenient major.
Probleme propuse
1. Se citeşte a≥1, număr real. Se cere să se scrie o funcţie care calculează ln(a)
cu 3 zecimale exacte. Nu este permisă utilizarea funcţiei logaritmice a limbajului.
2. Scrieţi o funcţie care calculează prin metoda DIVIDE ET IMPERA suma numerelor
reţinute dintr-un vector.
Răspunsuri
1. ln(a)=x ⇔ a=ex ⇔ ex-a=0. Dacă notăm cu f(x)=ex-a, atunci trebuie
rezolvată ecuaţia f(x)=0. Avem f(0)=e0-a=1-a<0 şi f(a)=ea-a>0. De aici,
rezultă că f(x) are o rădăcină în intervalul (0,a). Cum f(x) este strict
crescătoare (ca diferenţă între funcţia strict crescătoare ex şi o constantă),
rădăcina este unică. Algoritmul pe care îl folosim se numeşte în matematică
“metoda înjumătăţirii intervalului”, dar, din punct de vedere informatic, corespunde
metodei DIVIDE ET IMPERA.
Fie li=0 şi ls=a, m=(a+b)/2. Dacă f(li)×f(m)<0, rădăcina se găseşte
în (li,m), altfel rădăcina este în [m,ls). Condiţia de terminare este ca
li − ls < 0.0001 , pentru că trebuie să avem 3 zecimale exacte.
0 n = 1;
T(n) = n
2T + 1 altfel.
2
Capitolul 4
Metoda BACKTRACKING
Principiul metodei
Pentru permutări, vom avea o soluţie când s-a generat o secvenţă alcătuită din n
numere distincte. Cum subprogramul este recursiv, acest fapt se întâmplă atunci
când s-a ajuns pe nivelul n+1.
De exemplu, dacă n=4, o soluţie este reprezentată în figura 4.1., a). Modul de
obţinere al soluţiei este prezentat în figurile următoare, de la b) la i):
a) b) c)
d) e) f)
g) h) i)
Exemple:
a)
sol(1) = 1 i = 1
sol(3) = 3 j = 3
|sol(1) - sol(3)| = |1 - 3| = 2
|i - j| = |1 - 3| = 2
Figura 4.2.
Manual de informatică pentru clasa a XI-a 97
b)
sol(1) = 3 i = 1
sol(3) = 1 j = 3
|sol(i) - sol(j)| = |3 - 1| = 2
|i - j| = |1 - 3| = 2
Figura 4.3.
Exerciţii
1. Desenaţi configuraţia tablei corespunzătoare vectorului sol=(3,1,4,2,5) şi
verificaţi dacă aceasta reprezintă o soluţie a problemei damelor.
98 Capitolul 4. Metoda backtracking
Este întotdeauna necesar să-l folosim pe acesta sau putem reţine numai
ideea şi, după caz, să scriem mai puţin?
Exerciţii
1. Testaţi subprogramul anterior pentru problema generării permutărilor.
2. Adaptaţi rezolvarea problemei permutărilor astfel încât să se afişeze numai
permutările în care oricare două numere consecutive nu sunt alăturate.
3. Observaţi că ordinea de afişare a soluţiilor depinde de ordinea în care se
consideră elementele mulţimilor A1, A2, … Ce modificări trebuie aduse
procedurii recursive back astfel încât permutarile de 4 elemente să fie afişate în
ordinea: 4321, 4312, 4231, 4213, 4132, 4123, 3421, 3412 … 1243, 1234?
4. Renunţaţi la utilizarea subprogramului valid, utilizând un vector folosit, în
care folosit[i] are valoarea 0 dacă numărul i nu este deja folosit în soluţie
şi are valoarea 1 în caz contrar. Astfel, plasarea valorii i în vectorul soluţie
(sol[k]i) trebuie însoţită de memorarea faptului că i este utilizat
(folosit[i]1), la revenirea din recursie (cand se înlătură valoarea de pe
poziţia curentă) fiind necesară memorarea faptului că i nu mai este utilizat în
soluţie (folosit[i]0). Condiţia de validare se reduce în acest caz la:
Dacă folosit[i]=0 atunci ...
5. Urmăriţi toate modalităţile diferite de a aşeza patru obiecte identificate prin
numerele 1, 2, 3, 4 pe un cerc, la distanţe egale. Vom observa că nu toate
permutările de patru obiecte sunt configuraţii distincte, datorită distribuţiei pe
cerc. Astfel permutările 1234, 2341, 3412 şi 4123 reprezintă una şi aceeaşi
configuraţie. Scrieţi un program care afişează numai permutările distincte
conform aşezării pe un cerc. Indicaţie: se va considera sol[1]=1 şi se vor
permuta doar celelalte elemente.
Manual de informatică pentru clasa a XI-a 101
Este demonstrat faptul că sunt suficiente numai 4 culori pentru ca orice hartă
să poată fi colorată.
Pentru exemplificare, vom considera harta din figura 4.4., unde ţările sunt
numerotate cu cifre cuprinse între 1 şi 5.
Exerciţii
1. Soluţia afişată este şi soluţia care utilizează un număr
minim de culori?
2. Dacă ţările din centrul figurii alăturate sunt numerotate
cu 1, 2, 3, 4, iar cele de la exterior cu 5 şi 6, care este
soluţia afişată de programul dat? Este acesta numărul
minim de culori necesare?
3. Câte culori sunt suficiente pentru colorarea unei hărţi
particulare în care orice ţară se învecinează cu cel Figura 4.5.
mult două ţări?
4. Daţi exemplu de particularitate pe care poate să o aibă o hartă pentru a fi
suficiente două culori pentru colorarea tuturor ţărilor?
Exemple
Observaţii
2. O altă interpretare pentru metoda backtracking: fiind date n mulţimi: A1, A2, ...,
An, produsul cartezian al lor A1×A2×...×An se mai numeşte spaţiul soluţiilor. În
acest context, metoda backtracking caută una sau toate soluţiile, care sunt
elemente ale produsului cartezian şi care îndeplinesc anumite condiţii. Astfel, se
poate justifica faptul că, în generarea produsului cartezian, nu este necesar
subprogramul valid pentru că se generează toate elementele produsului
cartezian, fără a verifica anumite condiţii.
106 Capitolul 4. Metoda backtracking
Deşi algoritmul este exponenţial, există aplicaţii utile, evident, atunci când
fiecare mulţime Ai poate lua numai câteva valori şi unde n este suficient de mic.
Exerciţii
1. Tabelarea anumitor funcţii. Se dă funcţia
f:A1×A2×...×An→R,
unde fiecare mulţime Ai este dată de numerele întregi din intervalul [ai,bi] şi
f=c1x1+c2x2+...+cnxn, ci∈R.
2. Scrieţi programul care generează toate ”cuvintele” cu patru litere care au prima
şi ultima literă vocale, litera a doua consoană din mulţimea {P, R, S, T}, iar a treia
literă consoană din mulţimea {B, M, R, T, V}.
3. Scrieţi programul care generează şi numără câte cuvinte de cinci litere ale
alfabetului englez se pot forma, cu condiţia să nu existe două consoane alăturate
şi nici două vocale alăturate.
1, dacă i ∈ A
V[i] =
0, dacă i ∉ A
begin
write('n=');
readln(n);
back(1)
end.
Exerciţii
1. Problema nu este rezolvată în totalitate. Programul afişează numai toate valorile
pe care le poate lua vectorul caracteristic. Completaţi-l astfel încât programul să
afişeze toate submulţimile mulţimii {1,2,...,n}!
Observaţii
⋅ 2
2 ⋅ 2...2
=2 .
n
de n ori
n!
C pn = .
(n − p)! p!
Rezolvare. O soluţie este de forma x1,x2,...,xp, unde x1, x2, ..., xp∈A.
În plus, x1, x2, ..., xp trebuie să fie distincte. Cum la o mulţime ordinea elementelor
nu prezintă importanţă, putem genera elementele ei în ordine strict crescătoare.
Această observaţie ne ajută foarte mult în elaborarea algoritmului.
a) Pentru k>1, sol[k]>sol[k-1].
Manual de informatică pentru clasa a XI-a 109
sol[k+1]>n-p+k+1,
...
sol[p]>n-p+p=n.
1≤sol[1]≤n-p+1,
sol[1]<sol[2]≤n-p+2,
...
sol[n-1]<sol[n]≤n-p+p=n.
Relaţiile de mai sus simplifică mult algoritmul, pentru că ţinând cont de ele, nu mai
este necesar să se testeze nici o condiţie de continuare.
Exerciţii
1. Se dau coordonatele din plan a n puncte. Afişaţi coordonatele vârfurilor tuturor
pătratelor care au ca vârfuri puncte din mulţimea considerată.
2. Se dau n substanţe chimice. Se ştie că, în anumite condiţii, unele substanţe intră
în reacţii chimice cu altele. Fiind date p perechi de forma (i,j) cu semnificaţia că
substanţa i intră în reacţie cu substanţa j, se cer toate grupurile de s<n substanţe
astfel încât oricare două substanţe din grup nu intră în reacţie.
Exemplu: p=2, n=3. Avem: 12, 21, 13, 31, 23, 32. De exemplu, 21 este funcţia
f:A→B dată astfel: f(1)=2; f(2)=1. Avem relaţiile:
n!
A pn = = n(n − 1)...(n − p + 1) .
(n − p)!
Pe de altă parte, se poate lucra mult mai eficient. O soluţie este de forma:
x1x2...xp, unde x1, x2, ..., xp∈B. În plus, x1, x2, ..., xp trebuie să fie distincte.
Spre deosebire de algoritmul de generare a combinărilor, aici ne interesează toate
permutările unei soluţii (acestea sunt, la rândul lor, alte soluţii). Aceasta înseamnă
că nu mai putem pune în soluţie elementele în ordine crescătoare. Să recapitulăm:
Exerciţii
1. Se citesc n, p şi apoi n litere distincte. Afişaţi toate cuvintele care se pot forma
cu p dintre ele.
Rezolvare. Chiar dacă ştim să generăm toate submulţimile unei mulţimi, tot nu ne
ajută să generăm toate partiţiile.
1. Pentru a putea genera toate partiţiile, trebuie să găsim o metodă prin care să
putem reţine o partiţie. O primă idee ne conduce la folosirea unui vector, sol, astfel:
dacă sol[i]=k, atunci elementul i se găseşte în mulţimea k a partiţiei. Totuşi, nu
ştim câte mulţimi sunt în partiţia respectivă. Există o partiţie care conţine n mulţimi
atunci când fiecare element este într-o mulţime şi una care conţine toate mulţimile,
adică tocmai mulţimea A. Cu alte cuvinte, numărul mulţimilor dintr-o partiţie este
între 1 şi n.
Prin această condiţie se evită situaţia în care, de exemplu, vectorul sol reţine
(1,3,1). Aceasta ar avea semnificaţia că elementele 1 şi 3 se găsesc în
submulţimea 1 a partiţiei, iar elementul 2 se găseşte în submulţimea 3 a partiţiei. În
acest caz, lipseşte submulţimea 2 a partiţiei.
- sol=(1,1,1) - A1={1,2,3);
- sol=(1,1,2) - A1={1,2} A2={3};
- sol=(1,2,1) - A1={1,3} A2={2};
- sol=(1,2,2) - A1={1} A2={2,3};
- sol=(1,2,3) - A1={1} A2={2} A3={3}.
begin
write('n='); readln(n);
back(1);
end.
4.5.1. Generalităţi
Toate problemele pe care le-am întâlnit până acum admit soluţii care
îndeplinesc următoarele caracteristici:
soluţiile sunt sub formă de vector;
toate soluţiile unei probleme au aceeaşi lungime, unde prin lungime
înţelegem numărul de componente ale vectorului soluţie.
Manual de informatică pentru clasa a XI-a 115
c) Toate soluţiile sub formă de vector ale problemei generării tuturor partiţiilor
mulţimii A au lungimea n.
Ordinea numerelor din sumă este importantă. Astfel, se tipăreşte 112 dar
şi 211, 121.
sol[1]+sol[2]+...sol[k]≤n.
sol[1]+sol[2]+...sol[k]=n.
soluţie
1 0 0 0 1 1 0 0 1 1 1 0 1 1 1 1
s=0, k=1 s=1, k=2 s=2, k=3 s=3, k=4
1 1 2 1 2 0 0 1 2 1 1 3
s=2, k=3 s=1, k=2 s=3, k=3 s=1, k=2
Observaţi modul în care calculăm suma la fiecare pas. De câte ori se trece
la componenta următoare (k+1), la s se adună sol[k], de câte ori se face
pasul înapoi (se trece la componenta k-1), din s se scade sol[k].
Exerciţii
1. Cum trebuie procedat în cazul în care se cere ca soluţiile să fie afişate o singură
dată? Spre exemplu, dacă s-a afişat descompunerea 1,1,2 să nu se mai afişeze
2,1,1 sau 1,2,1?
Indicaţie: procedeul a mai fost întâlnit, de exemplu la generarea combinărilor.
Soluţiile se vor genera în ordine crescătoare. Modificaţi programul în acest sens.
2. Adaptaţi metoda de rezolvare astfel încât să se genereze numai partiţiile formate
din numere naturale distincte.
3. Adaptaţi metoda de rezolvare astfel încât să se genereze numai partiţiile formate
din cel puţin p numere naturale distincte (n şi p citite de la tastatură).
4. Adaptaţi metoda de rezolvare astfel încât să se genereze numai partiţiile formate
din numere naturale aflate în intervalul [a,b] (n, a şi b citite de la tastatură).
5. Rezolvaţi problema scrierii numărului natural n ca sumă de numere naturale
alese dintr-o mulţime formată din k valori date {v1, v2, …, vk}. Astfel, 10 se
poate scrie ca sumă de numere alese din mulţimea {2,3,6} în felul următor:
10=2+2+2+2+2, 10=2+2+3+3, 10=2+2+6.
1) 1 de 2, 1 de 3; 2) 1 de 1, 2 de 2; 3) 2 de 1, 1 de 3;
4) 3 de 1, 1 de 2; 5) 5 de 1;
Ce observăm?
118 Capitolul 4. Metoda backtracking
1. Există componente ale vectorului sol care reţin 0. Această situaţie corespunde
cazului în care moneda respectivă nu este luată în calcul. Din acest motiv, fiecare
componentă a vectorului sol va fi iniţializată cu o valoare aflată înaintea tuturor celor
posibile, adică cu -1.
Fie l(i,j) un element al matricei. Acesta poate lua valori între 0 şi 15. Se
consideră ieşirile spre nord, est, sud şi vest, luate în această ordine. Pentru fiecare
direcţie cu ieşire se reţine 1, iar în caz contrar, se reţine 0. Un şir de patru cifre 1
sau 0 formează un număr în baza 2. Acest număr este convertit în baza 10 şi
reţinut în l(i,j). De exemplu, pentru o cameră care are ieşire în nord şi vest,
avem 1001(2)=9(10).
Rezolvare
1. O cameră vizitată se reţine prin coordonatele ei: lin (linia) şi col(colana). Din
acest motiv, pentru a reţine un traseu vom utiliza o matrice cu două coloane şi mai
multe linii: sol. De exemplu, dacă camera iniţială este cea de coordonate (2,2)
o soluţie este (2,2), (2,3), (1,3).
120 Capitolul 4. Metoda backtracking
Exerciţii
1. Adaptaţi rezolvarea pentru un labirint în care fiecare căsuţă reţine valoarea 1
sau 0 (1 semnificând căsuţă plină, prin care nu se poate trece, iar 0 căsuţă liberă,
pe unde se poate trece). Ca şi în problema prezentată, deplasarea se poate face
dintr-o căsuţă în orice altă căsuţă alăturată, orizontal sau vertical, cu condiţia ca ea
să existe şi să fie liberă. Validaţi poziţia iniţială a omului (lin, col), astfel încât
aceasta să corespundă unei căsuţe libere. Estimaţi spaţiul de memorie utilizat
în această variantă.
begin if (k==n*n)
if k=n*n then { for (i=1;i<=k-1;i++)
begin cout<<sol[i][0]<<" "
for i:=1 to k-1 do <<sol[i][1]<<endl;
writeln(st[i,1],' ', cout<<lin<<" "<<col;
st[i,2]); exit(EXIT_SUCCESS);
writeln(lin,' ',col); }
halt; else
end { sol[k][0]=lin;
else sol[k][1]=col;
begin for (i=0;i<=7;i++)
st[k,1]:=lin; st[k,2]:=col; { linie=lin+x[i];
for i:=1 to 8 do coloana=col+y[i];
begin if (linie<=n && linie>=1
linie:=lin+x[i]; && coloana<=n &&
coloana:=col+y[i]; coloana>=1 &&
if (linie<=n) and t[linie][coloana]==0)
(linie>=1) and {
(coloana<=n) and t[linie][coloana]=1;
(coloana>=1) and back(k+1,linie,coloana);
(t[linie,coloana]=0) t[linie][coloana]=0;
then begin
}
t[linie,coloana]:=1;
}
back(k+1,linie,
}
coloana);
}
t[linie,coloana]:=0;
end;
main()
end
{ cout<<"n=";
end
cin>>n;
end;
back(1,1,1);
begin }
write ('n='); readln(n);
back(1,1,1);
end.
Probleme propuse
1. Avem la dispoziţie 6 culori: alb, galben, roşu, verde, albastru şi negru. Să se
precizeze toate drapelele tricolore care se pot proiecta, ştiind că trebuie
respectate regulile:
orice drapel are culoarea din mijloc galben sau verde;
cele trei culori de pe drapel sunt distincte.
2. Dintr-un grup de n persoane, dintre care p femei, trebuie formată o delegaţie
de k persoane, din care l femei. Să se precizeze toate delegaţiile care se
pot forma.
3. La o masă rotundă se aşează n persoane. Fiecare persoană reprezintă o
firmă. Se dau k perechi de persoane care aparţin unor firme concurente. Se
cere să se determine toate modalităţile de aşezare la masă a persoanelor,
astfel încât să nu stea alături două persoane de la firme concurente.
126 Capitolul 4. Metoda backtracking
17. Se dau N puncte albe şi N puncte negre în plan, de coordonate întregi. Fiecare
punct alb se uneşte cu câte un punct negru, astfel încât din fiecare punct, fie el
alb sau negru, pleacă exact un segment. Să se determine o astfel de
configuraţie de segmente astfel încât oricare două segmente să nu se
intersecteze. Se citesc N perechi de coordonate corespunzând punctelor albe
şi N perechi de coordonate corespunzând punctelor negre.
18. Să se genereze toate permutările de N cu proprietatea că oricare ar fi 2≤i≤N,
există 1≤j≤i astfel încâtV(i)-V(j)=1. Exemplu: pentru N=4, permutările
cu proprietatea de mai sus sunt:
2134, 2314, 3214, 2341, 3241, 3421, 4321.
19. O trupă cu N actori îşi propune să joace o piesă cu A acte astfel încât:
24. Fiind date numele a n soldaţi, ce algoritm vom utiliza pentru a lista toate
grupele de câte k soldaţi? Se ştie că într-o grupă, ordinea prezintă importanţă.
a) Generarea aranjamentelor; b) Generarea combinărilor;
c) Generarea permutărilor; d) Generarea tuturor partiţiilor.
25. Fiind date n numere naturale, ce algoritm vom utiliza pentru a determina
eficient o submulţime maximală de numere naturale distincte?
Indicaţii
6. Deşi algoritmul este asemănător, nu este acelaşi, trebuie pusă o condiţie
suplimentară. De exemplu, în cuvântul “mama” nu se poate inversa a de pe poziţia
2 cu a de pe poziţia 4.
7. Dacă vectorul care reţine numele persoanelor este V, în loc să se afişeze i, se
va afişa V[i].
23. b). 24. a). 25. d).
Explicaţie: primele două variante prezintă soluţii exponenţiale, a treia este în
O(n2). Dar dacă sortăm numerele, atunci le putem afişa pe cele distincte dintr-o
singură parcurgere. Sortarea se poate efectua în O(n×log(n)), iar parcurgerea
în O(n). Prin urmare, complexitatea este O(n×log(n)).
26. d).
Explicaţie: pe fiecare nivel al stivei se caută succesorii în ordinea n, n-1, ..., 1.
129
Capitolul 5
Metoda Greedy
5.1. Generalităţi
Enunţ general:
Sol:=∅;
Repetă
Alege x∈A;
Dacă este Posibil atunci Sol←Sol+x;
_ Până când am obţinut soluţia
Afişează Sol;
Soluţia este iniţial vidă şi, pe rând, se alege dintre elementele mulţimii A,
element cu element, până când se obţine soluţia cerută.
Enunţ. Într-o sală, într-o zi, trebuie planificate n spectacole. Pentru fiecare
spectacol se cunoaşte intervalul în care se desfăşoară: [st,sf). Se cere să se
planifice un număr maxim de spectacole, astfel încât să nu se suprapună.
P2 Primul spectacol programat este cel care se termină cel mai devreme.
Demonstraţie
Fie I1 I2 ... Ik şirul spectacolelor alese de algoritm şi O1 O2 ... Os o
soluţie optimă.
C 2 4 6
G 2 1 3
g[i]:=g[i+1]; i=1;
g[i+1]:=man; while (gv>0 && i<=n)
inv:=true; { if (gv>g[i])
man1:=ordine[i]; { cout<<"Obiectul "
ordine[i]:=ordine[i+1]; <<ordine[i]<<' '
ordine[i+1]:=man1 <<1<<endl;
end gv-=g[i];
until not inv; castig+=+c[i];
castig:=0; }
i:=1; else
while (gv>0) and (i<=n) do { cout<<"Obiectul "
begin <<ordine[i]<<' '
if gv>g[i] <<gv/g[i]<<endl;
then castig+=c[i]*gv/g[i];
begin gv=0;
writeln('Obiectul }
',ordine[i],' ',1); i++;
gv:=gv-g[i]; }
castig:=castig+c[i] cout<<"Castig total="<<castig;
end }
else
begin
writeln('Obiectul
',ordine[i],
' ',gv/g[i]:1:2);
castig:=castig+
c[i]*gv/g[i];
gv:=0
end;
i:=i+1
end;
writeln('Castig
total=',castig:3:2)
end.
E=a1x1+a2x2+...+anxn,
unde, a1, a2, ..., an sunt elementele mulţimii A într-o anumită ordine pe care
trebuie s-o determinaţi.
Rezolvare. Vom sorta crescător elementele mulţimii A şi obţinem a1, a2, ..., an
şi pe cele ale mulţimii B. E maxim va fi:
Emax=a1bn-m+1+a2bn-m+2+...ambn.
136 Capitolul 5. Metoda Greedy
B=(b1,b2,...,bi,bi+1,...,bm) şi E1=a1b1+...+aibi+ai+1bi+1+...+ambn.
B=(b1,b2,...,bi+1,bi,...,bm) şi E2=a1b1+...+aibi+1+ai+1bi+...+ambn.
E2-E1=aibi+1+ai+1bi-aibi-ai+1bi+1=bi+1(ai-ai+1)-bi(ai-ai+1)=
(bi+1-bi)(ai-ai+1)≥0.
Cazul 2: m>n.
E’-E=a1(b’1-b1)+a2(b’2-b2)+...+am(b’m-bm)>=0.
begin main()
write('M=');readln(m); { cout<<"M="; cin>>m;
for i:=1 to m do readln(A[i]); for(i=1;i<=m;i++) cin>>A[i];
write('N=');readln(N); cout<<"N="; cin>>n;
for i:=1 to n do readln(B[i]); for(i=1;i<=n;i++) cin>>B[i];
Sort(m,A); Sort(m,A);
Sort (n,B); Sort(n,B);
for i:=1 to m do for(i=1;i<=m;i++)
E:=E+A[i]*B[n-m+i]; E+=A[i]*B[n-m+i];
writeln ('Emax=', E); cout<<"Emax="<<E;
end. }
Enunţ. Se dau n tipuri de bancnote de valori b1, b2, ..., bm (numere naturale
strict mai mari ca 0). Din fiecare tip se dispune de un număr nelimitat de bancnote.
De asemenea, se ştie că vom avea întotdeauna bancnota cu valoarea 1. Fiind dată
o sumă S, număr natural, se cere ca aceasta să fie plătită prin utilizarea unui
număr minim de bancnote.
La fiecare pas, alegem acea mutare care aşează calul în poziţia cel mai greu
accesibilă la pasul următor. Fie calul în poziţia (l,c). Teoretic, din acea poziţie,
calul poate fi mutat în alte 8 poziţii. Desigur, nu toate sunt accesibile, deoarece
pentru unele dintre ele calul părăseşte tabla, iar pentru altele se ajunge în poziţii
deja vizitate. Dintre toate poziţiile în care se poate ajunge, se alege cea care este
cât mai izolată. Vezi funcţia Numar.
În practică, s-ar putea ca două oraşe să nu fie unite printr-o şosea. Asta nu
face inutilizabilă actuala problemă, pentru că se poate considera că oraşele
sunt unite printr-o şosea cu ∞ km (pentru calculator, un număr foarte mare).
Fie oraşele de mai jos şi matricea care reţine distanţele dintre ele:
1
3 1 0 1 5 5 3
5 5 1 0 2 4 1
1
A = 5 1
2
2 0 6
9 5 1
2 5 4 6 0 9
3 0
4
4 3 1 1 9
6
main()
begin { cout<< "Numar noduri=";
write('Numar noduri='); cin>>n;
readln(n); //citesc matricea
{citesc matricea} for (i=1;i<=n;i++)
for i:=1 to n do
for (j=i+1;j<=n;j++)
for j:=i+1 to n do
{ cout<<"A["<<i
begin
<<","<<j<<"]=";
write('A[',i,',',j,']=');
cin>>A[i][j];
readln(A[i,j]);
A[j][i]=A[i][j];
A[j,i]:=A[i,j];
}
end;
//Pentru fiecare nod
{Pentru fiecare nod}
cout<<"Nod de pornire ";
write ('Nod de pornire ');
cin>>v;
readln(v);
S[v]=1;
S[V]:=1;
vs1=v;
vs1:=v;
cout<<"Drumul trece prin ";
write('Drumul trece prin ');
for (i=1;i<=n-1;i++)
for i:=1 to n-1 do
{ min=30000;
begin
for (j=1;j<=n;j++)
min:=30000;
if (A[v][j]!=0 && S[j]==0
for j:=1 to n do
&& min>A[v][j])
if (A[v,j]<>0) and (s[j]=0)
{ min=A[v][j];
and (min>a[v,j])
vs=j;
then }
begin cost+=A[v][vs];
min:=A[v,j]; cout<<vs<<" ";
vs:=j S[vs]=1;
end; v=vs;
cost:=cost+A[v,vs]; }
write(vs,' ');
cost+=A[vs1][v];
S[vs]:=1;
cout<<endl<<"Cost="<<cost;
v:=vs;
}
end;
cost:=cost+A[vs1,v];
writeln('Cost=',Cost);
end.
Probleme propuse
1. În cazul unei mulţimi cu n numere reale, care este complexitatea algoritmului
pentru selectarea unei mulţimi cu număr maxim de elemente pentru care suma
elementelor este maximă?
10. Codul Gray. Pentru primele 8 numere naturale, în tabelul de mai jos, observaţi
numărul scris în baza 10, în binar şi în codul Gray. După cum puteţi observa,
codul Gray se caracterizează prin faptul că reprezentarea a două numere
consecutive diferă exact cu o poziţie binară.
11. Fiind dat un număr natural în cod Gray, g1g2...gn, scrieţi o funcţie care
converteşte numărul în binar b1 b2...bn după următorul algoritm:
b1=g1;
bk=(gk+bk-1) modulo 2; pentru k=2,3...n
12. Scrieţi un program care afişează codurile Gray ale primelor numere naturale
citite.
Răspunsuri / Indicaţii
1. a); 2. c);
3. Sortarea prin metoda bulelor are complexitatea O(n2). Apoi, selecţia specta-
colelor are complexitatea O(n). În concluzie, complexitatea este O(n2). Dar dacă
înlocuim metoda de sortare prin QuickSort, atunci complexitatea medie este
O(n×log(n)). 4. O(n2), dar dacă se schimbă metoda de sortare, se ajunge la
O(n×log(n)). 6. Recunoaşteţi problema rucsacului? În locul capacităţii rucsacului
G, avem suma S, în locul greutăţii fiecărui obiect avem costul lui, în locul câştigului
obţinut din transport, avem câştigul obţinut în urma vânzării produsului. 8. O(n2).
9. O(n3). 12. Se numără în baza 10, numerele se convertesc în binar, apoi în
Gray. 13. De vreme ce vorbim de submulţimi, ne gândim la vectorul caracteristic.
Acesta, însă, nu va mai reţine numărul în binar ci în cod Gray.
Capitolul 6
Programare dinamică
6.1. Generalităţi
Fie o problemă a cărei rezolvare este cerută pentru un număr natural n dat.
Uneori se poate aplica un raţionament de genul: dacă ştim să rezolvăm problema
pentru toate valorile strict mai mici decât n, atunci putem rezolva problema şi
pentru n dat. Dacă este aşa, atunci, pe baza aceluiaşi raţionament, înseamnă că
dacă ştim să rezolvăm problema pentru toate valorile strict mai mici decât n-1,
atunci ştim să rezolvăm problema pentru n-1 şi, aşa cum am arătat, pentru n.
Repetând acest raţionament ajungem să rezolvăm problema pentru n=1 şi,
eventual, pentru n=2. O astfel de problemă este foarte uşor de rezolvat. După care
rezolvăm problema pentru n=3, apoi n=4, ş.a.m.d., până se ajunge la acel n cerut
de problemă. De aici rezultă necesitatea găsirii unor relaţii de recurenţă.
Până acum am rezolvat problema pentru n=1 şi pentru n=2. Dacă notăm cu
un numărul de feluri în care se pot urca n scări, atunci ştim că u1=1 şi u2=2.
Acum, pentru a urca n scări putem proceda astfel: se urcă n-2 scări şi
deodată două scări sau se urcă n-1 scări, după care se mai urcă o scară. În câte
feluri se pot urca n-2 scări? În un-2 feluri. În câte feluri se pot urca n-1 scări? În
un-1 feluri. Atunci, n scări se pot urca în un=un-1+un-2 feluri. De ce le-am adunat?
Pentru că în acest fel se obţin doar soluţii diferite. Orice soluţie care provine din un-1
se termină prin a urca la sfârşit o scară şi orice soluţie care provine din un-2 se
termină prin a urca la sfârşit două scări. Dacă soluţiile sunt diferite, atunci se pot
aduna.
146 Capitolul 6. Programare dinamică
Tot atunci când aţi studiat recursivitatea aţi văzut că rezolvarea prin
utilizarea mecanismului recursivităţii a acestei relaţii este catastrofală din punct de
vedere al timpului de calcul, fiind exponenţială.
Dacă D1, D2,....Dn este un şir de decizii care conduce sistemul în mod optim
din S0 în Sn, atunci trebuie îndeplinită una din condiţiile următoare (principiul de
optimalitate):
1) Dk...Dn este un şir de decizii ce conduce optim sistemul din starea Sk-1
în starea Sn, ∀k, 1≤k≤n;
2) D1...Dk este un şir de decizii care conduce optim sistemul din starea S0
în starea Sk, ∀k, 1≤k≤n;
3) Dk+1...Dn, D1...Dk sunt şiruri de decizii care conduc optim sistemul din
starea Sk în starea Sn, respectiv din starea S0 în starea Sk, ∀k, 1≤k≤n.
Dacă drumul cel mai scurt între Bucureşti şi Suceava trece prin Focşani,
atunci porţiunea din acest drum, dintre Bucureşti şi Focşani, este cea mai
scurtă, ca şi porţiunea dintre Focşani şi Suceava (dacă n-ar fi aşa,
drumul considerat între Bucureşti şi Suceava nu ar fi optim).
Care este cea mai mare sumă care se poate forma astfel şi care sunt
numerele care o alcătuiesc?
Exemplu: Pentru n=4, se consideră triunghiul de mai jos:
2
3 5
6 3 4
5 6 1 4
putea să nu mai putem alege pentru liniile următoare elementele care maximizează
suma. Urmăriţi soluţia optimă din exemplul dat. Prin urmare, "soluţia" propusă nu
este corectă.
Vom forma un triunghi, de la bază către vârf, cu sumele maxime care se pot
forma cu fiecare număr. Dacă citim triunghiul de numere într-o matrice T şi
calculăm sumele într-o matrice C, vom avea relaţiile următoare:
C[n,1]:=T[n,1];
C[n,2]:=T[n,2];
C[n,n]:=T[n,n];
C[i,j]=max{T[i,j]+C[i+1,j],T[i,j]+C[i+1,j+1]},
i∈{1,2,...,n-1}, j∈{1,...,i}.
5 6 1 4.
150 Capitolul 6. Programare dinamică
C[3,1]=max{6+5,6+6}=12;
C[3,2]=max{3+6,3+1}=9;
C[3,3]=max{4+1,4+4}=8;
Vom avea:
12 9 8
5 6 1 4
Linia 2:
C[2,1]=max{3+12,3+9}=15;
C[2,2]=max{5+9,5+8}=14;
15 14
12 9 8
5 6 1 4
Linia 1:
C[1,1]=max{2+15,2+14}=17;
17
15 14
12 9 8
5 6 1 4
Exerciţii
1. Complexitatea algoritmului pentru ultima rezolvare este O(n2). De ce?
2. Puteţi rezolva problema prin metoda înapoi?
L=(3,3,2,2,1).
După aceasta se calculează maximul dintre componentele lui L, iar cel mai
lung subşir crescător format din elementele vectorului V va avea lungimea dată de
acest maxim. Pentru a lista efectiv acel subşir de lungime maximală se procedează
astfel:
se caută maximul din vectorul L precum şi indicele t, la care se
găseşte acest maxim;
se afişează V(t);
se găseşte şi se listează primul element care este mai mare sau egal
cu V(t) şi are lungimea mai mică cu 1 (max-1), se actualizează
valoarea max cu max-1;
algoritmul continuă până când se epuizează toate elementele
subşirului.
n(n − 1)
S = 1 + 2 + ... + n − 1 = ,
2
Notăm numerele citite cu n1, n2, ..., nn. Fie S=n1+n2+...+nn. Evident, orice
sumă care se poate forma cu numerele citite, poate fi un număr între 1 şi S.
Vectorul Sume, un vector cu S componente, va reţine 1 pentru fiecare sumă care
poate fi formată şi 0 în caz contrar.
La pasul i vom calcula toate sumele care se pot calcula cu numerele n1,
n2, ..., ni.
Dar cum reţinem toţi termenii care alcătuiesc o sumă? Mai simplu decât pare
la prima vedere… Un vector, numit Alege, cu S componente, va reţine pentru
fiecare sumă calculată ultimul termen care intră în alcătuirea ei. Atunci când afişăm
soluţia pentru Suma, tipărim Alege[Suma], apoi Alege[Suma-Alege[Suma]]…
Sume Alege
0 1 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0
1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
Manual de informatică pentru clasa a XI-a 155
0 1 1 0 1 0 0 0 0 0 0 2 3 0 3 0 0 0 0 0
1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
0 1 1 0 1 0 1 1 0 1 0 2 3 0 5 0 5 5 0 5
1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
Pentru a nu calcula o sumă de mai multe ori, pentru calculul noilor sume se
utilizează un vector auxiliar, numit Sume1, după care se execută sau logic
între conţinuturile celor doi vectori (vedeţi programul următor).
Cerinţă suplimentară. Odată ales un obiect, acesta trebuie transportat integral (nu
se admite transportul unei părţi din el). În acest fel trebuie să rezolvăm problema
discretă a rucsacului (rucsac 0/1).
a) Castig(i,0) = 0, i = 1...n
b) Castig(0, j) = 0, j = 1...G
Justificarea relaţiilor
Exerciţii
2) O firmă poate fabrica mai multe produse: p1, p2, …, pn. În acest scop firma
dispune de un o sumă de bani, S. Pentru fiecare produs se cunoaşte costul
fabricării şi beneficiul obţinut în urma vânzării lui. Ce produse trebuie să fabrice
firma pentru ca beneficiul obţinut să fie maxim?
IOANA
→
S
OANA →
M
DANA →
A
DANIA
Se va afişa 3 (numărul transformărilor).
A → T1 → T2 → ...Tk → ...B
IOANA →
A
IOANIA
→
S
OANIA →
M
DANIA
pe lângă şirul modificărilor de mai jos:
IOANA
→
S
OANA
M
→ DANA →
A
DANIA
Pentru calculul distanţei vom utiliza matricea Cost cu m+1 linii şi n+1
coloane. Liniile sunt între 0 şi m+1, iar coloanele între 0 şi n+1. Elementul
Cost[i,j] va reţine numărul mimim al transformărilor primelor i caractere ale
cuvântului A în primele j caractere ale cuvântului B. Mai precis, Cost[i,j]
înseamnă costul obţinerii cuvântului intermediar:
Avem:
3) 0<i≤m, 0<j≤n +
Cost[i − 1, j − 1], Ai = B j
Cost[i, j] =
1 + min{cost(i − 1, j − 1), cost(i, j − 1), cost(i − 1, j)}, altfel
B1B2...Bj-1AiAi+1...Am → B1B2...Bj-1BjAi+1...Am
B1) Adăugarea unui caracter (Bj). Atunci costul total este: 1+Cost[i,j-1].
B1B2...Bj-1Ai+1...Am → B1B2...Bj-1BjAi+1...Am
B2) Ştergerea unui caracter, cel aflat pe poziţia Ai. Atunci costul total este
1+Cost[i-1,j].
B1B2...Bj-1BjAiAi+1...Am → B1B2...Bj-1BjAi+1...Am
B3) Modificarea unui caracter, Ai va fi egal Bj. Atunci costul total este
1+Cost[i-1,j-1].
B1B2...Bj-1AiAi+1...Am → B1B2...Bj-1BjAi+1...Am
164 Capitolul 6. Programare dinamică
VDANIA
V012345
I1
O2
A3
N4
A5
C[1,1]. A[1]=I≠D=B[1].
1+min{cost[i-1,j-1],cost[i-1,j],cost[i,j-1]}=1+min{cost[0,0],
cost[0,1],cost[1,0]=1+min(0,1,1)=1.
Semnificaţia: modificarea minimă pentru a transforma ”I” în ”D” are costul 1. Se
face o modificare pentru că (i-1,i-1) trece în (i,j).
VDANIA
V012345
I11
O2
A3
N4
A5
...
VDANIA
V012345
I112334
O222344
A332344
N443234
A554333
min{cost[4,4],cost[4,5],cost[5,4]}= min{3,4,3}.
Avem i=4, j=3. Depistăm apoi, penultima modificare, ş.a.m.d., până când i=0 şi
j=0. Pentru a afişa modificările în ordine inversă vom utiliza o stivă (Sol). De
asemenea, se poate utiliza recursivitatea.
1)A(i,i) = 0;
2)A(i,i + 1) = DIM(i) × DIM(i + 1) × DIM(i + 2);
3)A (i, j) = min{A (i,k ) + A (k + 1, j) + DIM(i) × DIM(k + 1) × DIM( j + 1)}.
i≤k < j
Manual de informatică pentru clasa a XI-a 169
3)
Observaţii
Se pune problema să aflăm cum putem efectua acest calcul utilizând relaţiile
prezentate. Pentru exemplificare vom utiliza exemplul dat la începutul acestui
paragraf. Datorită relaţiei 1, diagonala principală a matricei A (cu 4 linii şi 4
coloane) va fi alcătuită numai din elemente având valoarea 0.
Observăm că:
pentru l de la 1 la n-1
pentru i de la 1 la n-l
j←l+i
generează pentru l=1 linii de la 1 la n-1, pentru l=2, linii de la 1 la n-2, ..., pentru
l=n-1, linia 1. Să observăm faptul că pentru fiecare l şi i, coloana este j. În
acest fel, pentru o anumită valoare a lui l, se obţine o paralelă la diagonala
principală. De exemplu, dacă l=1, i=1, j=2, se obţine A(1,2), dacă l=1, i=2,
j=3, se obţine A(2,3), ..., iar dacă l=1, i=n-1, j=n, se obţine A(n-1,n), adică
prima paralelă la diagonala principală.
În concluzie, pentru exemplul nostru, se fac minimum 120 de înmulţiri, rezultat luat
din matricea A şi anume A(1,4):
0 100 20 120
x 0 10 20
A= .
x x 0 100
x x x 0
172 Capitolul 6. Programare dinamică
0 100 20 120
1 0 10 20
A= .
1 2 0 100
1 3 0
3
Grafic, valorile se pot prezenta într-un arbore. Întrucât arborii vor fi descrişi
într-un capitol separat, vă rog să reveniţi după parcurgerea acelui capitol la
această problemă.
(1,4)
(1,1) (2,4)
(2,3) (4,4)
(2,2) (3,3)
Dacă listăm nodurile neterminale ale acestui arbore, arborele fiind parcurs în
postordine, obţinem modul în care se aşează parantezele pentru calculul
produsului de matrice. Pentru exemplul nostru, subprogramul parc afişează
(2,3), (2,4), (1,4). Aceasta ne spune cum să aranjăm parantezele.
Manual de informatică pentru clasa a XI-a 173
De fapt,
P 1 2 3 4 5
O 0 0 0 1 0
P 1 2 3 4 5
O 0 0 0 1 1
P 1 2 3 4 5
O 0 1 0 1 1
Apoi:
P 1 2 3 4 5
(n-1)!=1!=1.
(2-1)/1+1=2. O 0 1 1 1 1
Rezolvare. Prima idee care ne vine în minte este să generăm toate partiţiile
unei mulţimi (vedeţi generarea lor prin Backtracking) şi să le numărăm. Dar
numărul partiţiilor este aşa de mare astfel încât o asemenea idee se dovedeşte
dezastruasă. Pornind de la partiţiile unei mulţimi cu n elemente, observaţi, pentru
n=3, cum se pot obţine toate partiţiile unei mulţimi cu n+1 elemente.
178 Capitolul 6. Programare dinamică
{1,2,3}, {4}
{1,2,3}
{1,2,3,4}
{1,4}, {2,3}
{1} {2,3} {1}, {2,3,4}
{1}, {2,3}, {4}
{2,4}, {1,3}
{2} {1,3} {2}, {1,3,4}
{2}, {1,3}, {4}
{3,4}, {1,2}
{3} {1,2} {3}, {1,2,4}
{3}, {1,2}, {4}
{1,4}, {2,3}
{1} {2,3} {1}, {2,3,4}
{2,4}, {1,3}
{2} {1,3} {2}, {1,3,4}
{3,4}, {1,2}
{3} {1,2} {3}, {1,2,4}
b2) Dacă cunoaştem numărul partiţiilor unei mulţimi cu n elemente, partiţii care
sunt alcătuite din k-1 mulţimi, atunci din fiecare astfel de mulţime se poate obţine
o altă partiţie cu k mulţimi ale mulţimii cu n+1 elemente, adăugând la fiecare
partiţie mulţimea alcătuită din elementul n+1. În concluzie, în acest mod vom
obţine alte S(n,k-1) partiţii cu k submulţimi ale unei mulţimi cu n+1 elemente. În
exemplul dat, avem:
{1,2,3} {1,2,3} {4}
Cum alte posibilităţi de obţinere a partiţiilor unei mulţimi cu k clase ale unei
mulţimi cu n+1 elemente nu există şi cum, astfel obţinute, partiţiile nu se repetă,
relaţia este:
S(n+1,k)=S(n-1,k)+k*S(n,k).
Probleme propuse
1. Dintr-un element (ai,j) al unei matrice An,n se poate ajunge în elementele
ai+1,j, ai+1,j+1, ai+1,j-1. Ştiind că fiecare element al matricei reţine un număr
natural, se cere un drum care îndeplineşte condiţiile problemei şi uneşte un
element de pe linia 1 cu unul de pe linia n astfel încât suma numerelor reţinute de
elementele pe unde trece drumul să fie maximă.
3 1 3
Exemplu: Pentru n=3 şi matricea A = 4 1 2 ,
3 7 1
drumul este: a1,1, a2,1, a3,2, iar suma este 3+4+7=14.
Rezolvaţi problema prin utilizarea a 3 metode:
a) Greedy euristic (euristică) - se obţine o soluţie "suficient de bună", deşi
de cele mai multe ori, neoptimă. Se alege un element de maxim de pe linia
1, apoi, pornind de la el, cel mai mare element accesibil de pe linia 2, ...
Care este complexitatea algoritmului în acest caz?
b) Backtracking;
c) Prin programare dinamică - în acest caz, să se rezolve problema prin
metoda înainte şi prin metoda înapoi.
2. Problema diligenţei. Un comis voiajor are de făcut o călătorie cu diligenţa între
două oraşe din vestul sălbatic. Călătoria cu diligenţa se desfăşoară în n etape. În
fiecare etapă diligenţa merge, fără întrerupere, între două oraşe. După o etapă,
diligenţa este schimbată, iar comis-voiajorul decide în care oraş va merge în etapa
următoare. Se ştie că în fiecare etapă, cu excepţia ultimei etape, se poate ajunge
în unul dintre cele k oraşe. Se cunoaşte oraşul de start, S şi cel final, F (în ultima
etapă se ajunge în oraşul F). Pentru orice etapă se cunosc toate distanţele între
oraşele care sunt puncte de pornire şi cele care sunt puncte de destinaţie.
S F
k oraşe
Etapa 1 Etapa 2 Etapa n
Se cere să se decidă care sunt oraşele destinaţie pentru o anumită etapă, astfel
încât lungimea totală a drumului, care trebuie afişată, să fie minimă.
Marius Popescu
3. "Lucrare de control". Mai multor elevi li se cere să pună într-o anumită ordine
un număr de n<200 cuvinte formate numai din litere mici - ordinea exprimă faptul
că aceste cuvinte se succed după o anumită logică.
Exemplu: platon kant marx stalin havel.
Ideea de bază este de a afla, pentru fiecare V[i], numărul de elemente din
şir care îl preced şi sunt mai mici decât el.
6. Mere-pere. Se consideră n camere distincte, situate una după alta, astfel încât
din camera i (i∈{1,2,...,n-1}) se poate trece doar în camera i+1. În fiecare
cameră se găsesc mere şi pere în cantităţi cunoscute (bucăţi).
Horia Georgescu
Manual de informatică pentru clasa a XI-a 183
Se folosesc secvenţele 4 5 şi 3.
10. Pentru un triunghi ABC, cu vârfurile de coordonate întregi, definim costul său ca
fiind minimul ariilor dreptunghiurilor cu laturile paralele cu axele de coordonate care
au pe laturi vârfurile unui triunghi. De exemplu, cu vârfurile de coordonate (1,0),
(5,0), (0,3) costul ataşat este egal cu 15. Fie un poligon convex, cu
coordonatele vârfurilor numere întregi. Numim triangularizare a poligonului o
partiţionare a sa ale cărei vârfuri sunt vârfuri ale poligonului dat. Numim costul unei
triangularizări ca fiind suma costurilor triunghiurilor componente. Problema constă
în realizarea unei triangularizări de cost minim.
Indicaţii
1. Vezi “problema triunghiului”!
6. Aparent, se alege minimul dintre mere şi pere din fiecare cameră. Să analizăm
exemplul următor:
1 2
M 3 11
P 4 14
Dacă luăm merele din prima cameră, în a doua cameră vom avea 14 mere
şi 14 pere. Orice am alege, costul este 3+14=17.
Dar dacă luăm perele, atunci în a doua cameră vom avea 11 mere şi 18
pere. Alegem merele şi avem costul 4+11=15<17. În concluzie, rezolvarea prin
alegerea minimului nu este corectă.
Dacă ar fi să calculăm minimul după toate variantele de tip MP...PMPM,
avem un algoritm în O(2n). Acest lucru se observă uşor dacă, de exemplu, în loc
de M punem 0 şi în loc de P punem 1.
Fie Mi, i=1...n, merele care se află iniţial în camera i.
Fie Pi, i=1...n, perele care se află iniţial în camera i.
Vom nota CMi, i=1, …, n, costul optim dacă se pleacă cu merele din camera i
(i=1...n).
Vom nota CPi, i=1, …, n, costul optim dacă se pleacă cu perele din camera i
(i=1, …, n).
Iniţial, avem:
CMn=Mn;
CPn=Pn.
Pentru i=1, 2, …, n-1 avem:
M i + CPi +1
M + ( M i + M i +1 ) + CPi + 2
i
CM i = min M i + ( M i + M i +1 ) + ( M i + M i +1 + M i + 2 ) + CPi + 3
...
M i + ( M i + M i +1 ) + ( M i + M i +1 + M i + 2 ) + ...( M i + M i +1 ... + M n )
Pi + CM i +1
P + ( Pi + Pi +1 ) + CM i + 2
i
CPi = min Pi + ( Pi + Pi +1 ) + ( Pi + Pi +1 + Pi + 2 ) + CM i + 3
...
Pi + ( Pi + Pi +1 ) + ( Pi + Pi +1 + Pi + 2 ) + ...( Pi + Pi +1 ... + Pn )
Manual de informatică pentru clasa a XI-a 185
7. Se calculează S, suma tuturor numerelor. Se caută cea mai mare sumă mai
mică sau egală cu S, care se divide cu n. Se caută ca la "o problemă cu sume"
numerele care însumate o alcătuiesc. Dacă această sumă nu se poate forma, se
încearcă următoarea, mai mică decât aceasta care se divide cu n, până când se
găseşte o asemenea sumă. Se demonstrează faptul (cum?) că există întotdeauna
o astfel de sumă.
10. Se reţine costul minim triangularizării pentru poligoanele formate din 3, 4, ...,
k-1 puncte, luate în sensul de parcurgere a poligonului. Atunci când calculează
costul minim al triangularizării pentru poligonul cu k puncte, se alege minimul dintre
costul poligonului anterior format, la care se adaugă costul noului triunghi, fie costul
obţinut prin unirea punctelor 1, 2, ..., k-1 cu punctul k.
Capitolul 7
Grafuri neorientate
7.1. Introducere
Uneori, algoritmii trebuie să prelucreze date referitoare la anumite
elemente între care există anumite relaţii. Să analizăm exemplele următoare:
1. Se dau n oraşe. Unele dintre ele sunt unite prin şosele directe (care nu mai
trec prin alt oraş).
2. Se cunosc relaţiile de prietenie dintre n persoane.
3. Se dau n ţări şi se cunoaşte relaţia de vecinătate între ele.
4. Se dau n triunghiuri, iar unele dintre ele sunt asemenea.
unde:
V = {v1,v2,...,vn} este o mulţime finită şi nevidă. Elementele
mulţimii V se numesc noduri (vârfuri).
E este o mulţime finită de perechi neordonate de forma (vi, vj),
unde i≠j, şi vi,vj∈V. Elementele mulţimii E se numesc muchii.
Semnificaţia unei muchii este aceea că uneşte două noduri.
Un graf poate fi desenat aşa cum se observă în
exemplul următor (vezi figura 7.2), unde
1 6
G=(V,E),
V = {1,2,3,4,5,6};
E = {(1,2),(1,3),(1,5),(2,3),(3,4), 2
(4,5)} 5
3
Notaţie: în graful G=(V,E), vom nota cu n 4
numărul nodurilor şi cu m numărul muchiilor.
Figura 7.2.
Observaţii Alt exemplu de graf neorientat
Două noduri distincte pot fi unite prin cel mult o muchie. În exemplul de
mai sus, (1,2) este muchia care uneşte nodul 1 cu nodul 2. Dacă scriem
(2,1), ne referim la aceeaşi muchie (perechea este neordonată).
Nu există o muchie care uneşte un nod cu el însuşi (o muchie uneşte
două noduri distincte).
Definiţia 7.3. Într-un graf neorientat, prin gradul unui nod v se înţelege
numărul muchiilor incidente cu nodul v şi se notează cu d(v). Un nod
cu gradul 0 se numeşte nod izolat, iar unul cu gradul 1 se numeşte nod
terminal.
În exemplul dat, d(2)=2, d(1)=3, d(6)=0 (6 este nod izolat).
Definiţia este restrictivă, în unele lucrări veţi întâlni definiţii mai puţin restrictive, de
1
O relaţie utilă: fie un graf neorientat cu n noduri şi m muchii. Dacă notăm cu d1,
d2, ..., dn gradele celor n noduri, atunci avem relaţia:
d 1 + d 2 + d 3 + ...d n = 2m.
Fie afirmaţia: nodul i este izolat. Pentru exemplul 1., înseamnă că nu există
nici o şosea care leagă oraşul i cu alt oraş, pentru exemplul 2., înseamnă că
persoana i nu are nici un prieten, pentru exemplul 3., înseamnă că ţara i nu se
învecinează cu nici o ţară (este situată pe o insulă), pentru exemplul 4., înseamnă
că nu există nici un triunghi dintre celelalte n-1 triunghiuri care să fie asemenea cu
triunghiul i.
În acest paragraf, prezentăm principalele structuri de date prin care grafurile pot
fi memorate în vederea prelucrării lor. De la început, precizăm faptul că vom alege o
structură sau alta în funcţie de :
2
Pentru fiecare structură de date pe care o vom folosi, vom avea câte o
procedură (funcţie) care citeşte datele respective. Toate aceste subprograme se
2
Modul de alegere a structurii îl veţi înţelege pe parcursul studiului acestui capitol.
Manual de informatică pentru clasa a XI-a 189
6
Fişierul text:
1 6 1 2
1 3
1 5
2 2 3
3 4
5
Figura 7.3.
4 5
3
4 Exemplu de graf neorientat
0 1 1 0 1 0
1
0 1 0 0 0
1 1 0 1 0 0
A6, 6 =
0 0 1 0 1 0
1 0 0 1 0 0
0 0 0 0 0 0
Observaţii
1. Întrucât, din modul în care a fost definit graful, rezultă că nu există muchii de la un
nod la el însuşi, rezultă că elementele de pe diagonala principală reţin 0:
ai ,i = 0, ∀i ∈ {1,2,..., n} .
190 Capitolul 7. Grafuri neorientate
ai , j = a j ,i , ∀i, j ∈ {1,2,..., n} .
4. Tot aşa, suma elementelor de pe coloana j, j ∈{1, 2, ..., n}, are ca rezultat
gradul nodului j, d(j).
6. Dacă graful citit are un număr mic de muchii, atunci matricea de adiacenţă este o
formă ineficientă de memorare a lui, pentru că ea va reţine o mulţime de 0.
1 1 -> 2,3,5
6
2 -> 1,3
3 -> 1,2,4
2 4 -> 3,5
5 5 -> 1,4
3 6 ->
Figura 7.4. 4
Start 5 7 9 11 12 0
192 Capitolul 7. Grafuri neorientate
1 2 3 4 5 6 7 8 9 10 11 12
T[0] 2 1 3 1 5 1 3 2 4 3 5 4
T[1] 0 0 1 0 3 0 2 4 8 0 10 6
Exemplu de utilizare
begin k++;
k:=0; T[0][k]=i;
Assign(f,Nume_Fis); T[1][k]=Start[j];
Reset(f); Start[j]=k;
Readln(f,n); }
while(not eof(f)) do f.close();
begin }
readln(f,i,j);
k:=k+1;
T[0,k]:=j;
T[1,k]:=Start[i];
Start[i]:=k;
k:=k+1;
T[0,k]:=i;
T[1,k]:=Start[j];
Start[j]:=k;
end;
close(f);
end;
Definiţia 7.4. Prin graf complet vom înţelege un graf neorientat în care
oricare două noduri sunt adiacente. Vom nota un graf complet prin Kn, unde
n este numărul de noduri ale grafului.
2 3
Figura 7.5. 4
Exemplu de graf complet
Relaţii utile:
1. Într-un graf complet, gradul oricărui nod este n-1. Evident, din fiecare nod,
pleacă (sosesc) n-1 muchii.
n(n − 1)
2. Într-un graf complet, avem relaţia: m= , unde m este numărul de
2
muchii, iar n, numărul de noduri.
n(n − 1)
2
şi corespunde unui graf complet. Se ştie că, fiind dată o mulţime A cu n
elemente, avem 2n submulţimi disjuncte ale acesteia (aici este inclusă şi
submulţimea vidă şi A). Prin urmare, avem:
n(n−1)
2 2
Definiţia 7.5. Un graf parţial al unui graf neorientat dat G=(V,E) este un
graf G1=(V,E1), unde E1⊆E.
Un graf parţial al unui graf dat, este el însuşi sau se obţine din G prin
suprimarea anumitor muchii. Priviţi exemplul de mai jos:
Figura 7.6.
Obţinerea unui 1 1
graf parţial
2 3 3
rezultă 2
4 4
G=(V,E) G1=(V,E1)
1. Fiind date n persoane, între unele dintre ele există o relaţie de prietenie. Asociem
acestei situaţii un graf G. După un timp, unele persoane se ceartă. În teoria grafurilor,
aceasta înseamnă că în G se suprimă anumite muchii şi astfel, se obţine un graf
parţial G1.
2. Fiind date n oraşe, unele dintre ele sunt unite printr-o şosea directă (care nu mai
trece prin alte oraşe). Asociem situaţiei date un graf G. Datorită precipitaţiilor, anumite
şosele se inundă şi nu mai pot fi utilizate. Aceasta înseamnă că în G se suprimă
anumite muchii şi se obţine un graf parţial G1.
198 Capitolul 7. Grafuri neorientate
Un subgraf al unui graf G este el însuşi sau se obţine din G prin suprimarea
anumitor noduri şi a tuturor muchiilor incidente cu acestea. Priviţi exemplul de
mai jos:
Figura 7.7. 1 1
Obţinerea unui
subgraf
2 3 3
rezultă
4 4
G=(V,E) G1=(V1,E1)
3
În acest paragraf vom exemplifica parcurgerile doar în cazul grafurilor conexe. Cum noţiunea nu
a fost prezentată până în acest moment, precizăm doar că vom exemplifica parcurgerea grafurilor
în care oricare două noduri sunt "legate" printr-o succesiune de muchii.
Manual de informatică pentru clasa a XI-a 199
...
Parcurgerea continuă în acest mod până când au fost vizitate toate nodurile
accesibile.
1
Pentru graful alăturat avem:
2 6 3 Nod pornire 1: 1 3 6 2 7 5 4
Nod pornire 3: 3 7 6 1 2 5 4
Nod pornire 6: 6 3 1 7 2 5 4
4 5 7
Figura 7.8.
1
Pentru graful alăturat, avem:
Nod pornire 1: 1 3 7 6 2 5 4
2 6 3
Nod pornire 3: 3 7 6 1 2 5 4
Nod pornire 6: 6 3 7 1 2 5 4
4 5 7
Figura 7.9.
Cum m<n2, pentru parcurgere este preferabil ca graful să fie memorat prin liste
de adiacenţă. Cu toate acestea, pentru simplitate, de multe ori vom efectua
parcurgeri pornind de la matricea de adiacenţă.
7.7. Lanţuri
5 Figura 7.10.
Problema 7.1. Fiind dat un graf şi două
noduri ale sale a şi b, să se scrie un program care decide dacă între ele există un lanţ
sau nu, iar în caz că acest lanţ există, se cere să se afişeze lanţul.
Problema 7.2. Fiind dat un graf G, cum putem obţine matricea lanţurilor?
Răspunsul este uşor de dat. Parcurgem graful începând cu nodul 1. Pentru toate
nodurile j vizitate, vom avea L(1,j)=1, completând astfel prima linie a matricei.
Apoi, vom parcurge din nou, graful, pornind de la nodul 2. Pentru toate nodurile j,
vizitate, vom avea L(2,j)=1, apoi parcurgem graful începând cu nodul 3.... ş.a.m.d.
O anumită îmbunătăţire a algoritmului se obţine dacă ţinem cont de faptul că matricea
lanţurilor este simetrică (de ce?). Lăsăm ca exerciţiu scrierea acestui program.
Întrebare: care este complexitatea acestui algoritm?
Definiţia 7.9. Un graf neorientat G=(V,E) este conex, dacă pentru orice
pereche de noduri x,y∈V, există un lanţ în care
Figura 7.11.
extremitatea iniţială este x şi extremitatea finală 1
este y.
2 3 5
Problema 7.3. Fiind dat un graf G=(V,E), să se scrie un program care să decidă
dacă graful dat este sau nu conex.
Rezolvare. Ţinând cont de cele învăţate, problema nu este grea. Putem utiliza
una din metodele de parcurgere învăţate, DF sau BF. Ideea este următoarea: dacă,
pornind de la un nod, printr-una din metodele de parcurgere, ajungem să vizităm toate
celelalte noduri, atunci graful dat este conex. Cum putem şti dacă am vizitat toate
nodurile? Simplu, după parcurgere, toate componentele vectorului s reţin 1. Rămâne
sarcina dvs. să scrieţi acest program.
Rezolvare. După cum uşor vă puteţi da seama, o parcurgere a grafului (DF sau
BF) pornind de la un anumit nod, vizitează toate nodurile componentei conexe care
îl conţine. Pentru fiecare nod vizitat, s[i] reţine 1. Dacă, după o parcurgere, mai
rămân noduri nevizitate, parcurgerea se reia începând de la primul nod nevizitat.
Evident, numărul componentelor conexe este egal cu numărul de parcurgeri
necesare pentru a fi vizitate toate nodurile.
begin main()
CitireN('Graf.txt',A,n); { CitireN("Graf.txt",A,n);
for i:=1 to n do for (i=1;i<=n;i++)
if s[i]=0 then if (s[i]==0)
begin { cout <<"Comp conexa"
writeln('Comp conexa'); <<endl;
df_r(i); df_r(i);
writeln; cout<<endl;
end }
end. }
7.10. Cicluri
Definiţia 7.11. Un lanţ L care conţine numai muchii distincte şi pentru care
nodul iniţial coincide cu nodul final se numeşte ciclu. Dacă, cu excepţia
ultimului nod, care coincide cu primul, lanţul este elementar, atunci ciclul
este elementar (adică, cu excepţia ultimului nod, care coincide cu primul,
conţine numai noduri distincte).
Problema 7.5. Fiind dat un graf conex, G=(V,E), să se scrie un program care
decide dacă graful conţine cel puţin un ciclu.
Rezolvare. Începem prin a observa că dacă graful nu este conex, putem rezolva
problema verificând dacă există un ciclu într-o componentă conexă a sa. Pentru
simplitate, am preferat să considerăm că graful este conex. Şi aici, problema se poate
rezolva pornind de la o parcurgere DF. Graful conţine cel puţin un ciclu dacă, în
timpul parcurgerii, algoritmul va ajunge în situaţia de a vizita un nod de două ori
Manual de informatică pentru clasa a XI-a 211
(tentativă oricum respinsă, pentru că algoritmul testează acest lucru, vedeţi rolul
vectorului s). Vom da un exemplu, cu graful de mai jos, pe care îl parcurgem DF.
Observaţie
unde m este numărul de muchii, iar n este numărul de noduri. Dacă relaţia este
verificată, înseamnă că graful nu conţine cicluri, altfel, dacă m>n-1 înseamnă că
graful conţine cel puţin un ciclu, iar dacă m<n-1 înseamnă că nu este conex, şi ar
contrazice cerinţa. De unde această observaţie? Pentru a o înţelege, trebuie să
studiem arborii…
3
Figura 7.17. 7
Teorema 7.1. Un graf G=(V,E), fără vârfuri izolate, este eulerian dacă şi
numai dacă este conex şi gradele tuturor vârfurilor sale sunt numere pare.
Demonstraţie
⇒ Fie un graf fără vârfuri izolate care conţine un ciclu eulerian (graf eulerian).
Demonstrăm că graful este conex şi gradele tuturor vârfurilor sale sunt pare.
a) Graful este conex. Fie un ciclu eulerian. Fie x şi y două vârfuri ale grafului. Cum
x nu este izolat, rezultă că există o muchie incidentă cu x. Tot aşa, există o
muchie incidentă cu y. Cum ciclul este eulerian (conţine toate muchiile) va
conţine şi cele două muchii. Prin urmare, x şi y sunt unite printr-un lanţ. Cum x şi
y au fost alese întâmplător, rezultă că oricare două vârfuri sunt unite printr-un
lanţ. În concluzie, graful este conex.
Manual de informatică pentru clasa a XI-a 213
În acest fel am găsit un ciclu (care începe şi se termină cu v). Să-l notăm cu C.
Există două posibilităţi:
1) Ciclul obţinut este eulerian, caz în care teorema este demonstrată.
2) Ciclul nu este eulerian. În acest caz, operăm asupra grafului G, renunţând la
muchiile selectate. În acest fel, se obţine un graf parţial al lui G pe care îl notăm
H. În acelaşi timp, reţinem ciclul găsit. Din analiza grafului H rezultă:
gradele tuturor vârfurilor sale sunt pare (pentru fiecare vârf din ciclu, au fost
eliminate muchii în număr par, deci gradul său a rămas par);
mulţimea muchiilor lui H este nevidă (ciclul găsit nu este eulerian);
cel puţin una din muchiile lui H are o extremitate comună (vârf) cu una din
muchiile ciclului selectat (contrar, înseamnă că n-ar exista drum între un vârf
care nu aparţine ciclului C şi unul care aparţine acestuia, caz în care se
contrazice faptul că graful este conex).
Pornind dintr-un astfel de vârf comun, selectăm întocmai ca la început un alt
ciclu C1. Formăm din C şi C1 un nou ciclu (prin intermediul vârfului comun).
Extragem muchiile ciclului C1...
Repetăm procedeul până la selecţia unui ciclu eulerian.
Problema 7.6. Fiind dat un graf conex şi care are gradele tuturor muchiilor
pare, se cere să se găsească un ciclu eulerian.
Rezolvare. Menţionăm că s-ar fi putut da un graf oarecare şi, dacă există, să se
găsească un ciclu eulerian. Ar fi trebuit să verificăm dacă gradele tuturor muchiilor
sunt pare şi dacă graful este conex. Ambele cerinţe sunt foarte uşor de realizat, şi ar fi
complicată înţelegerea algoritmului.
214 Capitolul 7. Grafuri neorientate
eul 1 0 0 0 0 0 0 0 0 0 0 0
sf
eul 1 2 4 3 1 0 0 0 0 0 0 0
sf
eul 1 2 4 5 6 7 4 3 1 0 0 0
begin main()
CitireN('Graf.txt',A,n); { CitireN("Graf.txt",A,n);
eul[1]:=1; eul[1]=1; sf=1;
sf:=1; for (i=1;eul[i];i++)
i:=1; unCiclu(i);
while eul[i]<>0 do for (i=1;i<=sf;i++)
begin cout<<eul[i];
unCiclu(i); }
i:=i+1;
end;
for i:=1 to sf do
write(eul[i]);
end.
Astfel, veţi obţine un ciclu ca o listă liniară simplu înlănţuită. În acest fel,
intercalarea unui alt ciclu, în ciclul iniţial devine o operaţie mai simplă, nefiind
necesară deplasarea elementelor vectorului.
Care este complexitatea algoritmului?
Într-o firmă există n salariaţi. Fiecare salariat lucrează la propriul birou. Se ştie
că unii dintre aceşti salariaţi sunt prieteni. În vederea sporirii productivităţii muncii,
patronul doreşte să amplaseze birourile celor n salariaţi în două camere, astfel încât
prietenii fiecărui salariat să se găsească în celălalt birou. Se cere să se decidă dacă
acest lucru este posibil, iar în caz afirmativ, se cere o soluţie a problemei, adică
salariaţii care lucrează în fiecare cameră.
-1 -1 2
2 5 1
1 1
1 3
4 6 4
1
3 7 5
-1 -1 6
begin main()
if A[nod,k]=1 then { CitireN("Graf.txt",A,n);
if s[k]=0 df_r(1,1);
then df_r(k,v) if (bipart)
else {
if marc[k]<>v cout<<" mult A"<<endl;
then bipart:=false; for (j=1;j<=n;j++)
end if (marc[j]==1)
end; cout<<j<<" ";
cout<<endl<<" mult B"<<endl;
begin
for (j=1;j<=n;j++)
CitireN('Graf.txt',A,n);
if (marc[j]==-1) cout<<j<<" ";
bipart:=true;
}
df_r(1,1);
}
if (bipart) then
begin
writeln('Mult A');
for j:=1 to n do
if marc[j]=1 then write(j,' ');
writeln;
writeln('Mult B');
for j:=1 to n do
if marc[j]=-1 then write(j,' ');
end
end.
4 5
6 7
Figura 7.19.
Uneori, în anumite condiţii, se poate decide dacă un graf este hamiltonian. Vom
prezenta o serie de teoreme în acest sens. Dar, dacă condiţiile din aceste teoreme nu
sunt verificate, nu înseamnă că graful nu este hamiltonian.
Demonstraţie
Dacă v1 este adiacent cu vn, problema este rezolvată. Dacă nu sunt adiacente,
vom demonstra prin reducere la absurd.
Vom presupune că graful nu conţine un ciclu hamiltonian. Fie A, mulţimea
nodurilor care sunt adiacente cu v1 şi B mulţimea nodurilor care nu sunt
adiacente cu vn. Evident, numărul de elemente din A este d(v1) şi numărul de
elemente din B este n-1-d(vn) (fără vn avem n-1 noduri, dintre care d(vn) noduri
sunt adiacente cu vn).
Vom arăta că ∀ vi∈A, ∃ vj∈B. Fie vi∈A. Dacă vi-1 este adiacent cu vn,
atunci se poate forma ciclul (vezi figura 7.20):
v1, v2, ..., vi-1, vn, vn-1, ..., vi, v1.
v1 v2 vi-1 vi vn-1 vn
Prin urmare, vi-1 nu este adiacent cu vn sau, altfel spus, vi-1∈B. Deoarece
pentru orice nod din mulţimea A, există un nod în mulţimea B, rezultă că numărul de
elemente din A este mai mic sau egal cu numărul de elemente din B, adică:
d(v1)≤n-1-d(vn), de unde rezultă d(v1)+d(vn)≤n-1. Această ultimă relaţie
contrazice ipoteza d(v1)+d(vn)≥n. Prin urmare, graful este hamiltonian.
Probleme propuse
1. O cunoştinţă mi-a zis: la mine în birou suntem 5 persoane. Fiecare dintre noi
colaborează cu exact 3 persoane. A zis adevărul?
2. Demonstraţi că într-un graf neorientat numărul nodurilor de grad impar este par.
3. Fiind date n persoane şi m relaţii de prietenie între ele de forma: persoana i este
prietenă cu persoana j, se cere să se stabilească corespondenţele între afirmaţiile
din stânga şi cele din dreapta.
6. La un ştrand există 6 bazine. Unele dintre ele sunt unite printr-o ţeavă prin care
poate circula apa. Astfel, bazinul 1 este unit cu bazinul 2, bazinul 4 cu bazinul 5, şi
bazinul 2 cu bazinul 3.
6.1 Ştiind că fiecare bazin poate fi dotat cu un robinet, se cere numărul minim de
robinete care asigură umplerea tuturor bazinelor.
6.2. Care este numărul minim de ţevi prin care pot uni două bazine, astfel încât să
se poată umple toate bazinele cu un singur robinet? Daţi exemple de bazine unite
care asigură cerinţa problemei.
7. Fiind dat un grup de n persoane, în care dintre situaţiile de mai jos se poate
folosi pentru modelare un graf neorientat?
a) Unele persoane din grup cunosc alte persoane din grup.
b) Unele persoane din grup simpatizează alte persoane din grup.
c) În cazul în care toate persoanele lucrează într-o firmă, unele persoane din grup
sunt şefi pentru alte persoane din grup.
d) Unele persoane din grup sunt prietene cu alte persoane din grup.
3
5
4
6
8. Care este matricea de adiacenţă a 7
Figura 7.21.
grafului?
1 1 1 1 0 1 0 0 1 1 1 0 1 0 0 1 1 1 0 1 0 0 1 1 1 0 1 0
1 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0
1 0 1 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0
1 0 1 1 0 0 1 1 0 1 0 0 0 1 1 0 1 0 0 0 1 1 0 1 0 0 0 1
0
0 0 0 1 0 0 0
0 0 0 0 0 0
0
0 0 0 0 1 0 1
1 1 1 1 1 1
1 0 0 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1
0 0 0 1 0 1 1 0 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 1 0
a) b) c) d)
9. Care este valoarea de adevăr a afirmaţiilor de mai jos (A adevărat, iar F, fals):
9.1 Graful este alcătuit din 2 componente conexe.
1
9.2 [1,7,4] este un lanţ. 2
9.3 [2,1,4,3,1] este un ciclu.
9.4 Nodul 2 este izolat. 3
11. Care dintre matricele de mai jos poate fi matricea de adiacenţă a unui graf
neorientat?
0 1 0 1 0 0 1 0 0 0 1 1 1 1 1 0 1 0 1 0
1 0 1 0 0 1 0 1 0 0 1 1 1 1 1 1 0 1 0 0
0 1 0 1 1 0 1 0 1 1 1 1 1 1 1 0 1 0 1 1
1 0 1 1 1 1 0 1 0 1 1 1 1 1 1 1 0 1 0 1
0 0 1 1 0 0 0 1 1 0 1 1 1 1 1 0 0 1 1 0
a b c d
0 0 0 0 0 1 0 0 0 1 1 0 0 1 1 1
0 0 0 0 1 0 0 0 1 0 0 0 1 0 1 1
0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1
0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0
a b c d Figura 7.25.
224 Capitolul 7. Grafuri neorientate
15. Care este numărul minim şi care este numărul maxim de componente conexe
pe care le poate avea un graf neorientat cu 8 noduri şi 6 muchii?
16. Care este numărul de cifre 0 pe care îl reţine matricea de adiacenţă a unui
graf neorientat cu n noduri şi m muchii?
17. Care este numărul minim şi numărul maxim de noduri izolate pe care îl poate
avea un graf neorientat cu 10 noduri şi 10 muchii?
18. Care este numărul de grafuri neorientate cu 5 noduri.
19. Precizaţi care dintre afirmaţiile
următoare sunt adevărate şi care sunt 1 3
2
false. Toate afirmaţiile se referă la graful
alăturat.
19.1 "6 1 2 3 5 4 7" reprezintă o 5
parcurgere în adâncime a grafului. 4
6
19.2 "3 2 5 1 4 6 7" reprezintă o 7
parcurgere în lăţime a grafului. Figura 7.26.
19.3 Există două noduri din graf pentru care nu există un lanţ care le uneşte.
19.4 "6 1 2 3 5 4 7" este un lanţ în graful dat.
19.5 "3 2 5 1 4 6 7" este un lanţ în graful dat.
19.6 Numărul maxim de muchii care pot fi eliminate astfel încât graful să rămână
conex este 3.
19.7 Numărul minim de muchii care pot fi eliminate pentru ca graful să nu conţină
cicluri este 3.
20. Precizaţi dacă afirmaţiile de mai jos sunt adevărate sau false.
20.1 Cu ajutorul parcurgerii în adâncime se poate determina dacă un graf
neorientat are cel puţin un ciclu.
20.2 Cu ajutorul parcurgerii în lăţime se poate determina dacă un graf este conex.
20.3 Un graf este alcătuit din două componente conexe. Pentru ca graful să devină
conex, este suficient să eliminăm o anumită muchie.
20.4 Un graf este alcătuit din două componente conexe. Fiecare dintre ele
alcătuieşte un graf parţial al grafului dat.
20.5 Un graf este alcătuit din două componente conexe. Fiecare dintre ele
alcătuieşte un subgraf al grafului dat.
20.6 Cu ajutorul pacurgerii în lăţime se poate determina, dacă există, un lanţ între
două noduri ale grafului.
20.7 Cu ajutorul pacurgerii în adâncime se poate determina, dacă există, un lanţ
între două noduri ale grafului.
20.8 Există un graf complet cu n>2 noduri care nu conţine cicluri.
20.9 Orice graf complet este alcătuit dintr-o singură componentă conexă.
Manual de informatică pentru clasa a XI-a 225
Linia 1 n
Linia 2 1, ..., n într-o ordine oarecare.
Linia 3 m
următoarele m linii conţin fiecare câte o pereche de indici i, j.
Manual de informatică pentru clasa a XI-a 227
Exemplu:
3 Programul va afişa:
3 1 2 1 2
2 2 3
2 3
1 2
*
39 . Lucrare în echipă. Se doreşte scrierea unei aplicaţii de informare a călătorilor
privind transportul în comun într-un oraş. Se cunosc cele n staţii de autobuz din oraşul
respectiv. De asemenea, se ştie traseul a k linii de autobuz (staţiile prin care acestea
trec). Se cere ca aplicaţia să furnizeze modul în care o persoană se poate deplasa cu
autobuzul între două staţii date, în ipotezele:
a) În număr minim de staţii.
b) Prin utilizarea unui număr minim de linii de autobuz.
Răspunsuri
8. b).
9. 9.1 A; 9.2 F; 9.3 F; 9.4 F; 9.5 F; 9.6 A; 9.7 F; 9.8 A;
9.9 A; 9.10 A.
10. b). Dacă matricea este de adiacenţă, atunci vă puteţi orienta după gradele
vârfurilor. Evident, graful reprezentat de matricea de adiacenţă trebuie să aibă
vârfurile cu aceleaşi grade cu vârfurile grafului reprezentat în desen.
11. d). Desigur, puteţi desena graful, dar, mai uşor, eliminaţi variantele în care
aveţi 1 pe diagonala principală, sau acelea în care matricea nu este simetrică.
12. 8. Dacă matricea este dată corect, nu este nevoie să desenaţi graful pentru
ca, apoi, sa-i număraţi muchiile. Se ştie că suma gradelor tuturor nodurilor este
egală cu dublul numărului de muchii. Prin urmare, este suficient să însumaţi
elementele reţinute de matrice si să împărţiţi rezultatul la 2.
13. 2.
14. a) Dacă matricea are 4 linii şi 4 coloane, este clar că subgraful ar rezulta prin
eliminarea unui singur nod şi a muchiilor incidente lui. Dacă eliminăm nodul din
centru, se obţin 4 noduri izolate. Oricare alt nod am elimina, rămân un nod cu
gradul 3 şi 3 noduri cu gradul 1.
15. 2 componente conexe şi 5 componente conexe.
16. n2-2m. Matricea de adiacenţă are n2 elemente. Am văzut faptul că suma
tuturor cifrelor de 1 (adică a gradelor vârfurilor) este 2m (unde m este numărul de
muchii).
17. 0 şi 5.
18. 210.
19. 19.1 A, 19.2 A, 19.3 F, 19.4 A, 19.5 F, 19.6 A, 19.7 A.
20. 20.1 A, 20.2 A, 20.3 F, 20.4 F,
20.5 A, 20.6 A, 20.7 A, 20.8 F, 20.9 A.
29., 30. Descompunerea unui graf în componente conexe.
31. Backtracking. O soluţie are lungimea k. Pentru a evita ca un ciclu să fie
afişat de k ori, elementele vor fi aşezate în stivă în ordine strict crescătoare.
32. Fie i<j<k<l 4 noduri care formează un ciclu. Avem:
A(i,j)=1 A(i,l)=1
A(k,j)=1 A (k,l)=1
Astfel, în matricea de adiacenţă se formează un dreptunghi. Trebuie identificate
toate dreptunghiurile astfel formate.
33. 33.1 A; 33.2 F; 33.3 A; 33.4 F. 33.5 A (pentru n=2); 33.6 F; 33.7 F;
33.8 A; 33.9. A.
Manual de informatică pentru clasa a XI-a 229
34. Eliminând baza plicului vom avea un graf eulerian care se poate trasa "fără a
ridica creionul". Se pleacă de la unul dintre nodurile aflate la baza "plicului". După
trasarea ciclului, vom trasa baza "plicului". Evident, în final se ajunge în celălalt nod
al "bazei plicului".
35. Se poate lucra direct pe matricea L. Ideea: pentru camera iniţială vom avea
L(i,j)=1. Pentru toate camerele accesibile, vecine cu ea, vom avea L(i,j)=2,
apoi pentru toate camerele accesibile cu ele vom avea L(i,j)=3, ... ş.a.m.d.
Pentru a obţine această marcare vom parcurge în lăţime graful asociat. Putem
evita memorarea acestuia. Vom introduce în coadă coordonatele camerei iniţiale.
Vom încărca în coadă coordonatele tuturor camerelor vecine pentru care
L(i,j)=1. Pentru fiecare astfel de cameră, pentru care, iniţial, L(i,j)=0, vom
avea L(i,j)=2. Se trece apoi la următorul element din coadă cu care se
procedează asemănător. Se ştie că prin parcurgerea în lăţime se vizitează
nodurile în ordinea lungimii drumului, de la ele, la nodul iniţial. Deducem,
astfel, că marcarea este corectă. Algoritmul se termină când coada este vidă. În
final, se afişează matricea L.
36. Se procedează ca la problema anterioară. Imediat ce a fost vizitată camera
finală, se reface drumul de la camera iniţială către ea. Astfel, se pleacă de la
camera finală, marcată cu k. Printre vecinele acestei camere se caută una care
este marcată cu k-1. Printre camerele vecine cu ea se caută una care este
marcată cu k-2. Se procedează în mod asemănător până se ajunge la camera
iniţială marcată cu 1. Drumul se afişează în ordinea inversă găsirii lui, de la camera
finală la cea iniţială.
37. Algoritmul lui Lee.
38. Asociem problemei un graf neorientat. Nodurile sunt indicii elementelor
vectorului, de la 1 la n. Când conţinuturile a două elemente se pot inversa, nodurile
corespunzătoare sunt unite printr-o muchie. Dacă nodurile i1, i2, ..., ik sunt unite
printr-un drum: atunci interschimbările
(i1, i2), (i2, i3), ..., (ik-1, ik), (ik-1, ik-2), ..., (i2, i1)
inversează conţinuturile elementelor de indice i1 şi ik, lăsând conţinuturile
celorlalte elemente de indici i2, ..., ik-1 nemodificate. O parcurgere în lăţime
determină distanţa minimă între două noduri.
230
Capitolul 8
Grafuri orientate
5
Figura 8.1. 3 4
Exemplu de graf orientat
Manual de informatică pentru clasa a XI-a 231
Observaţii
a) Există numai arcul (vi,vj)∈A - în acest caz, spunem că arcul (vi,vj)∈A este
incident spre exterior cu vi şi spre interior cu vj.
b) Există numai arcul (vj,vi)∈A - în acest caz, spunem că arcul (vj,vi)∈A este
incident spre interior cu vi şi spre exterior cu vj.
c) Există arcul (vi,vj)∈A şi arcul (vj,vi)∈A.
Definiţia 8.3. Într-un graf orientat, prin gradul exterior al unui vârf v vom
înţelege numărul arcelor incidente spre exterior cu v. Gradul exterior al
unui nod va fi notat cu d+(v).
232 Capitolul 8. Grafuri orientate
Definiţia 8.4. Într-un graf orientat prin gradul interior al unui nod v vom
înţelege numărul arcelor incidente spre interior cu v. Gradul interior al
unui nod va fi notat cu d-(v).
Demonstraţie. Relaţia este adevărată, pentru că fiecare arc este incident spre
exterior cu un vârf şi fiecare arc este incident spre interior cu un vârf.
Demonstraţia se face prin inducţie. Dacă n=1, avem 1 graf orientat. Dacă
n=2, cele două noduri pot să nu fie sau să fie adiacente. În acest din urmă caz,
putem avea arcul (v1,v2) sau arcul (v2,v1) sau putem avea ambele arce
(v1,v2) şi (v2,v1). În total, avem 4 grafuri orientate, valoare care rezultă şi din
formulă, dacă înlocuim n cu 2.
Presupunem formula adevărată, adică dacă sunt n vârfuri, avem
n ( n −1)
2
4
grafuri orientate. Trebuie să demonstrăm că dacă sunt n+1 vârfuri, avem
n ( n +1)
2
4
Manual de informatică pentru clasa a XI-a 233
grafuri orientate. Adăugăm vârful n+1. Acest nod poate fi adiacent cu fiecare
dintre celelalte n vârfuri în exact 3 moduri (vedeţi adiacenţa) sau poate să nu fie
adiacent. Atunci, numărul de grafuri orientate cu n+1 noduri este
n ( n −1) n ( n −1) n ( n +1)
+n
4 2 ×4 =n
4 2 = 4 2 .
Pentru fiecare structură de date pe care o vom folosi vom avea câte o
procedură (funcţie) care citeşte datele respective. Toate aceste subprograme se
găsesc grupate în unitatea de program grafuri.pas (pentru Pascal) şi în
grafuri.cpp (pentru C++).
Toate subprogramele pe care le utilizăm citesc datele dintr-un fişier text, în care
pe prima linie vom scrie numărul de noduri (n), iar pe următoarele linii, câte o muchie
(i,j) ca în exemplul următor, în care este prezentat un graf şi liniile fişierului text
care este citit pentru el:
Fişierul 6
1 text: 1 2
6
1 3
1 5
2 2 3
5 3 4
3 4 5
4
Figura 8.4.
0 1 1 0 1 0
1, pentru (i, j) ∈ A
a i, j = 0
0 1 0 0 0
0, pentru (i, j) ∉ A 0 0 0 1 0 0
Pentru graful din figura 8.4, matricea de adiacenţă 0 0 0 0 1 0
este prezentată alăturat. 0 0 0 0 0 0
0 0 0 0 0 0
234 Capitolul 8. Grafuri orientate
Observaţii
1. Întrucât, din modul în care a fost definit graful, rezultă că nu există arce de la un
nod la el însuşi, rezultă că elementele de pe diagonala principală reţin 0 (ai,i=0,
oricare ar fi i∈{1,2,...,n}).
2. Matricea de adiacenţă nu este în mod obligatoriu simetrică.
3. Suma elementelor de pe linia i, i ∈{1,2,...,n} are ca rezultat gradul exterior
al nodului i, d+(i).
4. Tot aşa, suma elementelor de pe coloana i, i ∈{1,2,...,n} are ca rezultat
gradul interior al nodului i, d-(i).
5. Suma tuturor elementelor matricei de adiacenţă este, de fapt, suma gradelor
exterioare (sau interioare) adică suma arcelor, m.
6. Dacă graful citit are un număr mic de muchii, atunci matricea de adiacenţă este o
formă ineficientă de memorare a lui, pentru că ea va reţine o mulţime de 0.
Fie G=(V,A) un graf orientat. Ataşăm fiecărui arc (x,y)∈A o pondere (un
cost): cx,y>0. Priviţi exemplul de mai jos:
2
Modul în care fişierul
grafuri.txt reţine
7
1 datele:
4
5
1 2 1
1 9
3 1 3 9
1 5 3
3 2 4 3
1 3 2 3 7
2 4 3 2
4 1 1
5 2 5 2 4
4 5 4 2
c x, y , x ≠ y, ( x, y ) ∈ A
f : V × V → ℜ+ , f (( x, y )) = ∞, x ≠ y, ( x, y ) ∉ A
0, x=y
Funcţia este reţinută de o matrice, numită matricea ponderilor forma 1.
Pentru graful din exemplu, matricea ponderilor forma 1 este prezentată mai jos:
0 1 9 ∞ 3
∞ 0 7 3 ∞
∞ ∞ 0 ∞ ∞
1 ∞ 2 0 ∞
∞ 4 ∞ 2 0
În continuare, este prezentat subprogramul care realizează citirea grafului în
forma matricei ponderilor în forma 1.
Manual de informatică pentru clasa a XI-a 237
c x, y , x ≠ y, ( x, y ) ∈ A
g : V × V → ℜ, g (( x, y )) = − ∞, x ≠ y, ( x, y ) ∉ A
0, x=y
0 1 9 −∞ 3
− ∞ 0 7 3 − ∞
− ∞ − ∞ 0 − ∞ − ∞
1 −∞ 2 0 − ∞
− ∞ 4 − ∞ 2
0
Definiţia 8.5. Un graf parţial al unui graf orientat G=(V,A) este un graf
G1=(V,A1), unde A1⊆A.
Un graf parţial al unui graf dat este el însuşi sau se obţine din G prin
suprimarea anumitor arce.
1 1
2 3 3
2
rezultă
4 4
Figura 8.6.
Obţinerea unui
graf parţial G=(V,A) G1=(V,A1)
Referitor la exemplul 1 din paragraful 8.1, unele persoane îşi şterg din agendă
numerele altor persoane din grup. Aceasta înseamnă că noul graf nu va mai avea
anumite arce, deci va deveni un graf parţial al grafului iniţial.
Manual de informatică pentru clasa a XI-a 239
Un subgraf al unui graf G este graful G sau se obţine din G prin suprimarea
anumitor vârfuri şi a tuturor arcelor incidente cu acestea.
1 1
2 3 3
rezultă
4 4
Figura 8.7.
Obţinerea unui G1=(V1,A1)
subgraf G=(V,A)
1. Pot exista persoane din grup care îşi pierd telefonul mobil. Astfel, numerele de
telefon ale respectivelor persoane aflate în agenda altora, pentru moment, nu mai
folosesc. De asemenea, ceilalţi din grup nu mai păstrează numerele de telefon ale
acestora. În graful iniţial se renunţă la vârfurile respective şi la arcele adiacente lor.
Astfel, se obţine un subgraf al grafului iniţial.
2. Autorul renunţă la prezentarea anumitor noţiuni. Din nou, se obţine un subgraf al
grafului iniţial.
Definiţia 8.7. Un graf orientat este complet dacă oricare două vârfuri, i şi
j (i≠j), sunt adiacente.
n ( n −1)
2
Lema 8.1. Avem 3 grafuri complete cu n noduri.
Demonstraţia se face prin inducţie. Dacă n=1 avem 1 graf complet. Dacă n=2,
cele două noduri sunt adicente dacă avem arcul (1,2), sau avem arcul (2,1) sau
avem ambele arce (1,2) şi (2,1). În total, avem 3 grafuri orientate complete,
valoare care rezultă şi din formulă, dacă îl înlocuim pe n cu 1.
Presupunând formula adevărată, există
n ( n −1)
2
3
grafuri complete cu n vârfuri. Trebuie să demonstrăm că avem
n ( n +1)
2
3
grafuri complete cu n+1 vârfuri. Pentru fiecare graf complet cu n vârfuri adăugăm
vârful n+1. Acest nod este adiacent cu fiecare dintre celelalte n vârfuri în exact 3
moduri. Astfel, pentru fiecare graf complet cu n vârfuri, avem
3×
3 ×3 = 3 n
...
de n ori
grafuri complete cu n+1 vârfuri. Atunci, numărul de grafuri complete cu n+1 vârfuri
este
n ( n −1) n ( n −1) n ( n +1)
+n
3 2
× 3n = 3 2
=3 2
.
Definiţia 8.8. Un graf orientat este turneu, dacă oricare ar fi două vârfuri i
şi j, i≠j, între ele există un singur arc: arcul (i,j) sau arcul (j,i).
242 Capitolul 8. Grafuri orientate
Proprietăţi
1. Orice graf turneu este graf complet.
n ( n −1)
2. Avem 2 grafuri turneu cu n noduri. Ca şi în cazul numărului de grafuri
2
Demonstraţie. Fie un drum care trece prin k<n vârfuri distincte, [v1v2...vk].
Fie v un vârf prin care nu trece acest drum.
a. Dacă există arcul (v,v1) atunci găsim drumul vv1v2...vk, drum care trece
prin k+1 noduri. Vom presupune că există arcul (v1,v).
b. Dacă există arcul (vk,v) atunci găsim drumul v1v2...vkv, drum care trece
prin k+1 noduri. Vom presupune că există muchia (v,vk).
v1 v2 v3 vk-1 vk
v
Figura 8.9.
Dacă există arcul (vk-1,v), atunci v1v2...vk-1vvk este un drum care îndeplineşte
condiţiile şi trece prin k+1 noduri. Vom presupune atunci că există arcul (v,vk-1).
Dacă există arcul (vk-2,v), atunci v1v2...vk-2vvk-1vk este un drum care
îndeplineşte condiţiile şi trece prin k+1 noduri. Vom presupune că există arcul (v,vk-2).
...
Dacă există arcul (v2,v), atunci v1v2vv3...vk-1vvk este un drum care îndeplineşte
condiţiile şi trece prin k+1 noduri. Vom presupune că există arcul (v,v2).
Atunci v1vv2...vk este un drum care îndeplineşte condiţiile şi trece prin k+1 noduri.
Raţionamentul se repetă până când se obţine dumul care trece prin n noduri
distincte.
La o analiză mai atentă, proprietatea 3 este surprinzătoare. De exemplu, să
presupunem că la un concurs de tenis participă n jucători şi oricare 2 jucători joacă
exact o partidă. Cum remiza este exclusă, înseamnă că acestui concurs i se poate
asocia un graf orientat, în care vârfurile sunt jucătorii iar arcul (vi,vj) are
semnificaţia că jucătorul vi a învins jucătorul vj. Existenţa lanţului elementar care
Manual de informatică pentru clasa a XI-a 243
trece prin toate vârfurile ne asigură că putem aranja cei n jucători într-un şir astfel
încât jucătorul v1 l-a învins pe v2, jucătorul v2 l-a învins pe jucătrorul v3, ..., jucătorul
vn-1 l-a învins pe jucătorul vn.
Ce credeţi, aceasta înseamnă ca jucătorul v1 a câştigat concursul?
Problema 8.1. Fiind dat un graf turneu, se să cere se afişeze un drum elementar
care trece prin toate vârfurile grafului.
Indicaţie: aveţi un excelent exemplu prin care puteţi scrie un program bazat pe
un algoritm inspirat din demonstraţia unei teoreme (proprietăţi). Aţi mai întâlnit un
astfel de exemplu, atunci când s-a scris un program care găseşte un ciclu eulerian.
Întrebare suplimentară: care este complexitatea algoritmului?
Definiţia 8.9. Graful orientat G=(V,A) este tare conex dacă ∀x,y∈V, ∃
drum de la x la y şi drum de la y la x.
Problema 8.2. Fie un graf orientat G=(V,A), memorat prin intermediul matricei de
adiacenţă. Se cere să se determine vârfurile fiecărei componente tare conexă.
Rezolvare
a) Vom numi succesori ai vârfului i, toate nodurile j, pentru care există drum de la
i la j, la care se adaugă i. De exemplu, pentru graful dat, succesorii vârfului 1 sunt
vârfurile 1, 2, 3 şi 4. Pentru a determina toţi succesorii vârfului i, vom efectua o
parcurgere DF a grafului pornind de la acest vârf. Succesorii nodului i vor fi reţinuţi în
vectorul suc.
b) Fie i un vârf al grafului. Vom numi predecesori ai vârfului i, toate vârfurile j,
pentru care există drum de la j la i, la care se adaugă i. Pentru graful dat,
predecesorii vârfului 1 sunt: 1, 2 şi 3.
c) Dacă un vârf este simultan succesor şi predecesor al lui i, atunci el va face parte
din componenta tare conexă a vârfului i. Mulţimea nodurilor cu această proprietate va
fi o componentă tare conexă a grafului. De ce? Pentru că între două vârfuri k şi l,
există atât drum de la k la l (de la k la i şi de la i la l) cât şi drum de la l la k (de la
l la i şi de la i la k). Mulţimea nodurilor cu această proprietate este maximală în
raport cu relaţia de incluziune. Dacă, prin absurd, ar mai exista un vârf cu această
proprietate, care nu aparţine acestei mulţimi, atunci ar trebui să existe drum de la i la
el, şi de la el la i, caz în care acesta ar fi fost găsit prin procedeul dat.
1 2 3 4 5 6 7 1 2 3 4 5 6 7
suc 1 1 1 1 0 0 0 suc 1 1 1 0 0 0 0
pred 1 1 1 0 0 0 0 pred 1 1 1 0 0 0 0
Manual de informatică pentru clasa a XI-a 245
1 2 3 4 5 6 7 1 2 3 4 5 6 7
suc 1 1 1 2 0 0 0 suc 1 1 1 2 0 0 0
pred 1 1 1 2 2 2 0 pred 1 1 1 2 0 0 0
1 2 3 4 5 6 7 1 2 3 4 5 6 7
suc 1 1 1 2 3 0 3 suc 1 1 1 2 3 4 3
pred 1 1 1 2 3 0 3 pred 1 1 1 2 3 4 3
8.7.1. Introducere
Să presupunem că avem n oraşe. Unele dintre ele sunt unite prin şosele.
Există posibilitatea ca pe unele şosele să se poată circula într-un singur
sens. Pentru fiecare şosea care uneşte oraşele i şi j, se cunoaşte
numărul de kilometri între i şi j.
Noi vom studia doi algoritmi care rezolvă aceste probleme. Primul, algoritmul
Roy-Floyd, rezolvă cazul 3 (sursa multiplă, destinaţia multiplă). Al doilea
algoritm, cel al lui Dijkstra, rezolvă cazul 2 (sursa unică, destinaţia multiplă).
Evident, din rezultatele furnizate de oricare din cei doi algoritmi, se poate obţine
răspunsul la prima întrebare. De asemenea, dacă aplicăm algoritmul lui Dijkstra de
n ori, se poate rezolva şi cazul 3 (sursa multiplă, destinaţia multiplă).
Observaţii
Pentru ambii algoritmi, vom considera că între oricare două noduri distincte, i
şi j, există un arc de la i la j şi un arc de la j la i. Dacă arcul nu există, vom
memora pentru el o valoare foarte mare, drept cost, pe care o vom presupune
ca fiind +∞.
În anumite condiţii, algoritmul Roy-Floyd, uşor modificat, poate furniza şi
drumuri de cost maxim. În acest din urmă caz, când nu există nod pentru un
arc, vom considera că el are costul -∞.
Întrucât grafurile neorientate sunt cazuri particulare de grafuri orientate, putem
aplica cei doi algoritmi şi în cazul lor.
1. Iniţial, matricea ponderilor reţine numai lungimea drumurilor directe între două
noduri, adică nu este permis ca un drum între două noduri să treacă printr-un
alt nod. Pentru arce inexistente se reţine, aşa cum s-a precizat, +∞, (în
program, o valoare foarte mare).
2. La început, încercăm să obţinem drumuri mai scurte, între oricare două
vârfuri i,j∈V, permiţând ca acestea să poată trece prin nodul 1. Aceasta
înseamnă că pentru ∀i,j∈V se face comparaţia: A(i,j)>A(i,1)+A(1,j),
adică se compară dacă lungimea drumului direct (care nu trece prin alte noduri)
este mai mare decât cea a drumului care trece prin nodul 1. În caz afirmativ se
face atribuirea A(i,j)←A(i,1)+A(1,j). După acest pas, matricea va reţine
lungimea optimă a drumurilor între oricare două noduri, drumuri care pot trece
prin nodul 1.
248 Capitolul 8. Grafuri orientate
3. Încercăm să obţinem drumuri mai scurte, între oricare două noduri i,j∈V,
permiţând ca acestea să poată trece şi prin nodul intermediar 2. Aceasta
înseamnă că pentru ∀i,j∈V, se face comparaţia: A(i,j)>A(i,2)+A(2,i),
adică se compară dacă lungimea drumului de la i la j, drum care nu poate
trece decât cel mult prin nodul 1 este mai mare decât cea a drumului care trece
prin nodul 2. În caz afirmativ, se face atribuirea A(i,j)←A(i,2)+A(2,i).
După acest pas, matricea va reţine lungimea optimă a drumurilor între oricare
două noduri, drumuri care pot trece prin nodurile intermediare 1 2.
...
Algoritmul continuă în acest mod, prin eventuale îmbunătăţiri succesive ale lungimii
drumurilor, considerând ca noduri intermediare 3, 4, ..., n.
2
7 0 1 9 ∞ 3
1
∞ 7 3 ∞
4
0
1 9 ∞ ∞ 0 ∞ ∞
3
3 1 ∞ 2 0 ∞
∞ 4 ∞ 2 0
1 3
2
5 2
4
Figura 8.11.
Pasul 3. Se caută drumurile optime între oricare două noduri, unde drumurile pot
trece şi prin nodul intermediar 3. Nu se obţine nici o îmbunătăţire.
Pasul 4. Se caută drumurile optime între oricare două noduri, unde drumurile pot
trece şi prin nodul intermediar 4.
A(1,3)=8>A(1,4)+A(4,3)=4+2=6. A(1,3)=6. 0 1 6 4 3
A(2,1)=∞>A(2,4)+A(4,1)=3+1=4. A(2,1)=4. 4 0 5 3 7
A(2,3)=7>A(2,4)+A(4,3)=3+2=5. A(2,3)=5. ∞ ∞ 0 ∞ ∞
A(2,5)=∞>A(2,4)+A(4,5)=3+4=7. A(2,5)=7. 1 2 2 0 4
3 4 4 2 0
A(5,1)=∞>A(5,4)+A(4,1)=2+1=3. A(5,1)=3.
A(5,3)=11>A(5,4)+A(4,3)=2+2=4. A(5,3)=4.
Pasul 5. Se caută drumurile optime între oricare două noduri, unde drumurile pot
trece şi prin nodul intermediar 5. La acest pas nu se obţin îmbunătăţiri. În concluzie,
am obţinut matricea de mai sus, a drumurilor minime.
1) Dacă n=0, matricea ponderilor reţine costul arcelor, dacă ele există, altfel reţine
+∞. Prin urmare, matricea ponderilor reţine costul drumurilor optime în cazul în care
nu este permis ca acest drum să conţină decât pe un singur arc (i,j).
2) Presupunem proprietatea adevărată pentru pasul k. Aceasta înseamnă că
matricea A reţine pentru oricare două vârfuri, i şi j, costul drumului optim între ele,
drum care trece doar prin vârfurile 1, 2, ..., k. Trebuie să arătăm că după pasul k+1,
matricea A va reţine costul drumurilor optime care trec prin primele k+1 vârfuri. Mai
întâi, să observăm că, la pasul k+1, nu se modifică A(k+1,i) şi A(j,k+1),
i,j∈{1,2, ..., n} pentru că nu se obţine îmbunătăţirea costului drumurilor [k+1...i]
sau [j...k+1] dacă se permite ca drumul să treacă şi prin nodul k+1. De altfel,
aceasta permite algoritmului să utilizeze o singură matrice, A, care la pasul 0, este
matricea ponderilor. La pasul k+1, determinăm dacă drumul optim de la i la j,
i,j∈{1,2, ..., n}, trece sau nu prin vârful k+1.
Dacă trece, conform principiilor programării dinamice, drumurile de la i la
k+1 şi de la k+1 la j, sunt optime, iar suma costurilor lor este egală cu costul
drumului optim de la i la j. Prin urmare, A(i,j) obţinut la pasul anterior, care
reţine costul drumului optim care trece numai prin vârfurile 1, 2, k va reţine, o
valoare mai mare decât A(i,k+1)+A(k+1,j). În acest caz, se efectuează
atribuirea: A(i,j)←A(i,k+1)+A(k+1,j).
Dacă drumul optim de la i la j, nu trece prin nodul k+1, atunci valoarea
reţinută de A(i,j) este mai mică decât A(i,k+1)+A(k+1,j). Această
ultimă sumă exprimă, în acest caz, costul unui drum de la i la j, drum care
"ocoleşte" prin nodul k+1. În acest caz, valoarea lui A(i,j), obţinută la pasul
anterior, rămâne nemodificată.
250 Capitolul 8. Grafuri orientate
Până în prezent, am aflat costul drumurilor optime între oricare două vârfuri.
Dar ce valoare are acest rezultat dacă nu cunoaştem şi pe unde trece un astfel de
drum. Astfel, se pune următoarea problemă: fiind dată matricea drumurilor optime şi
fiind date două vârfuri i şi j, se cere să se reconstituie nodurile prin care trece unul
din drumurile optime între i şi j (pot exista mai multe de aceeaşi lungime, minimă).
3 3 1 4
1 2
Figura 8.12. 2
252 Capitolul 8. Grafuri orientate
Prezentarea algoritmului
Pasul 1. Vârful r este adăugat mulţimii S iniţial vidă (S[r]=1);
- costurile drumurilor de la r la fiecare nod al grafului se preiau în vectorul D de
pe linia r a matricei A;
- pentru toate nodurile i, având un cost al drumului de la r la ele, finit, se pune
T(i)=r.
Manual de informatică pentru clasa a XI-a 253
Fie graful de mai jos pentru care se doreşte aflarea drumurilor minime
de la nodul 1 la toate celelalte:
2
7
1
0 1 9 ∞ 3
4
1 9 ∞ 0 7 3 ∞
3
∞ ∞ 0 ∞ ∞
3
1 3 1 ∞ 2 0 ∞
2
∞ 4 ∞ 2 0
5 2
4 Figura 8.13.
S 1 0 0 0 0
T 0 1 1 0 1
T 0 1 2 2 1
Se selectează nodul 4. D 0 1 6 4 3
D[3]=8>D[4]+A[4][3]=4+2=6 S 1 1 0 1 1
⇒ D[3]=6, T[3]=4;
T 0 1 4 2 1
Demonstrăm b). După selecţia nodului i, acesta este extras din N şi introdus în S.
De asemenea, se actualizează costurile drumurilor de la r la vârfurile rămase din
N. Fie j∈N un astfel de nod. Conform ipotezei de inducţie b) la pasul anterior
respecta condiţia. Faţă de pasul anterior, S conţine, în plus, vârful i. Singura
posibilitate ca D[j] să reţină o valoare mai mică, ar fi ca drumul de la r la el să
treacă prin i. Dar acest caz a fost tratat, atunci când s-au actualizat costurile prin
vârful i.
Iată şi programul:
Probleme propuse
1. Într-un grup de n persoane, anumite persoane, împrumută alte persoane cu
diverse sume de bani! Modelând problema cu ajutorul grafurilor orientate, se cere să
stabiliţi corespondenţa dintre afirmaţiile din stânga şi cele din dreapta. Observaţie:
dacă persoana i împrumută cu bani pe persoana j, atunci există un arc de la i la j.
2
Figura 8.14.
a) 3 2; b) 2 2; c) 2 3; d) 1 2. 4
3
Figura 8.15. 2
a) 1 1; b) 1 2; c) 2 2; d) 2 5.
Figura 8.16.
258 Capitolul 8. Grafuri orientate
9. Care este numărul minim de arce care trebuie adăugate pentru ca graful să
devină tare conex?
a) 1; b) 2; c) 3; d) 4.
10. Se dă un graf orientat. Se cere să se afişeze, pentru fiecare vârf în parte, gradul
interior şi gradul exterior. Problema se va rezolva în cazul în care graful dat prin
matricea de adiacenţă şi în cazul în care el este dat prin liste de adiacenţe.
11. Fiind date un graf orientat şi o succesiune de vârfuri să se decidă dacă
succesiunea este drum, iar în caz afirmativ se va preciza dacă este sau nu un drum
elementar. Problema se va rezolva în cazul în care graful dat prin matricea de
adiacenţă şi în cazul în care el este dat prin liste de adiacenţe.
12. La fel ca mai sus, dar se cere să se determine dacă succesiunea respectivă
este sau nu lanţ (lanţ elementar).
13. Se dă un graf prin lista muchiilor. Programul va decide dacă graful este
neorientat.
14. Se dau listele de adiacenţe a unui graf orientat. Programul va afişa matricea de
adiacenţă.
15. Se dă matricea de adiacenţă a unui graf orientat. Programul va afişa listele de
adiacenţe ale acestuia.
16. Se dă matricea de adiacenţă a unui graf orientat. Se cere să se listeze toate
circuitele de lungime 3.
18. Într-un grup de n persoane, fiecare persoană declară dacă cunoaşte sau nu
celelalte persoane din grup. Scrieţi un program care decide care este persoana cea
mai cunoscută din grup.
19. La fel ca la problema anterioară, se cere să se determine dacă există o "vedetă"
a grupului. Prin "vedetă" vom înţelege o persoană care este cunoscută de toate
celelalte persoane, dar nu cunoaşte nici o persoană.
20. Se dă un graf orientat. Se cere să se afişeze, dacă există, un circuit care conţine
toate arcele grafului (circuit eulerian).
21. Se dă un graf orientat. Se cere să se afişeze, dacă există, un drum elementar
care trece prin toate vârfurile grafului (drum hamiltonian).
22. Se dă un graf orientat şi două noduri ale sale, u şi v. Se cere să se afişeze,
dacă există, un drum de la u la v.
23. La fel ca mai sus, numai că se cere ca drumul să aibă lungimea minimă (să
conţină un număr minim de arce).
Manual de informatică pentru clasa a XI-a 259
27. Un graf orientat este bipartit dacă mulţimea vârfurilor sale poate fi împărţită în
două submulţimi disjuncte astfel încât orice arc este incident la un vârf din prima
mulţime şi la un vârf din a doua mulţime. Fiind dat un graf orientat, se cere să se scrie
un program care decide dacă graful dat este bipartit şi în caz afirmativ, afişează cele
două mulţimi de noduri.
28. Se dă un graf orientat. Se cere să se afişeze (prin mulţimile de muchii) toate
grafurile parţiale pe care le admite.
29. Se dă un graf orientat. Se cere să se afişeze (prin mulţimile de muchii) toate
subgrafurile pe care le admite.
30. Fie grafurile G1=(V1,A1) şi G2=(V2,A2). Să se scrie un program care să
verifice dacă G2 este subgraf pentru G1.
32. Fie graful orientat G=(V,A) dat prin matricea de adiacenţă. Fie A⊂V. Se cere
să se listeze mulţimea arcelor care au o extremitate într-un vârf din A şi altă
extremitate într-un vârf din V-A.
33. Fie graful orientat G=(V,A) dat prin liste de adiacenţe. Fie A⊂V. Se cere să se
listeze mulţimea arcelor care au o extremitate într-un nod din V-A şi altă extremitate
într-un nod din A.
34. Mai multe oraşe 1,2, ..., n sunt legate prin autostrăzi cu sens unic. Nu toate
oraşele sunt legate între ele prin legătură directă. Fiind dat oraşul în care se află un
turist cu maşina sa, k∈{1, 2, ..., n}, distanţele între oraşe (acolo unde ele sunt unite
prin autostrăzi) se cer următoarele (programe separate):
a) lista oraşelor în care turistul poate ajunge;
b) lista oraşelor în care turistul poate ajunge, dar se poate şi întoarce fără a trece prin
nici un oraş dintre oraşele întâlnite pe traseul de plecare;
c) drumul cu distanţa minimă între k şi celelalte oraşe;
e) care sunt oraşele care se pot vizita, pornind din oraşul k şi pentru care drumul
până la ele trece exact prin 3 oraşe (cu excepţia lui k şi a lor) şi care este lungimea
drumului de la k la ele?
g) care sunt drumurile minime între oricare perechi de oraşe, drum care nu poate
trece decât prin oraşele a, b, c?
35. Într-o fabrică există n secţii: o secţie de plecare, n-2 secţii intermediare şi o
secţie unde sosesc produsele finite. Fabricii i se poate asocia un graf orientat în
care nodurile sunt secţiile fabricii, iar muchiile sunt traseele pe care produsele
intermediare circulă prin secţii. O secţie nu poate furniza produsul intermediar în
care este specializată până când nu îi sosesc toate "ingredientele" de la secţiile de
care ea depinde. De asemenea, drumul între două secţii durează un anumit
interval de timp. Citindu-se N (numărul de secţii) şi o mulţime de tripleţi (i,j,k),
unde k este timpul necesar produselor pentru a ajunge din secţia i în secţia k, să
se tipărească timpul necesar pentru fabricarea produsului finit din momentul în
care secţia de plecare începe să funcţioneze.
Notă. Datele se introduc în mod corect (nu este necesară validarea lor).
Răspunsuri
1. 1-b, 2-a.
2. 1-d; 2-b; 3-a; 4-c;
4. b)
5. a)
6. c)
7. b)
8. c)
9. a)
35. Graful nu prezintă circuite. Dacă, prin absurd, o secţie ar trimite "ingredientele"
către o alta de la care a primit "ingrediente", ţinând cont de faptul că întreaga fabrică
începe să funcţioneze cu secţia 1, fabricaţia n-ar putea să aibă loc: secţia va aştepta
"ingredientele" care nu sosesc pentru că ea n-a trimis înapoi "ingredientele" necesare
la secţia de unde le aşteaptă. În aceste condiţii se poate aplica Roy-Floyd pentru a
afla drumul de cost maxim de la secţia 1 la secţia n.
261
Capitolul 9
Arbori
Demonstraţie
⇒ Fie G un arbore (graf neorientat, conex şi fără cicluri). Trebuie să demonstrăm
că are n-1 muchii. Vom demonstra prin inducţie. Dacă n=1, numărul muchiilor
este 0 (se verifică, are n-1 muchii). Vom presupune proprietatea adevărată
pentru arbori cu n noduri (adică au n-1 muchii). Fie un arbore cu n+1 noduri.
Există cel puţin un nod terminal (nod care are o singură muchie incidentă).
Dacă nu ar exista un astfel de nod, să considerăm un lanţ care porneşte
dintr-un nod oarecare. La fiecare pas, vom selecta o muchie. Până la urmă,
pentru că mulţimea nodurilor este finită şi pentru că nu există nod terminal,
lanţul va trece de două ori printr-un acelaşi nod. Asta înseamnă că arborele ar
conţine cicluri (absurd, se contrazice definiţia). Eliminăm nodul terminal şi
muchia care îi este incidentă. Obţinem un arbore cu n noduri. Conform ipotezei
făcute, acesta va avea n-1 muchii. Înseamnă că arborele cu n+1 noduri va
avea n muchii (n-1+1).
Demonstraţie
Să ne amintim un rezultat referitor la gradele nodurilor grafurilor neorientate cu
n noduri şi m muchii. Avem relaţia: d1+d2+...+dn=2m. Dar, pentru un arbore avem
egalitatea m=n-1.
1
Paragraful a fost introdus în acest capitol întrucât înţelegerea lui implică cunoaşterea
noţiunii de arbore.
Manual de informatică pentru clasa a XI-a 265
Rezolvare. Vom folosi, din nou, parcurgerea DF (în exemplu, pornind de la nodul
1). Astfel, muchiile care constituie arborele parţial, vor fi numite muchii de avans, iar
cele care sunt în plus se vor numi muchii de întoarcere. O muchie este de
întoarcere dacă în timpul parcurgerii, după ce este selectată, va "atinge" un nod deja
vizitat.
Altfel spus, o muchie de întoarcere, determină un ciclu. Aceasta înseamnă că
cele m-(n-1) muchii de întoarcere determină m-(n-1) cicluri.
Definiţia 9.2. Fiind dat un graf neorientat, vom numi set de cicluri
elementare, un număr maxim de cicluri în graf, astfel încât fiecare ciclu
conţine cel puţin o muchie care nu mai apare în nici un alt ciclu din set.
Să observăm că un graf poate conţine mai multe cicluri decât m-(n-1), dar
unele dintre ele nu conţin nici o muchie care nu apare în alte cicluri.
Pentru graful din exemplu, un set de cicluri elementare poate fi:
1) 1 2 3 4 1
2) 2 3 4 2
3) 1 2 3 4 5 1
În acelaşi timp, graful mai are şi ciclul 1 2 4 1. Dar muchia (1,2) este în
ciclul 1), muchia (2,4) este în ciclul 2), iar muchia (4,1) este în ciclul 1).
Problema 9.4. Fiind dat un graf conex, G=(V,E) să se determine un set de cicluri
elementare.
Rezolvare. Vom utiliza o parcurgere DF. Pentru a reţine selecţiile astfel efectuate,
vom utiliza un vector T, iar elementele acestuia au semnificaţia deja cunoscută:
j,
T[]
dacă i este descendent al lui j
i =
0, dacă i este rădăcina arborelui
begin
CitireN('Graf.txt',A,n);
df(1);
if not gasit
then
writeln('Graful nu contine
cicluri');
end.
Manual de informatică pentru clasa a XI-a 267
În exemplul de mai jos, puteţi observa acelaşi arbore, reprezentat aşa cum am
fost obişnuiţi până acum (figura a)) şi ca arbore cu rădăcina 1 (figura b)):
1
1 2
5 4 2
3 4
5
3
a) b)
Definiţia 9.3. Un arbore cu rădăcină este un set finit T, de unul sau mai
multe noduri, astfel încât:
a) Există un nod cu destinaţie specială, numit rădăcina arborelui.
b) Celelalte noduri sunt repartizate în m≥0 seturi disjuncte T1, T2, ..., Tm
şi fiecare din aceste seturi este un arbore. Arborii T1, T2, ..., Tm se
numesc subarbori ai rădăcinii.
Terminologie
Nu uitaţi, definiţia este recursivă. Prin urmare, fiecare nod al arborelui, pe lângă
informaţia asociată, va reţine adresele descendenţilor săi. Structura unui astfel de nod
este:
Atunci când numărul de posibili descendenţi ai unui nod nu este cunoscut, sau
este cunoscut dar este mare, pentru a evita risipa de memorie, vom utiliza listele de
adiacenţe. În plus, trebuie cunoscută rădăcina.
Pentru fiecare din cei doi arbori de mai jos, cu rădăcina 1, respectiv 3,
puteţi observa reprezentarea lor cu ajutorul unor referinţe ascendente:
3
1
5
2 4 5
1
sau
3 2 4
0 1 5 1 1 5 1 0 1 3
1 2 3 4 5 1 2 3 4 5
nivelul 3
2 4
Figura 9.6. Exemplu
4
1
0 1 1 0 4
Figura 9.7. 2 3
1 2 3 4 5 5
Exemplu
Sau, altfel spus, mulţimile nodurilor celor k arbori alcătuiesc o partiţie a mulţimii
{1,2,...,n}.
Rezultă de aici că printr-un vector de tip Tata putem reţine o partiţie a mulţimii
{1,2,...,n}. Desigur, ne putem întreba: la ce foloseşte asta? Şi, de ce ar fi nevoie
ca o submulţime a partiţiei să fie reţinută sub formă de arbore, pentru că, se ştie, între
elementele ei nu există nici o relaţie?
Există două motive esenţiale pentru a reţine o partiţie ca o pădure:
1) Cum testăm cărei mulţimi îi aparţine un element? Foarte simplu, vedem care este
tatăl său, apoi tatăl tatălui, ş.a.m.d., până ajungem la un element din vectorul T care
reţine 0. Dacă, în exemplul de mai sus vrem să vedem cărei mulţimi îi aparţine
elementul 3, atunci avem T[3]=1 şi T[1]=0. Prin urmare, elementul 3 aparţine
mulţimii nodurilor arborelui care are ca vârf nodul 1. Procedând astfel, avem avantajul
că această operaţie se efectuează în O(log(n)), adică foarte rapid. Într-o
reprezentare clasică, am identifica mulţimea în O(n)...
2) Reuniunea a două mulţimi din partiţie se face foarte uşor. Pur şi simplu, se
subordonează vârful unui arbore i, vârfului unui alt arbore j. Operaţia este T[i]=j.
Pentru a beneficia din plin de primul avantaj, va trebui ca atunci când reunim
două mulţimi din partiţie, să subordonăm arborele cu înălţimea mai mică,
arborelui cu înălţimea mai mare. Pentru a înţelege acest fapt, vom da un exemplu
în care iniţial nu ţinem cont de această observaţie, apoi arătăm cum trebuie procedat.
Să presupunem că iniţial avem 4 arbori, unde fiecare arbore are un singur
nod. În termeni din teoria mulţimilor, iniţial avem o partiţie din 4 mulţimi şi
fiecare mulţime are un singur element.
Manual de informatică pentru clasa a XI-a 273
A) Fără a ţine seama de observaţie, reunim mulţimea {1} cu mulţimea {2}, apoi
mulţimea obţinută cu {3}, apoi mulţimea obţinută cu {4}. Arborele obţinut are
înălţimea 3, deci regăsirea informaţiei se face în O(n).
2 3 4 2 4 2
1 1 1
3 3
4
Figura 9.9.
2 3 4 2 4 2
1 1 3 4 1 3
Figura 9.10.
Pentru fiecare vârf al grafului, vectorul H reţine înălţimea sa. Iniţial, toate
componentele lui H reţin 0. Când se schimbă numărul de niveluri ale unui vârf? Atunci
când un arbore se subordonează altuia şi numai atunci când cei doi arbori au aceeaşi
înălţime. În acest caz, înălţimea arborelui care subordonează alt arbore creşte cu 1.
Dacă înălţimile celor doi arbori nu sunt egale, atunci se subordonează arborele cu
înălţime mai mică, arborelui cu înălţime mai mare. Acesta din urmă rămâne cu
aceeaşi înălţime. Exemplu: se reunesc cei doi arbori din stânga şi se obţine arborele
din dreapta:
2 5 2
5
1 3 1 3
6
6
4 4
Complexitatea algoritmului
1 1
3 2
5 5 2 5 5 2
2 2
4 1 7 4 7
4 4 3 4 3
a) b)
1 1
3 3 2
5 2 5 2
2
1 1
4 4 3 4 4 3
c) d)
Iniţial, fiecare nod va constitui un arbore. Prin urmare, vom avea o pădure
alcătuită din n arbori. Apoi, se execută de n-1 ori pasul următor:
Se caută muchia de cost minim care uneşte noduri care aparţin la doi
arbori diferiţi. Se selectează muchia respectivă.
După selectarea a n-1 muchii, se obţine un arbore parţial de cost minim. Vom
da un exemplu, pentru a arăta cum funcţionează algoritmul. Fie graful următor:
Manual de informatică pentru clasa a XI-a 277
1 1 1
3 2
5 5 2 5 2 5 2
2
4 1 7 1
4 4 3 4 3 4 3
1 1 1
2 3 2 3 2
5 2 5 2 5 2
1 1 1
4 3 4 3 4 4 3
muchiei mk+1. Evident, mk+1 va induce un ciclu. Ciclul nu este alcătuit exclusiv din
muchiile m1, m2, ..., mk, mk+1 pentru că ele fac parte din arborele generat de algoritm.
Prin urmare, ciclul va mai conţine o muchie, alta decât cele ale arborelui iniţial, muchie
pe care o eliminăm. Cum mk+1 are cea mai mică lungime, arborele rămâne optim,
dar are o muchie, în plus, comună cu arborele generat de algoritm. Procedând la
fel, vom transforma arborele parţial de cost minim în arborele generat de algoritm.
Prin urmare, arborele determinat de algoritm este optim.
1 1
3 2 3
5 2 5 2
2
1 1
4 4 3 4 4 3
a) b)
Arborele generat de algoritm (M) Arborele optim (O)
M={(2,4),(1,2),(1,5),(3,4)} şi O={(2,4),(1,4),(1,5),(3,4)}.
4 4 3
Figura 9.15.
Implementarea algoritmului
care muchia este incidentă, iar c este costul ei. Atenţie! Din motive strict didactice,
pentru ca sursa programului să nu fie prea mare, am renunţat la sortarea muchiilor
după cost. Dar programul dvs. va trebui să conţină subprogramul respectiv.
1 1 1
3 2 2
5 5 2 2
2
4 1 7
4 4 3
1 1 1
2 3 2 3 2
2 5 2 5 2
1 1 1
4 4 3 4 4 3
1 1
3 2 3
5 2 5 2
2
1 1
4 4 3 4 4 3
begin
Citesc;
S[1]:=1;
for k:=1 to n-1 do
begin
min:=PInfinit;
for i:= 1 to n do
for j:=1 to n do
if (S[i]=1) and (S[j]=0)
and (min>C[i,j])
then
begin
min:=C[i][j];
lin:=i;
col:=j;
end;
writeln(lin,' ',col,
' ',min:2:0);
S[col]:=1;
end
end.
Figura 9.19.
7
Exemplu de arbore binar
Figura 9.20. 2 2
Exemple de descendenţi
Proprietăţi
2) Un arbore binar de înălţime h are cel mult 2h noduri pe ultimul nivel. Ultimul
nivel este h.
(să observăm că avem o progresie geometrică cu n+1 termeni, primul termen este
1 şi raţia este 2).
2h+1-1=n ⇔ 2h+1=n+1
Dacă considerăm arborele ca un graf orientat, unde există un arc de la tată la fiu,
atunci fiecare nod are gradul exterior 0,1,2. Cum suma gradelor exterioare este
egală cu numărul de muchii m, şi cum m este egal cu n-1, atunci avem şi relaţia:
ii) b+2c=n-1.
1 1 2 3 4 5 6
St 2 4 0 0 0 0
2 3
Dr 3 5 6 0 0 0
4 5 6
Figura 9.22.
2. Reprezentarea în HEAP
a+b ab+
a*(b+c) abc+*
a*(b+c)-e/(a+d)+h abc+*ead+/-h+
+ c d e
a b
Figura 9.24.
a) folosim o stivă ST, care permite memorarea variabilelor x0, x1, ..., xn, a, b, ..., z.
b) forma poloneză se găseşte în FP;
c) din FP, se citeşte caracter cu caracter iar rezultatul citirii se tratează
diferenţiat astfel:
- în cazul în care caracterul citit este operand, se încarcă în stivă;
- în cazul în care caracterul citit este operator, se scot din stivă
conţinuturile ultimelor două niveluri şi se face tipărirea sub forma:
xj = variabila penultimă în stivă, operator, variabila ultimă în stivă,
iar variabila tipărită se reţine în stivă.
Procedeul se repetă atât timp cât nu au fost citite toate caracterele.
Pentru exemplul considerat, rularea decurge astfel:
b a, b, c se încarcă în stivă;
x0
la citirea operatorului “+”, se tipăreşte x0=b+c şi x0 se pune în stivă;
a
e e, a, d se încarcă în stivă;
x1
296 Capitolul 9. Arbori
x2
e se tipăreşte x2 = a+d;
x1
x3
se citeşte ”/”, se tipăreşte x3 = e/x2;
x1
h
caracterul ”h” se pune în stivă;
x4
'+','-','*','/': {
begin cout<<"x"<<j<<"="
writeln('x',j,'=', <<st[k-1][0]
st[k-1,1], <<st[k-1][1]<<fp[i]
st[k-1,2],fp[i], <<st[k][0]<<st[k][1]
st[k,1],st[k,2]); <<endl;
k:=k-1; k--;
t[k,1]:='x';str(j:1,c); st[k][0]='x';
st[k,2]:=c[1]; itoa(j,c,10);
j:=j+1 st[k][1]=c[0];
end j++;
end {case} }
end. }
2 3
Figura 9.25.
Exemplu de arbore binar plin
4 5 6 7
Proprietăţi
1. Un arbore binar plin cu înălţimea h are 2h+1-1 (vezi proprietăţile arborilor binari).
1 1
2 3 2 3
4 5 6 4 5
2 h ≤ n < 2 h +1 ⇔ log 2 2 h ≤ log 2 (n) < log 2 2 h +1 ⇔ h ≤ log 2 (n) < h + 1 ⇔ h = [log 2 (n)]
1 2 3 4 5 6
4 6
V 1 4 6 3 1 5
3 1 5
Observaţii
n v[i] ≤ v[2 ⋅ i]
∀i ∈ {1, 2,... } ⇒
2 v[i] ≤ v[2 ⋅ i + 1] , pentru 2 ⋅ i + 1 ≤ n
n v[i] ≥ v[2 ⋅ i]
∀i ∈ {1, 2,... } ⇒
2 v[i] ≥ v[2 ⋅ i + 1] , pentru 2 ⋅ i + 1 ≤ n
2
1 2 3 4 5 6
V 2 3 7 6 4 9 3 7
6 4 9
5
1 2 3 4 5 6
V 5 3 7 6 4 9 3 7
6 4 9
3
1 2 3 4 5 6
V 3 5 7 6 4 9 5 7
6 4 9
3
1 2 3 4 5 6
V 3 4 7 6 5 9 4 7
6 5 9
Algoritmul se încheie fie când noua valoare este mai mică decât valorile
reţinute de vârfurile celor două minHeap-uri, fie când se ajunge la un nod fără
descendenţi (are indicele mai mare decât [n / 2] ).
[
Estimarea timpului de calcul. Pentru că graful are înălţimea log 2 ( n) , şi cum ]
interschimbările se fac în adâncime, atunci algoritmul are complexitatea
[ ]
O( log 2 (n) ). Raţionamentul este de tipul DIVIDE ET IMPERA.
Figura 9.32. 8
Exemplu de arbore de căutare
1 4
valoarea este mai mică decât cheia asociată vârfului - se reia problema
pentru subarborele stâng;
valoarea este mai mare decât cheia asociată vârfului - se reia problema
pentru subarborele drept.
Altfel
nu există în arbore o cheie egală cu valoarea dată.
dacă numai subarborele stâng este nevid, nodul este şters, iar
părintele lui va reţine, în locul adresei lui, adresa subarborelui stâng.
dacă ambii subarbori sunt nevizi:
se identifică cel mai din dreapta nod al subarborelui stâng;
cheia nodului astfel identificat va fi memorată de nodul analizat;
nodul astfel identificat se şterge, iar ştergerea se efectuează ca în
cazul în care nodul subordonează numai subarborele stâng.
valoarea este mai mică decât cheia asociată vârfului - se reia problema
pentru subarborele stâng;
valoarea este mai mare decât cheia asociată vârfului - se reia problema
pentru subarborele drept.
Altfel
nu există în arbore o cheie egală cu valoarea dată.
Exemple de ştergeri
7 7
1. În arborele din stânga
se şterge nodul 8. Acesta
este nod terminal. 3 9 3 9
1 4 8 1 4
Figura 9.33.
Figura 9.34. 1 4 8 1 4
Figura 9.36.
Se poate alege şi cheia celui mai din stânga nod din subarborele drept! De
ce? Justificaţi răspunsul!
else else
if c^.as=nil then if (c->ad==0)
begin { f=c->as;
f:=c^.ad; delete c;
dispose(c); c:=f; c=f;
end }
else else cmmd(c,c->as);
if c^.ad=nil then else
begin if (c->nr<k)
f:=c^.as; Sterg(c->ad,k);
dispose(c); c:=f else Sterg(c->as,k);
end
else
else cmmd(c,c^.as)
else cout<<"numar absent
if c^.nr<k - tentativa esuata ";
then sterg(c^.ad,k) }
else sterg(c^.as,k)
else
writeln('numar absent
-tentativa esuata ')
end;
Probleme propuse
1. Stabiliţi valoarea de adevăr a propoziţiilor următoare:
a) Dacă un graf neorientat cu n noduri are n-1 muchii, atunci el este arbore.
b) Orice arbore este un graf conex.
c) Orice graf neorientat conex este arbore.
d) Orice graf neorientat care nu conţine cicluri este o pădure.
e) Orice pădure este un graf conex fără cicluri.
f) O partiţie a mulţimii {1,2,...,n} poate fi reprezentată ca o pădure.
g) O pădure poate fi dată sub forma unei partiţii.
h) Orice arbore este graf bipartit.
i) Orice graf bipartit este arbore.
2. Căte cicluri elementare are un graf complet cu n noduri?
3. Un arbore cu 6 noduri este reprezentat cu ajutorul matricei de adiacenţă. Câte
cifre de 0 conţine aceasta?
4. Fie un arbore cu 5 noduri. Care dintre şirurile de mai jos poate reprezenta şirul
gradelor nodurilor acestui arbore?
a) 1 1 1 1 0; b) 1 2 0 0 2; c) 2 4 0 1 1; d) 2 3 1 1 1.
Exemplu: arborele de mai jos este reprezentat sub forma unei liste:
2 4
3 2 1 4 5
3
5 Figura 9.37. Exemplu
17. Scrieţi o funcţie care eliberează memoria din HEAP ocupată de un arbore binar.
18. Scrieţi o funcţie care afişează nodurile unui arbore binar memorat în HEAP în
ordinea obţinută în urma parcurgerii acestuia în lăţime.
19. Scrieţi o funcţie care listează nodurile de pe nivelul k ale unui arbore binar
memorat în HEAP.
20. Scrieţi o funcţie care listează nodurile de pe nivelul k ale unui arbore binar
memorat cu ajutorul a doi vectori.
21. Scrieţi o funcţie care listează nodurile terminale ale unui arbore binar memorat în
HEAP.
22. Scrieţi o funcţie care listează nodurile terminale ale unui arbore binar memorat cu
ajutorul vectorilor.
23. Numărarea arborilor binari. Autoinstruire.
23.1. Se citeşte n, număr natural. În câte feluri se pot aşeza în linie n litere P şi n
litere S?
Exemplu. Pentru n=2, avem: PPSS, SSPP, SPSP, SPPS, PSSP, PSPS. Se va afişa 6.
2
P 2 se pune în stivă: rămâne 3.
1
P 3 se pune în stivă. 1 2
⇒ Fie o secvenţă inadmisibilă. Fie k, minim, astfel încât secvenţa 1...k să fie
inadmisibilă.
Exemplu: PSPSSP. Aici, k=5.
(())() ()(())
((()))
()()()
(()())
24. Un arbore oarecare, de vârf dat, poate fi reţinut ca un arbore binar astfel: pentru
fiecare nod i, vârful subarborelui stâng este dat de primul descendent al său (în
ordinea de la stânga la dreapta), iar vârful subarborelui drept, de primul nod aflat pe
acelaşi nivel, în aceeaşi ordine.
Exemplu. Arborele oarecare din figura 9.39, a) se reprezintă ca arbore binar, asa
cum rezultă din figura b) şi c).
1 1
2
2 3 7
3
4 5 6
4 7
a)
5
1
6
2 3 7
c)
4 5 6
b)
Figura 9.39. Exemplu
Răspunsuri
1. a) F; b) A; c) F; d) A; e) A; f) A; g) F; h) A; i) A.
n(n − 1) (n − 1)(n − 2)
2. − (n − 1) = .
2 2
Anexa 1
Aplicaţii practice ale grafurilor
Internet
Router
Switch 1 Switch 2
Subreţeaua 1 Subreţeaua 2
Observaţii
L1 L2
Având cunoscut drumul critic pentru un graf asociat unui proiect, se pot
analiza în detaliu anumite aspecte particulare ale fiecărui eveniment sau activitate.
Dorim să cunoaştem cum se pot derula celelalte activităţi, care nu sunt critice, în
funcţie de durata drumului critic. Astfel, au fost introduse câteva noţiuni teoretice,
ce vor fi prezentate în continuare.
Să revenim la exemplul din figura A.3. Pentru evenimentul 4, vom avea data
aşteptată egală cu 10 (5+2+3) unităţi, iar data limită, egală cu 16 (21-5) unităţi.
Putem astfel considera că evenimentul 4 trebuie să fie atins după 10 unităţi
temporale, iar în cazul unei întârzieri, atingerea sa nu poate să dureze cu mai mult
de 6 (16-10) unităţi faţă de data sa aşteptată de terminare.
Arcele ce formează drumul critic au aceste două valori nule (nu le este
permisă nici o întârziere).
Intervalul de fluctuaţie permite managerului de proiect să utilizeze resursele,
echipamentele şi utilajele rămase libere pentru a ajuta alte activităţi şi implicit
pentru a micşora durata de efectuare a întregului proiect (în cazul în care se poate
realiza acest lucru).
Grafurile de activităţi sunt extrem de utile în evaluarea lucrărilor complexe, iar
reprezentarea lor permite analistului de proiect o viziune de ansamblu şi totodată, o
modalitate prin care poate testa o multitudine de variante, înainte de a o alege pe cea
considerată optimă. De asemenea, soft-urile specializate ce oferă metode complexe
de analiză, utilizează cu succes metode de optimizare ca cea a “Drumului Critic“.