Vous êtes sur la page 1sur 102

ALGORITMICA GRAFURILOR

Q NOTE DE CURS

MIHAI TALMACIU
CUPRINS

Capitolul 1 Introducere
1.1. Definiţia unui graf 5
1.2. Grade 6
1.3. Subgrafuri 6
1.4. Operaţii cu grafuri 7
1.5. Clase de grafuri 7
1.6. Drumuri şi circuite 8
1.7. Mulţimi separatoare, transversale şi mulţimi omogene 9
1.8. Algoritmi şi complexitate de calcul 9

Capitolul 2 Metode de căutare


2.1. Căutarea în lăţime 11
2.1.1. Algoritmul 11
2.1.2. Implementarea C++ 11
2.1.3. Complexitate şi optimalitate 12
2.1.4 Aplicaţii ale BFS 12
2.2. Căutarea în adâncime 13
2.3. Metoda Greedy 15
2.4. Metoda backtracking 15
2.5. Metoda divide et impera 17
2.6. Metoda branch and bound 17

Capitolul 3 Structuri de date


3.1. Liste 20
3.1.1. Liste simplu înlănţuite 20
3.1.2. Parcurgerea unei liste simplu înlănţuite 21
3.1.3. Liste dublu înlănţuite 21
3.1.4. Parcurgerea unei liste dublu înlănţuite 21
3.2. Arbori 22
3.2.1. Arbori liberi 22
3.2.2. Arbori cu rădăcină 22
3.2.3. Arbori binari 23
3.2.4. Parcurgerea arborilor binari 23

Capitolul 4 Parcurgeri de grafuri


4.1. Parcurgerea BF a grafurilor 26
4.2. Parcurgerea DF a grafurilor 31
4.3. Aplicaţii 33
4.3.1. Sortarea topologică 33

2
4.3.2. Componentele conexe ale unui graf 34

Capitolul 5 Probleme de drum în (di)grafuri


5.1. Problema celui mai scurt drum 37
5.1.1. Arborele Steiner 38
5.1.2. Algoritmul lui Dijkstra 40
5.1.3. Probleme similare şi algoritmi 42
5.1.4. Probleme legate de drum 42
5.1.5. Algoritmul Bellman-Ford 42
5.1.6. Algoritmul de căutare A* 45
5.1.7. Algoritmul Floyd-Warshall 47
5.1.8. Algoritmul lui Johnson 50
5.2. Probleme de conexiune. Teorema lui Menger şi aplicaţii 50
5.3. Structura grafurilor p-conexe 51
5.4. Problema drumului Hamiltonian 52
5.5. Problema ciclului Hamiltonian 53
5.6. Arborele parţial de cost minim 56
5.7. Algoritmul lui Prim 58
5.8. Algoritmul lui Kruskal 63

Capitolul 6 Probleme de fluxuri în reţele


6.1. Problema fluxului maxim 68
6.2. Fluxuri de cost minim 72
6.3. Algoritmul Ford-Fulkerson 75

Capitolul 7 Numărul de stabilitate şi densitatea unui graf


7.1. Mulţimi stabile şi clici 78
7.2. Problema mulţimii independente 79
7.3. Problema clicii 80
7.4. Determinarea mulţimilor stabile maximale 81

Capitolul 8 Probleme de descompuneri în grafuri


8.1. Tipuri de descompuneri în grafuri 85
8.1.1. Descompunerea de tip 2 85
8.1.2. Descompunerea de tip 3 86
8.1.3. Descompunerea în funcţie de compoziţie 87
8.1.4. G-descompunerea 87
8.1.5. Descompunerea substituţie şi partiţionarea vârfurilor 88
8.2. Descompunerea slabă a grafurilor 91
8.2.1. Introducere 91
8.2.2. Descompunerea slabă a unui graf 91
8.3. Teorema celor patru culori 94
8.3.1. Colorarea grafurilor 95

3
8.3.2. Colorarea vârfurilor 96
8.3.3. Numărul cromatic 96
8.3.4. Aspecte algoritmice 96
8.3.5. Algoritmul Welsh – Powell 97
8.3.6. Polinomul cromatic 97

Bibliografie 99

4
Capitolul 1
INTRODUCERE

Cuvinte cheie
Graf, subgraf, drum, algoritm.
Obiectivele invatarii
Dupa parcurgerea acestui capitol va trebui:
Sa stiti definiţia unui graf, notiunea de grad, notiunea de subgraf, operatii cu grafuri. Sa
cunoasteti operatii cu grafuri, clase de grafuri, notiunea de drum, de circuit, de multimi
separatoare, transversale si multimi omogene. Sa stiti sa definiti notiunea de algoritm si
complexitate de calcul.

Pentru noţiunile din acest paragraf am consultat Behzad, Chartrand, Foster, Croitoru,
Olaru, Tomescu. Alte completări bibliografice sunt precizate în momentul utilizării.

1.1. Definiţia unui graf


Un graf este o pereche G = (V,E), unde V este o mulţime finită nevidă, iar E este o
mulţime de submulţimi cu două elemente distincte ale lui V. V se numeşte mulţimea vârfurilor şi
numărul său de elemente, |V| este ordinul grafului G. E este mulţimea muchiilor grafului G şi |E|
este dimensiunea grafului G. Când facem referire la mulţimea de vârfuri şi muchii ale grafului G
folosim V(G) şi E(G), respectiv.
Un digraf (graf orientat) este o pereche D = (V(D), A(D)) unde V(D) este o mulţime
finită nevidă (mulţimea vârfurilor digrafului D), iar A(D) ʹ V(D) V(D) este mulţimea arcelor
digrafului D.
Dacă e = {x,y} este o muchie a grafului G, vom nota, pe scurt, {x,y} = xy (yx) şi vom
spune că: muchia e este incidentă cu vârfuri1e x şi y; vârfurile x şi y sunt adiacente în G;
vârfurile x şi y sunt vecine în G; vârfurile x şi y sunt extremităţile muchiei e.
Dacă v �V(G) , atunci mulţimea
NG (v) = {w | w �V(G) - {v}, vw �E(G)} ,
se numeşte vecinătatea vârfului v în G.
NG (v) = {w | w �V(G) - {v}, vw �E(G)}
se numeşte mulţimea nevecinilor vârfului v în G.
Dacă A, B �V(G), A �B = 0/ atunci :
 A ~ B (A este total adiacent cu B) dacă şi numai dacă:
"a �A, "b �B : ab �E(G) ;
/
~
 A B (A este total neadiacent cu B) dacă şi numai dacă:
"a �A, "b �B : ab �E(G) ;
 A ~ p B (A este parţial adiacent cu B) dacă şi numai dacă:
$a �A, $b �B : ab �E(G) ;
a ~ B (vârful a este B – universal, adică a vede toate vârfurile din B) dacă şi numai dacă
{a} ~ B .
a ~/ B (vârful a este B – nul) dacă şi numai dacă {a} ~/ B .
Dacă S �V(G) atunci:
S este mulţime stabilă (sau independentă) a lui G dacă şi numai dacă
5
"ι�
x, y V(G), x y : xy E(G) .
O stabilă S este maximală dacă nici o stabilă a lui G nu conţine propriu S;
S(G) = {S | S este stabilă maximală în G} ;
a(G) = max{S | S �S(G)} este numărul de stabilitate a lui G.
Sa (G) = {S | S �S(G),| S | = a(G)} ;
S este o mulţime a - stabilă dacă şi numai dacă S �Sa (G) .
Complementarul, G , al unui graf G este graful a cărui mulţime de vârfuri este V(G), iar
două vârfuri sunt adiacente în G dacă şi numai dacă ele nu sunt adiacente în G.
Dacă Q �V(G) atunci:
Q este clică (său mulţime completă) a lui G dacă şi numai dacă Q este mulţime stabilă în
G ; o clică Q este maximală dacă nici o clică a lui G nu conţine propriu Q;
C(G) = {Q | Q este clică maximală în G}; ,
w (G) = max{|Q| | Q �C(G) } este numărul clică a lui G;
Cw (G) ={Q | Q �C(G) şi |Q| = w (G)}; Q este o w - clică dacă şi numai dacă
Q �Cw (G) .
Dacă p �N* , se numeşte p – colorare a vârfurilor lui G o aplicaţie
c : V(G) � {1,..., p}
cu proprietatea că c -1 (i) este o mulţime stabilă în G.
c(G) este numărul cromatic a lui G, adică cel mai mic număr de culori necesare pentru a
colora vârfurile de felul că două vârfuri adiacente distincte nu pot fi de aceeaşi culoare.
q(G) = c(G) este numărul de acoperire cu clici a lui G.
Două grafuri, G = (V(G),E(G)) şi H = (V(H),E(H)) se numesc izomorfe, şi notăm aceasta
prin G �H (sau G @ H ) dacă există o bijecţie
j : V(G) � V(H)
cu proprietatea că aplicaţia
y : E(G) � E(H) ,
definită pentru orice uv �E(G) prin
y (uv) = j(u)j(v)
este o bijecţie.

1. 2. Grade
Gradul unui vârf v dintr-un graf G este numărul de muchii ale lui G incidente cu v, care se
notează cu d G (v) . Un vârf de grad zero se numeşte izolat. Un vârf de grad unu se numeşte
pendant .

1.3. Subgrafuri
Fie G un graf. Un graf H este un subgraf al lui G dacă V(H) �V(G) şi E(H) �E(G). Dacă
A este o submulţime nevidă a lui V(G) atunci subgraful lui G indus de A este graful având
mulţimea vârfurilor A, iar mulţimea muchiilor constă din acele muchii ale lui G incidente cu două
elemente din A. Dacă A �V(G), v �V, U �E(G),e �E(G) atunci notăm: G A sau G(A) sau [A]G
sau [A] subgraful lui G indus de A; G – A = G(V(G) – A); G – v = G – {v} ; G – U =
(V(G),E(G) – U); G – e = G – {e}.

6
1.4. Operaţii cu grafuri
Pe parcursul acestei cărţi vom folosi unele operaţii cu grafuri, pe care le reamintim mai
jos.
1) Dacă G1 şi G 2 sunt două grafuri cu V( G1 ) �V( G 2 ) = 0/ atunci reuniunea disjunctă a
grafurilor G1 şi G 2 înseamnă graful G = G1 � G 2 cu V(G) = V( G1 ) �V( G 2 ) şi E(G) = E( G1 )
�E( G 2 ).
2) Se numeşte graf adiacenţă (X - adiacenţă) (Sabidussi, Olaru, Antohe) a unei familii de
grafuri (G x ) x�V(X) , indexată de mulţimea de vârfuri a grafului X, graful notat UX
x �X G x , unde:

(
V UX )
x�X G x = Ux�X V(X) �{x};

E ( UX
x�X G x ) = Ux�V(X) E(G x ) �
ȹ���
{[(a, x), (b, x ')] | x x ',[x, x '] E(X), a V(G x ), b V(G x ' )}.
3) Produsul lexicografic (compoziţia) (Harary) a două grafuri G1 şi G 2 este graful notat G
= G1 [ G 2 ], unde V(G) = V( G1 ) �V( G 2 ) şi două vârfuri (u1 , u 2 ) şi (v1 , v2 ) ale lui G sunt
adiacente dacă şi numai dacă u1v1 �E(G1 ) sau ( u1 = v1 şi u 2 v 2 �E(G 2 ) ).
4) Suma a două grafuri. Dacă luam X = K 2 , K 2 - adiacenţa grafurilor oarecare G1 şi G 2 se
mai numeşte suma celor două grafuri şi se notează cu G1 + G 2 .
1.5. Clase de grafuri
Se numeşte graf complet de ordin n, graful notat, K n , unde | V(K n ) | = n şi
E(K n ) = P2 (V(K n )) , iar P2 (X) este mulţimea părţilor cu două elemente ale lui X.
Se numeşte graf nul de ordin n, graful N n = K n .
Se numeşte ciclu de lungime n ( n �3 ) graful notat, Cn , unde V(Cn ) = {1, 2,..., n} şi
E(Cn ) = {12, 23,..., n - 1n, n1} . Deoarece vârfurile sunt distincte, ciclul este elementar. Peste tot în
carte vom folosi ciclu elementar. De asemenea îl vom numi şi circuit
Se numeşte lanţ de ordin n, graful Pn = Cn - e (e �E(Cn )) . Îl vom numi şi drum. Dacă a
= i, b = i + l, pentru orice i = l, ..., n – l şi e = ab atunci spunem că avem un a – b drum (sau ab –
drum).
Un circuit (drum) al unui graf G este un subgraf indus al lui G care este el însuşi circuit
(drum) în G. (Observăm că un circuit (drum) al unui graf nu are corzi. O coardă într-un circuit
(drum) al unui graf este o muchie cu extremităţile vârfuri neconsecutive pe circuit (drum)).
Un graf G = (V,E) se numeşte n – partit, n �1 , dacă V se partiţionează în n submulţimi
V1 , V2 ,..., Vn nevide astfel încât [Vi ]G = (Vi , 0) / , "i = 1, n (adică, orice muchie din E este
incidentă cu un vârf din Vi şi un vârf din Vj , unde i �j ). Pentru n = 2, un astfel de graf se
numeşte bipartit. Un graf n – partit G se numeşte n – partit complet dacă mulţimile partiţiei
V1 , V2 ,..., Vn au proprietatea că pentru orice u din Vi şi orice v din Vj , i �j , uv este muchie a
lui G. Un graf bipartit complet cu mulţimile partiţiei V1 şi V2 , unde | V1 | = m şi | V2 | = n este
notat K(m,n) ( K m,n ). Graful K(l,n) (sau K1,n ) se numeşte graf stea (star).
Un graf se numeşte perfect (Berge) dacă numărul cromatic şi numărul clică sunt egale
pentru orice subgraf indus al său.

7
Un graf G este minimal imperfect dacă şi numai dacă G nu este perfect şi orice subgraf G
– v (v din V(G)) este perfect.
O muchie e a lui G se numeşte a – critică (Olaru) dacă a (G – e) = a (G) + 1.
Notăm mulţimea muchiilor a – critice ale lui G prin E c şi numim G c = (V, E c ) scheletul a –
critic a lui G. Dacă E c = E, graful se numeşte a - critic.
Un graf G se numeşte ( a , w ) – partiţionabil (a se vedea Golumbic, Olaru) dacă pentru
fiecare v �V(G), G – v admite o partiţie de a w – clici şi o partiţie de w mulţimi a – stabile.
Un graf G = (V,E) se numeşte a – partiţionabil ( a – decompozabil) (Olaru) dacă există o
partiţie Z = {V1 ,..., Vm } a lui V, cu m > 1 astfel încât �im=1 a(G i ) = a(G) , unde
G i = G(Vi ), i = 1, m .
Z se numeşte a – partiţie a lui G, iar G i sunt a – componente.
Un graf G se numeşte w - partiţionabil dacă G este a - partiţionabil.
Dacă graful G are o a – partiţie astfel încât a – componentele sunt clici atunci G este
perfect a – partiţionabil ( a – decompozabil).
G este partiţionabil dacă este a – partiţionabil şi w – partiţionabil.
Un graf G se numeşte tare stabil (Olaru, Antohe) dacă
a(G - Q) = a(G) ,
pentru orice clică proprie Q a lui G.
Un graf G se numeşte tare a – stabil (Olaru) dacă şi numai dacă "v �V(G) şi orice
clică Q a lui G, q(G - v) = a(G) = a(G - Q) .
Cografurile sau grafurile P4 – libere (sau P4 – free), descoperite de mai mulţi autori,
printre care Lerchs sunt grafuri care nu conţin P4 indus.
Un graf G se numeşte gem – free dacă pentru fiecare vârf v, [NG(v)] este graf P4 – free.
Un graf se numeşte paw dacă este izomorf cu
({a,b,c,d},{ab,bc,cd,db}).
Un graf izomorf cu P5 se numeşte house.
Un graf se numeşte bull dacă este izomorf cu
({a,b,c,d,e},{ab,bc,ca,bd,ce}).
Chvatal a introdus clasa grafurilor ordonabile perfect, care sunt caracterizate prin
existenţa unei ordini lineare < pe mulţimea vârfurilor astfel încât nici un P4: abcd nu are a < b şi d
< c.
Fie F o familie de mulţimi nevide. Graful intersecţie al lui F este obţinut reprezentând
fiecare mulţime din F printr-un vârf şi două astfel de vârfuri determină o muchie dacă şi numai
dacă mulţimile lor corespunzătoare se intersectează.

1.6. Drumuri şi circuite


Fie G un graf si a, b două vârfuri ale sale. Distanţa dintre a şi b, notată d(a,b) înseamnă
minimum lungimilor a – b drumurilor din G. Pentru A �V(G) şi x �V(G) – A, distanţa de la x la
A înseamnă
d(x, A) = min a �A d(x, a) .
Un graf este conex dacă există un drum între orice două vârfuri ale sale. Un graf care nu
este conex se numeşte neconex. Orice graf poate fi exprimat unic ca o reuniune disjunctă de
subgrafuri induse conexe şi maximale cu această proprietate. Aceste subgrafuri se numesc
componente conexe ale grafului.
Un arbore este un graf conex şi fără circuite.
8
1.7. Mulţimi separatoare, transversale şi mulţimi omogene
Fie G = (V,E) un graf conex. O submulţime S �V se numeşte vârf separator (a se vedea
Golumbic) pentru vârfurile a şi b (sau a – b - separator) dacă în G – S, a şi b vor aparţine la
componente conexe diferite. Dacă nici o submulţime proprie a lui S nu este a – b separator atunci
S este vârf separator minimal pentru a şi b.
Fie G = (V,E) un graf conex. O submulţime A �V se numeşte mulţime separatoare
(cutset) dacă G – A este neconex. O mulţime separatoare A se numeşte minimală dacă nici o
submulţime proprie a sa nu este mulţime separatoare.
Fie G = (V,E) un graf. O mulţime nevidă T de vârfuri se numeşte star – cutset (Chvatal)
dacă G – T este neconex şi există un vârf v din T care este adiacent la toate vârfurile rămase din
T. Vârful v se numeşte centrul lui T.
Fie M o mulţime şi F = {Mi }i�I o familie de submulţimi a lui M şi T �M.
Mulţimea T este o transversală a lui F dacă
T ǹ/"M�i 0, i I .
Transversala T este perfectă dacă
| T �M i | = 1, "i �I .
Fie G = (V,E) un graf. Se numeşte cuplaj (Berge) o mulţime F �E astfel încât muchiile
din F sunt neadiacente. Un cuplaj se numeşte perfect dacă orice vârf din V este extremitate a unei
muchii din F. Un cuplaj se numeşte maximal dacă are cardinal maxim între toate cuplajele
grafului G.
O submulţime nevidă A, a lui V(G), se numeşte modul dacă "x �V(G) - A , ori x ~ A ori
x ~/ A . Dacă A este submulţime proprie a lui V(G) cu cel puţin două elemente, atunci A se
numeşte mulţime omogenă (Babel, Olariu, Olaru). Summer numeşte mulţimea A, partitivă.
Fie A o mulţime omogenă în G. G / A este graful obţinut din G, înlocuind A cu un nou
vârf a şi conectând a prin muchii cu toate vârfurile x �V(G) – A dacă şi numai dacă x ~ A.

1.8. Algoritmi şi complexitate de calcul


Vom înţelege prin problemă algoritmică (Croitoru) o funcţie total definită P : I � F , unde
I este mulţimea informaţiilor iniţiale (intrărilor problemei), iar F este mulţimea informaţiilor
finale. Vom presupune că I şi F sunt cel mult numărabile. Dacă i �I este precizat, atunci
determinarea lui P(i) se numeşte instanţă a problemei P. Vom folosi pentru o instanţă notaţia p şi
prin abuz de notaţie vom scrie p �P .
Pentru fiecare instanţă p �P se poate asocia un număr natural g(p) numit dimensiunea
problemei.
Un algoritm care rezolvă problema P va porni de la o codificare a unei instanţe oarecare a
problemei P si va oferi o codificare a rezultatului.
Vom nota cu TA (p) timpul necesar algoritmului A pentru rezolvarea instanţei p a
problemei P.
Comportarea în cazul cel mai nefavorabil a algoritmului A pe o intrare de dimensiune n
este
TA (n) = sup{TA (p) | p �Pşi g(p) =n} .
Acest tip de analiză a algoritmilor ne asigură că, oricare ar fi o intrare de dimensiune n,
timpul de lucru este mărginit de TA (n) . De aceea, o abordare naturală este să se studieze
comportarea în medie a unui algoritm, care presupune:
9
 precizarea unei distribuţii de probabilitate pe mulţimea instanţelor p �P ;
 determinarea mediei variabilei aleatoare TA (p) :
TAmed (n) = M({TA (p) | p �Pşi g(p) =n}) .
Fie f : N � N . Atunci
O(f ) =�
{g$| �
g :>$
N Σ"�
N, c R, c 0, n 0 N : g(n) cf (n), n n 0 } .
Un algoritm A cu proprietatea că TA(n) = O(p(n)), unde p este un polinom în n se numeşte
polinomial. Un algoritm care nu este polinomial se numeşte exponenţial. O problema pentru care
nu se cunosc algoritmi polinomiali se numeşte intractabilă.

Discutii finale:
Sa se determine, pentru un graf dat, gradele varfurilor, subgrafuri, operatii cu diferite
subgrafuri. Pentru un graf dat sa se precizeze diferite multimi separatoare, transversale, drumuri.
Pentru diferita clase de grafuri sa se calculeze numere combinatorii.
Tema propusa:
Determinati complexitatea de calcul a unor algoritmi descrisi in disciplinele anterioare de
felul algoritmi de sortare si cautare.

Capitolul 2
METODE DE CĂUTARE ŞI PROGRAMARE

10
Cuvinte cheie
Greedy, backtracking, BFS, DFS.
Obiectivele invatarii
Dupa parcurgerea acestui capitol va trebui:
Sa stiti căutarea în lăţime, căutarea în adâncime, metoda greedy, metoda backtracking, metoda
divide et impera, metoda branch and bound.

2.1. Căutarea în lăţime


În teoria grafurilor, breadth-first search (BFS) este un algoritm de căutare în grafuri,
care începe cu vârful rădăcină şi explorează toate nodurile vecine. Apoi, pentru fiecare dintre
aceste noduri se explorează nodurile vecine încă necercetate, ş.a.m.d.., până când scopul a fost
atins.
BFS este o metodă de căutare, care ţinteşte extinderea şi examinarea tuturor nodurilor
unui graf, cu scopul de a găsi soluţia.
Din punct de vedere al algoritmului, toate nodurile „fii” obţinute prin expansiunea unui
nod sunt adăugate într-o „coadă” de tipul FIFO (First In First Out). În implementările tipice,
nodurile care nu au fost încă examinate de către vecinii corespunzători sunt plasate într-un
„recipient” (asemănător unei cozi sau unei liste de legătură), numit „deschis”, iar odată examinaţi
sunt plasaţi în „recipientul” „închis”.

2.1.1. Algoritmul
1. Introducerea nodului rădăcină în coadă.
2. Extragerea unui nod din capătul listei şi examinarea acestuia.
 Dacă elementul căutat se identifică cu acest nod, se renunţă la căutare şi se
returnează rezultatul.
 Altfel, se plasează toţi succesorii (nodurile „fii”) (neexaminaţi încă) acestui nod la
sfârşitul „cozii” (acesta în cazul în care există)
3. Dacă „coada” este goală, fiecare nod al grafului a fost examinat - se renunţă la căutare
şi se întoarce la „not found”.
4. Repetă începând cu Pasul 2.

2.1.2. Implementarea C++


În continuare este implementarea algoritmului de mai sus, unde „neexaminaţii până în
momentul de faţă” sunt gestionaţi de către tabloul părinte.
Fie structura struct şi structura de noduri
struct Vertex {
...
std::vector<int> out;
...
};

std::vector<Vertex> graph(vertices);
bool BFS(const std::vector<Vertex>& graph, int start, int end) {
std::queue<int> next;
std::vector<int> parent(graph.size(), 0);
parent[start] = -1;
next.push(start);
while (!next.empty()) {

11
int u = next.front();
next.pop();
if (u == end) return true;
for (std::vector<int>::const_iteratorj=graph[u].out.begin();
j != graph[u].out.end(); ++j)
{
// Look through neighbors.
int v = *j;
if (parent[v] == 0) {
// If v is unvisited.
parent[v] = u;
next.push(v);
}
}
}
return false;
}

Sunt stocaţi părinţii fiecărui nod, de unde se poate deduce drumul.

2.1.3. Complexitate şi optimalitate


Complexitate în spaţiu. Având în vedere faptul că toate nodurile descoperite până la
momentul de faţă trebuiesc salvate, complexitatea în spaţiu a breadth-first search este
O ( V  E ) , unde V reprezintă numărul nodurilor, iar E numărul muchiilor grafului. Altă
modalitate de a consemna acest lucru: complexitate O ( B M ) , unde B reprezintă cea mai lunga
ramură, iar M lungimea maximă a drumului arborelui. Această cerere imensă de spaţiu este
motivul pentru care breadth-first search nu este practică în cazul problemelor mai ample.
Complexitatea în timp. Odată ce, în cel mai rău caz breadth-first search trebuie să ia în
considerare toate drumurile către toate nodurile, complexitatea în timp a acestui tip de căutare
este de O(| V |  | E |) . Cel mai bun caz în această căutare este conferit de complexitatea O(1) .
Are loc atunci când nodul este găsit la prima parcurgere.
Completitudine. Metoda breadth-first search este completă. Această înseamnă că dacă
există o soluţie , metoda breadth-first search o va găsi, indiferent de tipul grafului. Cu toate
acestea, dacă graful este infinit şi nu există nici o soluţie, breadth-first search va “eşua”.
Optimalitate. Pentru costul unitar pe muchii, bread-first search este o metodă optimă. În
general, breadth-first search nu este o metodă optimă, şi aceasta deoarece returnează întotdeauna
rezultatul cu cele mai puţine muchii între nodul de start şi nodul vizat. Dacă graful este un graf
ponderat, şi drept urmare are costuri asociate fiecărei etape, această problemă se rezolvă
îmbunătăţind metoda breadth-first search astfel încât să se uniformizeze costurile de căutare,
identificate cu: costurile drumului. Totuşi, dacă graful nu este ponderat, şi prin urmare toate
costurile etapelor sunt egale, breadth-first search va identifica cea mai apropiată şi optimă
soluţie.

2.1.4 Aplicaţii ale BFS


Breadth-first search poate fi folosită pentru rezolvarea unei game variate de probleme de
teoria grafurilor, printre care:
• Găsirea tuturor componentelor conexe dintr-un graf.
• Identificarea tuturor nodurilor într-o componentă conexă.
• Găsirea celui mai scurt drum între nodurile u şi v (într-un graf neponderat).
• Testarea bipartiţiei unui graf.
Găsirea Componentelor Conexe
Mulţimea vârfurilor accesate prin metode BFS reprezintă cea mai mare componentă
conexă care conţine vârful de start.
12
Testarea bipartiţiei
BFS poate fi folosită pentru testarea bipartiţiei, începând căutarea cu orice vârf şi
atribuind etichete alternative vârfurilor vizitate în timpul căutării. Astfel, se atribuie eticheta 0
vârfului de start, 1 tuturor vecinilor săi, 0 vecinilor acelor vecini, şi aşa mai departe. Dacă într-un
anumit moment al procesului un vârf are vecini vizitaţi cu aceeaşi etichetă, atunci graful nu este
bipartit. Dacă parcurgerea se sfârşeşte fără a se produce o astfel de situaţie, atunci graful este
bipartit.

2.2. Căutarea în adâncime


Depth-first search (DFS) este un algoritm căutare a arborelui, structurii arborelui, sau a
grafului. Formal, DFS reprezintă o căutare care evoluează prin expansiunea la primul vârf „fiu” a
arborelui ce ia naştere pe măsură ce se coboară în adâncime, până în momentul în care vârful
„ţintă” este descoperit sau până când se întâlneşte un vârf care nu are „fii”. La pasul următor,
căutarea se reia (backtracking), revenind la nodul cel mai recent vizitat, însă pentru care
explorarea nu este încheiată. Într-o implementare ne-recursivă, toate vârfurile recent vizitate sunt
adăugate într-o stivă de tipul LIFO (Last In First Out), în scopul explorării acestora.
Complexitatea în spaţiu a DFS este cu mult mai mică decât cea a BFS (Breadth-First Search). De
asemenea se pretează mult mai bine metodelor euristice de alegere a ramurilor asemănătoare.
Complexitatea în timp a ambilor algoritmi este proporţională cu numărul vârfurilor plus numărul
muchiilor grafului corespunzător ( O( V  E ) ) .
Căutarea în adâncime se poate folosi şi la ordonarea liniară a vârfurilor grafului (sau
arborelui). Există trei astfel de posibilităţi:
■ O preordine reprezintă o listare a vârfurilor în ordinea în care au fost vizitaţi prin
intermediul algoritmului căutării în adâncime. Aceasta este o modalitate naturală şi compactă de
descriere a progresului căutării. O preordine a unei expresii arbore este ceea ce numim expresie
în notaţia Polonezǎ.
■ O postordine reprezintă o listare în care cel din urmă vârf vizitat este primul element
al listei. O postordine a unui expresii arbore este de fapt expresia în oglindă a expresiei în notaţie
Polonezǎ.
■ O postordine inversată (în oglindă) este, de fapt, reversul postordinii, i.e. o listare a
vârfurilor în ordinea inversă a celei mai recente vizite a vârfurilor in cauză. În căutarea unui
arbore, postordinea inversată coincide cu preordinea, însă, în general, diferă atunci când se caută
un graf.
Spre exemplu când se caută graful:

începând cu vârful A, preordinile posibile sunt A B D C, respectiv A C D B (în funcţie de alegerea


algoritmului de a vizita mai întâi vârful B sau vârful C), în timp ce postordinile inversate (în
oglindă) sunt: A B C D şi A C B D. Postordinea inversată produce o sortare topologică a oricărui
graf orientat aciclic. Această ordonare este folositore şi în analiza fluxului de control,
reprezentând adesea o liniarizare naturală a fluxului de control. Graful mai sus amintit poate
reprezenta fluxul de control într-un fragment de cod ca cel de mai jos:

13
if (A) then {
B
} else {
C
}
D

şi este natural să considerăm că acest cod urmează ordinea A B C D sau A C B D, însă nu este
normal să urmeze ordinea A B D C sau A C D B.
PSEUDOCOD (recursiv)
dfs(v)
process(v)
mark v as visited
for all vertices i adjacent to v not visited
dfs(i)

O altă variantă
dfs(graph G)
{
list L = empty
tree T = empty
choose a starting vertex x
search(x)
while(L is not empty)
{
remove edge (v, w) from beginning of L
if w not yet visited
{
add (v, w) to T
search(w)
}
}
}
search(vertex v)
{
visit v
for each edge (v, w)
add edge (v, w) to the beginning of L
}

Aplicaţii
Iată câţiva algoritmi în care se foloseşte DFS:
■ Găsirea componentelor conexe.
■ Sortarea topologică.
■ Găsirea componentelor tare conexe.

2.3. Metoda Greedy


Descrierea metodei Greedy
Metoda Greedy (greedy = lacom) este aplicabilă problemelor de optim.
Considerăm mulţimea finită
A = {a1 ,..., a n }
şi o proprietate p definită pe mulţimea submulţimilor lui A:
� / =1
p(0)
p : P(A) � {0,1} cu �
�p(X) = 1 � p(Y) = 1, "Y �X
O submulţime S �A se numeşte soluţie dacă p(S) = 1.

14
Dintre soluţii va fi aleasă una care optimizează o funcţie de cost
p : P(A) � R
dată.
Metoda urmăreşte evitarea căutării tuturor submulţimilor (ceea ce ar necesita un timp de
calcul exponenţial), mergându-se "direct" spre soluţia optimă. Nu este însă garantată obţinerea
unei soluţii optime; de aceea aplicarea metodei Greedy trebuie însoţită neapărat de o
demonstraţie.
Distingem doua variante generale de aplicare a metodei Greedy:
Prima variantă alege în mod repetat câte un element oarecare al mulţimii A şi îl adaugă
soluţiei curente S numai dacă în acest mod se obţine tot o soluţie. În a doua variantă procedura
prel realizează o permutare a elementelor lui A, după care elementele lui A sunt analizate în
ordine şi adăugate soluţiei curente S numai dacă în acest mod se obţine tot o soluţie.
Exemplu. Se consideră mulţimea de valori reale A = {a1 ,..., a n } . Se caută
submulţimea a cărei sumă a elementelor este maximă.
Vom parcurge mulţimea şi vom selecta numai elementele pozitive, care vor fi plasate în
vectorul soluţie s.

k�0
for i = 1,n
if ai > 0
then k � k + 1; sk � ai
write(s)

2.4. Metoda backtracking


Un algoritm este considerat "acceptabil" numai dacă timpul său de executare este
polinomial, adică de ordinul O(nk) pentru un anumit k; n reprezintă numărul datelor de intrare.
Pentru a ne convinge de acest lucru, vom considera un calculator capabil să efectueze un
milion de operaţii pe secundă. în tabelul următor apar timpii necesari pentru a efectua n 3, 2n şi 3n
operaţii, pentru diferite valori mici ale lui n:

n = 20 n = 40 n = 60
n3 - - 0,2 sec
2n 1 sec 12,7 zile 366 secole
3n 58 min 3855 secole 1013 secole

Tabelul de mai sus arată că algoritmii exponenţiali nu sunt acceptabili.

Descrierea metodei Backtracking


Fie produsul cartezian X = X1 �... �X n . Căutam x �X cu j(x) = 1 , unde j : X � {0,1}
este o proprietate definită pe X.
Din cele de mai sus rezultă că generarea tuturor elementelor produsului cartezian X nu
este acceptabilă.
Metoda backtracking încearcă micşorarea timpului de calcul. X este numit spaţiul
soluţiilor posibile, iar j sintetizează condiţiile interne.
Vectorul X este construit progresiv, începând cu prima componentă. Nu se trece la
atribuirea unei valori lui x, decât dacă am stabilit valori pentru x1 ,..., x k -1 şi

15
jk -1 (x1 ,..., x k -1 ) = 1 . Funcţiile jk : X1 �... �X n � {0,1} se numesc condiţii de continuare şi
sunt de obicei restricţiile lui j la primele k variabile. Condiţiile de continuare sunt strict
necesare, ideal fiind să fie şi suficiente.
Distingem următoarele cazuri posibile la alegerea lui xk:
1) "Atribuie şi avansează": mai sunt valori neanalizate din Xk şi valoarea xk aleasă
satisface j k=> se măreşte k.
2) "Încercare eşuată": mai sunt valori neconsumate din Xk şi valoarea xk aleasă dintre
acestea nu satisface j k=> se va relua, încercându-se alegerea unei noi valori pentru
xk.
3) "Revenire": nu mai există valori neconsumate din Xk (Xk epuizată) � întreaga Xk
devine disponibilă şi k <- k-1.
4) "Revenire după determinarea unei soluţii": este reţinută soluţia.
Reţinerea unei soluţii constă în apelarea unei proceduri retsol care prelucrează soluţia şi
fie opreşte procesul (dacă se doreşte o singură soluţie), fie prevede k � k-1 (dacă dorim să
determinăm toate soluţiile).
Notăm prin Ck �X k mulţimea valorilor consumate din Xk. Algoritmul este următorul:

Ci<- 0, " i;
k<-l;
while k > 0
if k = n+1
then retsol (x); k<- k-1;
else if Ck �X k
then alege v �X k \ Ck ; C k � C k �{v} ;
if jk (x1 ,..., x k -1 , v) = 1
then x k � v ; k<- k+l;
else
else Ck<- 0/ ; k<- k-l;

Pentru cazul particular X1 = ... = X n = {1,...,s} , algoritmul se simplifică

k<-l; x i <-0, " i = 1, …, n;


while k > 0
if k = n+1
then retsol (x); k<-k-l;
else if x k < s
then x k � x k  1 ;
if jk (x1 ,..., x k ) = 1
then k<- k+l;
else
else x k <-0; k<-k-l;

2.5. Metoda divide et impera


Metoda Divide et Impera ("desparte şi stăpâneşte") consta în împărţirea repetată a unei
probleme de dimensiuni mari în mai multe subprobleme de acelaşi tip, urmată de rezolvarea
acestora şi combinarea rezultatelor obţinute pentru a determina rezultatul corespunzător
problemei iniţiale. Pentru fiecare subproblemă procedăm în acelaşi mod, cu excepţia cazului în
care dimensiunea ei este suficient de mică pentru a fi rezolvată direct. Este evident caracterul
recursiv al acestei metode.

16
Schema generală
Descriem schema generală pentru cazul în care aplicăm metoda pentru o prelucrare
oarecare asupra elementelor unui vector. Funcţia DivImp, care întoarce rezultatul prelucrării
asupra unei subsecvenţe a p ,..., a u , va fi apelată prin DivImp (1,n).
function DivImp(p,u)
if u-p < e
then r <- Prel (p, u)
else m <- Interm (p,u);
r1 <- DivImp (p,m);
r2 <- DivImp (m+1,u);
r <- Combin (r1,r2)
return r
end;

unde:
� p u�
- funcţia Interm întoarce un indice în intervalul p..u; de obicei m = � ;
�2 � �
- funcţia Prel întoarce rezultatul subsecvenţei p .. u, dacă aceasta este suficient de mică;
- funcţia Combin întoarce rezultatul asamblării rezultatelor parţiale r1 şi r2.
2.6. Metoda Branch and Bound
Prezentare generală
Metoda Branch and Bound se aplică problemelor care pot fi reprezentate pe un arbore: se
începe prin a lua una dintre mai multe decizii posibile, după care suntem puşi în situaţia de a
alege din nou dintre mai multe decizii; vom alege una dintre ele etc. Vârfurile arborelui
corespund stărilor posibile în dezvoltarea soluţiei.
Deosebim două tipuri de probleme:
1) Se caută un anumit vârf, numit vârf rezultat, care nu are descendenţi.
2) Există mai multe vârfuri finale, care reprezintă soluţii posibile, dintre care căutăm de exemplu
pe cel care minimizează o anumită funcţie.
Deşi metoda este aplicabilă pe arbori. Există multe deosebiri, dintre care menţionăm:
- ordinea de parcurgere a arborelui;
- modul în care sunt eliminaţi subarborii care nu pot conduce la o soluţie;
- faptul ca arborele poate fi infinit (prin natura sa sau prin faptul că mai multe
vârfuri pot corespunde la o aceeaşi stare).
În general arborele de stări este construit dinamic.
Este folosită o listă L de vârfuri active, adică de stări care sunt susceptibile de a fi
dezvoltate pentru a ajunge la soluţie / soluţii. Iniţial, lista L conţine rădăcina arborelui, care este
vârful curent. La fiecare pas, din L alegem un vârf (care nu este neapărat un fiu al vârfului
curent!), care devine noul vârf curent.
Când un vârf activ devine vârf curent, sunt generaţi toţi fiii săi, care devin vârfuri active
(sunt incluşi în L). Apoi din nou este selectat un vârf curent.
Legat de modul prin care alegem un vârf activ drept vârf curent, deci implicit legat de
modul de parcurgere a arborelui, facem următoarele remarci:
- căutarea în adâncime nu este adecvată, deoarece pe de o parte arborele poate fi infinit, iar
pe de altă parte soluţia căutată poate fi de exemplu un fiu al rădăcinii diferit de primul
fiu şi căutarea în adâncime ar fi ineficientă: se parcurg inutil stări, în loc de a avansa
direct spre soluţie;
- căutarea pe lăţime conduce totdeauna la soluţie (dacă aceasta există), dar poate fi
ineficientă dacă vârfurile au mulţi fii.

17
Metoda Branch and Bound încearcă un "compromis" între cele două căutări menţionate
mai sus, ataşând vârfurilor active cate un cost pozitiv, ce intenţionează sa fie o măsură a gradului
de "apropiere" a vârfului de o soluţie. Alegerea acestui cost este decisivă pentru a obţine un timp
de executare cât mai bun şi depinde de problema concretă, dar şi de abilitatea programatorului.
Costul unui vârf va fi totdeauna mai mic decât cel al descendenţilor (fiilor) săi.
De fiecare dată drept vârf curent este ales cel de cost minim (cel considerat ca fiind cel
mai "aproape" de soluţie).
Din analiza teoretică a problemei deducem o valoare lim care este o aproximaţie prin
adaos a minimului căutat: atunci când costul unui vârf depaseste lim, vârful curent este ignorat:
nu este luat în considerare şi deci este eliminat întregul subarbore pentru care este rădăcină. Dacă
nu cunoaştem o astfel de valoare lim, o iniţializăm cu + �.
Se poate defini o funcţie de cost ideală, pentru care c(x) este dat de:
 nivelul pe care se află vârful x dacă x este vârf rezultat;
 + � dacă x este vârf final, diferit de vârf rezultat;
 min {c(y) | y fiu al lui x} dacă x nu este vârf final.
Această funcţie este ideală din două puncte de vedere:
- nu poate fi calculată dacă arborele este infinit; în plus, chiar dacă arborele
este finit, el trebuie parcurs în întregime, ceea ce este exact ce dorim să evităm;
- dacă totuşi am cunoaşte această funcţie, soluţia poate fi determinată
imediat: plecăm din rădăcină şi coborâm mereu spre un vârf cu acelaşi cost, până
ajungem în vârful rezultat.
Neputând lucra cu funcţia ideală de mai sus, vom alege o aproximaţie d a lui c, care
trebuie să satisfacă condiţiile:
1) în continuare, dacă y este fiu al lui x avem d(x) < d(y);
2) d(x) să poată fi calculată doar pe baza informaţilor din drumul de la
rădăcină la x;
3) este indicat ca d �c pentru a ne asigura că dacă d(x ) > lim, atunci şi
c(x) > lim, deci x nu va mai fi dezvoltat.
O primă modalitate de a asigura compromisul între căutările în adâncime şi pe lăţime este
de a alege funcţia d astfel încât, pentru o valoare naturală k, să fie îndeplinită condiţia: pentru
orice vârf x situat pe un nivel nx şi orice vârf situat pe un nivel ny �nx + k, să avem d(x) > d(y),
indiferent dacă y este sau nu descendent al lui x.
Condiţia de mai sus spune că niciodată nu poate deveni activ un vârf aflat pe un nivel ny
� nx + k dacă în L apare un vârf situat pe nivelul nx, adică nu putem merge "prea mult" în
adâncime. Dacă aceasta condiţie este îndeplinită, este valabilă următoarea propoziţie:
Propoziţie.
În ipoteza că este îndeplinită condiţia de mai sus şi dacă există soluţie, ea va fi atinsă într-
un timp finit, chiar dacă arborele este infinit.
Algoritmul Branch & Bound pentru probleme de optim
Să presupunem că dorim să determinăm vârful final de cost minim şi drumul de la
rădăcină la el. Fie lim aproximarea prin adaos considerată mai sus.
Algoritmul este următorul (rad este rădăcina arborelui, iar ifinal este vârful rezultat):

i<-rad; L<={i}; min<-lim;


calculăm d(rad); tata(i)<-0
while L≠ Ø
i <= L {este scos vârful i cu d(i) minim din min-ansamblul L}
for toţi j fii ai lui i
calculăm d(j); calcule locale asupra lui j; tata(j)<-i
if j este vârf final
then if d(j)< min

18
then min<-d(j); ifinal<-j
elimină din L vârfurile k cu d(k) �min (*)
else if d(j)<min
then j => L
if min =lim then write {'Nu există soluţie')
else writeln (min); i<-ifinal
while i≠ 0
write{i); i<-tata{i)

La (*) am ţinut cont de faptul că dacă j este descendent al lui i, atunci


d(i ) < d(j).

Discutii finale:
Sa se aplice pe diverse grafuri particulare căutarea în lăţime, căutarea în adâncime, metoda
greedy, metoda backtracking, metoda divide et impera, metoda branch and bound.
Tema propusa:
Sa se aplice metodele prezentate pentru diverse probleme.

Capitolul 3
STRUCTURI DE DATE

Cuvinte cheie
Liste, arbori.
Obiectivele invatarii
Dupa parcurgerea acestui capitol va trebui:
Sa se cunoasca parcurgerea unei liste simplu înlănţuite, parcurgerea unei liste dublu înlănţuite,
parcurgerea arborilor binari.

3.1. Liste
Pentru a îmbunătăţi utilizarea memoriei sunt folosite structuri de date înlănţuite.
Acestea poartă numele de liste, fiind compuse dintr-o mulţime de noduri între care sunt
definite anumite legături. Se întâlnesc liste simplu înlănţuite, liste dublu înlănţuite, liste
19
circulare, structuri de liste şi liste speciale care sunt formate din atomi şi din alte liste. În
continuare se prezintă listele simplu înlănţuite şi dublu înlănţuite.

3.1.1. Liste simplu înlănţuite


Lista simplu înlănţuită este o structură liniară de date, în care fiecare nod este
format dintr-o parte de informaţie ce reţine diverse valori caracteristice elementului
respectiv şi o parte de legătură ce reţine adresa următorului element al listei. Există
valoarea NIL ce semnifică adresa către nicăieri folosită în marcarea legăturii ultimului
element al listei. Pentru a crea un nou nod, folosim o procedură predefinită new iar
pentru a elibera spaţiul de memorie în momentul în care nu mai este necesară reţinerea
unui anumit nod, există procedura predefinită dispose.
Declararea unei liste simplu înlănţuite se realizează astfel:
lista=Anod;
nod=record
inf:integer;
leg:lista;
end;
L:lista;

În pseudocod, crearea unei liste simplu înlănţuite poate fi realizată astfel:


algoritm creare (L:lista);
{ L:=NIL;
read (A);
while (A<>0) do
{ new(Pl);
P->inf:=A;
P->leg:=L;
L:=P;
readln (A);
}
}
Procesul se încheie la citirea numărului 0. Prezentam în continuare o variantă
recursivă de creare a listei:
algoritm creare:lista;
{ read (A);
if (A=O) then creare:=NIL;
else
{ new(P);
P->inf:=A;
P->leg:=creare;
creare:=P;
}
}

3.1.2. Parcurgerea unei liste simplu înlănţuite


După ce a fost creată, se doreşte în cele mai multe cazuri prelucrarea listei şi
afişarea rezultatelor. Deoarece există numai legătura către următorul element al listei,
toate prelucrările vor fi făcute începând cu primul element până la ultimul, în ordinea
dată de legături. Cel mai simplu exemplu de prelucrare îl constituie afişarea şirului
valorilor memorate în listă. Parcurgem pas cu pas elementele listei şi afişăm informaţia
elementului curent:
algoritm listare(L:lista);
{ P:=L;

20
while (P<>NIL) do
{ write (P->inf,’ ‘);
P:=P->leg;
}
)

3.1.3. Liste dublu înlănţuite


Într-o listă dublu înlănţuită fiecare nod are o parte de informaţie şi două legături:
legătura succ către următorul element şi legătura pred către elementul precedent.
Putem memora poziţia primului element L al listei. De cele mai multe ori se vor memora
doua poziţii în listă: poziţia P a primului element şi poziţia U a ultimului element.
Declararea listei dublu înlănţuite se realizează în felul următor:
lista=^nod;
nod=record
inf:integer;
succ,pred:lista;
end;
L:lista;

Inserarea unui nou nod în interiorul unei liste dublu înlănţuite se execută prin
introducerea informaţiei ataşate şi refacerea a patru legături, astfel:

new (Q);
Q^.inf:=A;
Q^.succ:=P^.succ;
Q^.pred:=P;
Q^.succ^.pred:=Q;
P^.succ:=Q;

Pentru eliminarea nodului desemnat de pointerul P, se vor executa instrucţiunile:


Q:=P;
P^.succ^.pred:=Q^.pred;
P^.pred^.succ:=Q^.succ;
dispose (Q) ;

3.1.4. Parcurgerea unei liste dublu înlănţuite


Fie o listă dublu înlănţuită, având referinţele la capete P, respectiv Q.
a) Să se parcurgă lista de la stânga la dreapta şi de la dreapta la stânga.
b) Să se calculeze referinţa elementului aflat aproximativ la mijlocul acestei liste.
Prezentam rezolvările în pseudocod:
algoritm listare1(P,Q:lista);
{ AUX:=P;
while (AUX<>NIL) do
{ write (AUX->inf,’ ‘);
AUX:=AUX->succ;
}
}
algoritm listare2(P,Q:lista);
{ AUX:=Q;
while (AUX<>NIL) do
{ write (AUX->inf, ‘ ‘) ;
AUX:=AUX->pred;
}
}
algoritm cautare(P,Q:lista):lista;

21
{ AUXl:=P;
AUX2:=Q;
while (AUXl<>AUX2) do
{ AUXl:=AUXl->succ;
if (AUXl=AUX2) then return (AUXl);
AUX2:=AUX2->pred;
if (AUXl=AUX2) then return (AUXl);
}
}

3.2. Arbori
3.2.1. Arbori liberi
Definiţie. Fie o mulţime V de noduri şi o mulţime E de muchii, fiecare muchie
legând două noduri distincte.
Se numeşte lanţ un şir X1, X2, … , XL de noduri pentru care oricare două noduri
consecutive sunt legate printr-o muchie. Dacă nodurile sunt distincte, lanţul se numeşte
elementar.
Se numeşte ciclu elementar un lanţ X1, X2, … , XL, X1 pentru care lanţul X1, X2, … ,
XL este lanţ elementar.
Se numeşte arbore liber o pereche A = (V,E) cu proprietăţile:
l. Oricare două noduri distincte sunt legate printr-un lanţ.
2. Nu conţine cicluri elementare.
Propoziţie. Un arbore liber cu |V| = n noduri are exact n – l muchii. Se poate
demonstra prin inducţie după numărul n de noduri.
Propoziţie. Următoarele afirmaţii sunt echivalente:
l. A = (V, E) este arbore liber.
2. A = (V,E) cu |V| = n are exact n – 1 muchii şi nu conţine cicluri elementare.
3. A = (V,E) cu |V| = n are exact n – 1 muchii şi oricare două noduri sunt legate
printr-un lanţ.
3.2.2. Arbori cu rădăcină
Definiţie. Se numeşte arbore cu rădăcină o mulţime de noduri şi muchii în care:
există un nod special numit rădăcină, iar celelalte noduri sunt repartizate în k mulţimi
disjuncte A1, A2, … , Ak care sunt la rândul lor arbori cu rădăcină.
Observaţie.
1. Prin alegerea unui nod drept rădăcină, un arbore liber se poate transforma în
arbore cu rădăcină. Totodată, fiecărui nod al arborelui îi va fi asociat un nivel. Nivelul
rădăcinii se consideră a fi nivelul 1, iar un nod de pe nivelul i are descendenţii direcţi pe
nivelul i + 1.
2. Pentru fiecare nod, se consideră o ordine a mulţimilor A 1, A2,… , Ak. Spunem
atunci că arborele cu rădăcină este ordonat.
Definiţie. Numim adâncimea unui arbore cu rădăcină nivelul maxim pe care îl are
un nod al acestui arbore.
Modalităţi statice de memorare
1. Putem memora arborele ca o expresie cu paranteze, în care prima poziţie este
eticheta rădăcinii, urmată, între paranteze, de lista subarborilor respectivi.
2. Putem memora un vector de taţi. Vectorul are lungimea egală cu numărul de
noduri al arborelui, fiecare poziţie i memorând ascendentul direct al nodului i, iar
ascendentul rădăcinii (care nu există) se consideră a fi 0.
3.2.3. Arbori binari
Definiţie. Un arbore binar este un arbore cu rădăcină, în care orice nod are cel

22
mult doi descendenţi direcţi şi se face distincţia între descendentul stâng şi
descendentul drept.
Definiţie. Un arbore binar se numeşte arbore binar strict dacă fiecare nod care
are descendenţi direcţi are exact doi astfel de descendenţi.
Definiţie. Un arbore binar se numeşte arbore binar plin dacă are un număr de n
nivele şi pentru toate nodurile de pe nivele 1, 2, ... , n – l există doi descendenţi direcţi.
Un arbore plin cu n nivele are
1  2  ...  2n -1 = 2n - 1
noduri.
Definiţie. Un arbore binar se numeşte arbore binar complet dacă pe primele n –
1 niveluri are toate nodurile posibile, iar pe ultimul nivel n are o parte din noduri,
considerate pe orizontală în ordinea de la stânga la dreapta.
Modalităţi statice de memorare
1. Putem memora un arbore binar prin memorarea etichetei nodului rădăcină şi
folosind doi vectori ST şi DR ce memorează etichetele descendenţilor direcţi stâng
respectiv drept. Dacă nu există descendent direct, pe poziţia respectiva se va memora
valoarea 0.
2. Dacă arborele este arbore binar complet, sau apropiat de un arbore binar
complet putem folosi eficient un singur vector, în care legăturile stânga şi dreapta sunt
implicite.

3.2.4. Parcurgerea arborilor binari


Preordine
Parcurgerea în preordine constă în vizitarea rădăcinii urmată de vizitarea
subarborelui stâng şi apoi a subarborelui drept, acest lucru fiind valabil recursiv, pentru
orice subarbore al arborelui considerat. Algoritmul recursiv este următorul:
algoritm preordine(A:arbore);
{ if (A<>NIL) then
{ write (A->INF) ;
preordine(A->ST);
preordine(A->DR);
}
}

Inordine
Parcurgerea în inordine vizitează, pentru fiecare subarbore, mai întâi subarborele
stâng, apoi rădăcina, apoi subarborele drept. Dacă arborele binar respectiv este şi
arbore de căutare, atunci parcurgerea în inordine vizitează vârfurile în ordinea
crescătoare a cheilor. Prezentăm algoritmul recursiv:
algoritm inordine(A:arbore);
{ if (A<>NIL) then
{ inordine(A->ST);
write(A->INF);
inordine(A->DR);
}
}

Postordine
Parcurgerea în postordine vizitează, pentru fiecare subarbore, mai întâi
subarborele său stâng, apoi subarborele său drept, apoi vârful rădăcină.

23
Parcurgerea în postordine se poate realiza recursiv astfel:
algoritm postordine(A:arbore);
{ if (A<>NIL) then
{ postordine(A->ST);
postordine(A->DR);
write(A->INF);
}
}

Toţi algoritmii recursivi prezentaţi au şi variante iterative, eliminarea recursivităţii


realizându-se prin folosirea explicită a unor stive. Prezentăm în continuare algoritmul
iterativ de parcurgere în preordine:
algoritm RSD_iterativ
{ stiva<-vida;
I :=RAD;
OK:=true;
while OK=true do
{ while (I<>NIL) do
{ write( I );
stiva<-I;
I:=ST[I);
}
if (stiva<>vida) then
{ I<-stiva;
I:=DR[I);
}
else OK:=false;
}
}

Parcurgerea pe nivele
Dându-se un arbore binar, să se viziteze vârfurile acestuia în ordinea crescătoare
a nivelelor. Acest lucru se realizează folosind o coadă auxiliară de noduri vizitate dar
neprelucrate. Algoritmul se încheie când coada devine vidă, şi este o particularizare a
parcurgerii în lăţime a unui graf.
Procedura este iterativă şi poate fi prezentată astfel:
algoritm nivele{A:arbore);
ST:stiva; P,U:intregi;
{ P:=O;U:=O;
U:=U+1;ST[U):=A;write {A->INF);
while (P<U) do
{ P:=P+1; NOD:=ST[P];
if {NOD->ST<>NIL) then
{ U:=U+1; ST[U):=NOD->ST;
write{NOD->ST->INF);}
if {NOD->DR<>NIL) then
{ U:=U+1; ST[U]:=NOD->DR;
write{NOD->DR->INF);}
}
}

Discutii finale:
Sa se aplice parcurgerea unei liste simplu înlănţuite, parcurgerea unei liste dublu înlănţuite,
parcurgerea arborilor binari pe diverse probleme particulare
Tema propusa:

24
Sa se proiecteze programe intr-un limbaj de programare care sa realizeze parcurgerea unei liste
simplu înlănţuite, parcurgerea unei liste dublu înlănţuite, parcurgerea arborilor binari.

Capitolul 4
PARCURGERI DE GRAFURI

Cuvinte cheie
Parcurgerea BF, DF a grafurilor.
Obiectivele invatarii
Dupa parcurgerea acestui capitol va trebui:
Sa se cunoasca parcurgerea BF a grafurilor, parcurgerea DF a grafurilor, notiunea de
componente conexe ale unui graf.

Problema parcurgerii unui digraf G = (N,A), N = {1, ... , n}, A = {1, ... , m} are
următoarea formulare: să se genereze mulţimea W �N a nodurilor y pentru care există drum de
25
la un nod sursă dat s la nodul y în digraful G. Dacă există drum, în digraful G, de la nodul sursă
s la nodul y atunci se spune că nodul y este accesibil din nodul s.
Algoritmii pe care îi vom prezenta pentru rezolvarea problemei parcurgerii unui digraf G
sunt metode sistematice de vizitare a nodurilor y accesibile din s. Fiecare iteraţie a execuţiei
oricărui algoritm de parcurgere stabileşte pentru fiecare nod apartenenţa la una din următoarele
trei stări:
 nevizitat;
 vizitat şi neanalizat, adică un nod vizitat ai cărui succesori au fost parţial vizitaţi;
 vizitat si analizat, adică un nod vizitat ai cărui succesori au fost în totalitate
vizitaţi.
Dacă nodul x este vizitat si neanalizat, există arcul (x,y) şi nodul y este nevizitat, atunci se
poate vizita nodul y. În acest caz se spune că arcul (x,y) este arc admisibil şi dacă nodul y este
vizitat explorând arcul (x,y) se spune că nodul x este predecesorul parcurgere al nodului y.
Se vor prezenta următorii algoritmi pentru parcurgerea unui digraf: algoritmul BF şi
algoritmul DF. Aceşti algoritmi utilizează următoarele notaţii comune:
U mulţimea nodurilor nevizitate;
V mulţimea nodurilor vizitate şi neanalizate;
W mulţimea nodurilor vizitate şi analizate;
p tabloul predecesor, care este unidimensional cu n elemente.

4.1. Parcurgerea BF a grafurilor


Parcurgerea se face "mai întâi în lăţime". În engleză "breadth first" (BF).
Fie digraful G = (N,A) cu nodul sursă s şi Dx mulţimea drumurilor de la nodul sursa s la
nodul x �N. Numărul de arce ce compun un drum Dx �Dx defineşte lungimea acestui drum pe
care îl notăm l(Dx). Distanţa de la nodul s la nodul x se defineşte în modul următor:

�min{l(D x ) D x ι Dx }, Dx 0/
d(x) = �
� �, Dx = 0/
Un drum D̂ x �Dx cu l( D̂ x ) = d(x) se numeşte cel mai scurt drum de la nodul sursă s la
nodul x.
Observaţia 1. Pentru orice arc (x,y) �A avem
d(y) �d(x) + 1.
Într-adevăr, dacă Dx = 0/ atunci d(x) = � şi inegalitatea se păstrează. Dacă Dx � 0/ atunci
evident Dy � 0/ . Fie D̂ x un cel mai scurt drum de la s la x, deci l( D̂ x ) = d(x). Mulţimea Dy poate
conţine un drum Dy, astfel încât l(Dy) < d(x) + 1. Conform definiţiei distanţei avem d(y) �l(Dy)
şi rezultă că d(y) < d(x) + 1. Avem egalitate când un drum cel mai scurt D̂ y de la s la y are
lungimea
d(y) = l( D̂ y ) = d(x) + 1,
de exemplu
D̂ y = D̂ x �{(x,y)}.
În algoritmul parcurgerii BF (algoritmul PBF) se utilizează tabloul lungime l care este
unidimensional şi are n elemente. Mulţimea nodurilor vizitate şi neanalizate V este organizată, ca
structură de date, ca o coadă.
Algoritmul PBF este următorul:

26
(1) PROGRAM PBF;
(2) BEGIN;
(3) U := N - {s} ; V := {s} ; W := 0/ ;
(4) FOR toţi y �N DO p(y) := 0;
(5) l(s) := 0;
(6) FOR toţi y �U DO l(y) := �;
(7) WHILE V �0/ DO
(8) BEGIN
(9) se selectează cel mai vechi nod x introdus In V;
(10) FOR (x, y) � A DO
(11) IF y � U
(12) THEN U:=U–{y}; V:=V �{y}; p(y):=x:
l(y) := l(x) + 1;
(13) V:=V-{x}; W:=W �{x};
(14) END;
(15) END.

Teorema 1 .
(1) Algoritmul PBF calculează elementele tabloului l astfel încât d(y) �l(y), y �N ;
(2) Dacă la iteraţia k oarecare a algoritmului PBF avem V = (x 1, ..., xr) în această ordine,
atunci l(x r ) �l(x1 )  1 şi l(x i ) �l(x i 1 ), i = 1, r - 1 .

Demonstraţie.
(1) Utilizăm inducţia după k, numărul de iteraţii ale ciclului WHILE. Iniţial l(s) := 0, l(y) :=
� pentru y �U şi evident d(y) �l(y), y �N . Presupunem că, la iteraţia k avem d(y) �l(y)
pentru y �N . La iteraţia k + 1 pot exista cazurile:
(Cl ) Există arc (x,y) admisibil ((x,y) �A şi y�U). În acest caz
l(y) = l(x) + 1
şi
d(y) �d(x)  1 �l(x)  1 = l(y)
(l(x) pentru x�V nu se modifică). Deci pentru toate arcele (x,y) �A şi y �U avem d(y) �l(y) .
Pentru celelalte noduri y, conform ipotezei inducţiei, avem d(y) �l(y) , deoarece la iteraţia k
+ 1, l(y) nu se mai modifică.
(C2) Nu există arc (x,y) admisibil ((x,y) �A sau y �U). În acest caz la iteraţia k + 1 nu se
modifică nici un element l(y) şi conform ipotezei inducţiei d(y) �l(y) pentru y �N .
(2) Utilizăm inducţia după k numărul de iteraţii ale ciclului WHILE. Iniţial V := {s}. Deci x1
= a, xr = a şi l(s) < l(s) + 1, l(s) = l(s). Presupunem că la iteraţia k avem l(xr) �l(x1) + 1 şi l(xi) <
l(xi+1), pentru i = 1, r - 1 . La iteraţia k + 1 pot exista cazurile:
(Cl ) Există arc (x,y) admisibil ((x,y) �A şi y�U). În acest caz V={x 1, ..., xr, xr+1}, x1
= x, xr+1 = y. Astfel, l(xr+1) = l(y) = l(x) + 1 = l(x 1)+1. De asemenea, avem l(xr) �l(xr) + 1 = l(x) +
1 = l(y) = l(xr+1) şi inegalităţile l(xi) �l(xi+1), i = 1, r - 1 au rămas nemodificate.
(C2) Nu există arc (x,y) admisibil ((x,y) �A sau y �U). În acest caz V = {x2, ..., xr}. Avem
l(xr) �l(x1) + 1 �l(x2) + 1 şi inegalităţile l(xi) �l(xi+1), i = 1, r - 1 au rămas nemodificate.
Teorema 2. Algoritmul PBF este convergent şi la terminarea execuţiei deter-
minăm mulţimea tuturor nodurilor care sunt accesibile din nodul sursă s în digraful G =
(N,A).
Demonstraţie. Din liniile (10), (11) şi (12) ale algoritmului rezultă că toate
nodurile introduse în V sunt eliminate după ce sunt analizate. Deci după un număr finit de
iteraţii se obţine V = 0/ şi execuţia algoritmului se opreşte. Pentru a arăta că la terminarea

27
execuţiei, algoritmul determină mulţimea tuturor nodurilor care sunt accesibile din nodul
sursă s în digraful G = (N,A), trebuie să arătăm că la terminarea execuţiei algoritmului
mulţimea W este:
W = {y y �N şi există drum de la s la y}.
Din liniile (10), (11) şi (12) ale algoritmului rezultă că în V sunt introduse numai noduri y
care sunt accesibile din s şi că după ce un nod x �V a fost analizat el este eliminat din V şi
introdus în W. Deoarece algoritmul se opreşte când V = 0/ rezultă că W conţine toate
nodurile y �N care sunt accesibile din s şi introduse în V. Să arătăm că W conţine toate
nodurile y �N care sunt accesibile din s. Prin reducere la absurd să presupunem că există
un drum D = (yl,y2, ... ,yk-l,yk) cu yi = s, yk = y în G şi y �W. Rezultă că yk �V. Deoarece yk �V
şi (yk-h,yk) �A deducem că yk-1�V, astfel yk ar fi fost introdus în V. Continuând procedeul
vom deduce în final că s = y1 �V. Aceasta contrazice faptul că în linia (3) a algoritmului
iniţializăm V:={s}. Rezulta că y �V, deci y �W şi teorema este demonstrată.
Teorema 3.
Algoritmul PBF este convergent şi determină:
(1) mulţimea tuturor nodurilor care sunt accesibile din nodul sursă s;
(2) elementele tabloului l astfel încât l(y) = d(y) pentru y �N.
Demonstraţie. Convergenţa şi punctul (1) se demonstrează la fel ca Teorema 2.
Punctul (2) se demonstrează prin inducţie după k numărul iteraţiilor ciclului WHILE. Fie
mulţimea Nk = {y �N |d(y) = k}. Pentru k = 0 avem N0 = {s} şi deci d(s) = l(s) = 0.
Presupunem afirmaţia adevărată pentru k. Afirmaţia pentru k + 1 rezultă cu uşurinţă,
deoarece, în conformitate cu Teorema 1 punctul (2), un nod y �Nk+l este vizitat plecând de la un
nod x �Nk numai după ce toate nodurile din Nk sunt vizitate. Deci, dacă y �Nk+l şi este vizitat
explorând arcul (x,y), x �Nk, atunci,
l(y) = l(x) + 1 = d(x) + 1 = k + 1 = d(y).
Teorema 4.
Algoritmul PBF are complexitatea O(m).
Demonstraţie. Din liniile (10) , (11) şi (12) ale algoritmului rezultă că fiecare nod al
digrafului G este introdus şi eliminat din V cel mult o dată. Deoarece execuţia algoritmului se
termină când V = 0/ deducem că algoritmul execută cel mult 2n iteraţii. Fiecare arc (x,y) �A este
explorat cel mult o dată pentru identificarea arcelor admisibile. Deci complexitatea algoritmului
PBF este O(m + n) = O(m).
În parcurgerea BF, dacă Np = W şi subgraful predecesor Gp = (Np,Ap) este o
arborescenţă atunci Gp se numeşte arborescenţă parcurgere BPF.
Teorema 5.
Algoritmul PBF determină elementele tabloului p astfel încât subgraful predecesor
Gp = (Np,Ap) este o arborescenţă parcurgere BPF.
Demonstraţie. Din liniile (10), (11) şi (12) ale algoritmului rezultă că p(y) := x numai
dacă y este accesibil din s. Evident că la terminarea execuţiei algoritmului avem Np = W. Din
modul cum sunt definite Np şi Ap rezultă că Gp este o arborescenţă. Deci subgraful predecesor Gp
este o arborescenţă parcurgere a digrafului G = (N,A).
Observaţia 2. Drumul unic de la nodul sursă s la un nod y din arborescenţa parcurgere
BF este un cel mai scurt drum de la nodul sursă s la acelaşi nod y din digraful G, conform
punctului (2) al Teoremei 3.
Observaţia 3. Algoritmul PBF sau PTBF se poate aplica şi grafurilor neorientate. În
acest caz, subgraful predecesor Gp = (Np,Ap) este o arborescenţă. sau mai multe
arborescenţe.
Observaţia 4. Un drum unic de la nodul sursă s la un nod y din arborescenta

28
parcurgere BF se poate determina cu următoarea procedură.
(1) PROCEDURA DRUM (G,s,y);
(2) BEGIN;
(3) se tipăreşte u;
(4) WHILE p (y)≠i-0 DO
(5) BEGIN
(6) x := p(y); se tipăreşte x;
(7) IF x≠s
(8) THEN y := x
(9) ELSE EXIT;
(10) END;
(11) END.

Observaţia 5. Mulţimea W este în general submulţimea mulţimii nodurilor N. Pentru


parcurgerea întregului digraf G = (N,A) se utilizează algoritmul parcurgerii totale generice
(algoritmul PTG). Algoritmul PTG este următorul:
(1) PROGRAM PTG;
(2) BEGIN;
(3) U := N - {s}; V :== {s}; W := 0/ ;
(4) FOR toţi y �N DO p(y) := 0;
(5) k:= 1; o(s):= 1;
(6) FOR toţi y �U DO o(y) := �;
(7) WHILE W≠N DO
(8) BEGIN
(9) WHILE V ≠ 0/ DO
(10) BEGIN
(11) se selectează un nod x din V;
(12) IF există arc (x, y) �A şi y �U
(13) THEN U:=U-{y}; V:=V �{y};p(y):=x;
k := k + 1; o(y) :=k
(14) ELSE V:=V-{x}; W:=W �{x};
(15) END;
(16) se selectează s �U; U:= U - {s}; V:= {s};
k := k + 1: o(S) := k;
(17) END;
(18) END.

Este evident că algoritmul PTG are complexitatea tot O(m) şi că vectorul p determină una
sau mai multe arborescente parcurgeri.
Exemplu 1.
Se aplică algoritmul PBF digrafului din figura 1 .

Figura 1

Iniţializări:
s = 1, U = {2,3,4,5,6},
V = {1}, W = 0/ , p = (0,0,0,0,0,0), l = (0, �, �, �, �, �).
Iteraţia 1:

29
x = 1, (1,2) �A, 2 �U: U = {3,4,5,6},
V = {1,2}, p = (0,1,0,0,0,0) ,
l = (0,1, �, �, �, �); (1,3) �A, 3 �U: U = {4,5,6},
V = {1,2,3}, p= (0,1,1,0,0,0),
l = (0,1, 1, �, �, �); V = {2, 3}; W = {1}.
Iteraţia 2:
x = 2, (2,4) �A, 4 �U: U = {5,6}, V = {2,3, 4},
p = (0,1,1,2,0,0), l = (0,1,1,2, �, �); (2,5) �A,
5 �U : U = {6}, V = {2,3,4,5} , p = (0,1,1,2,2,0) ,
l = (0,1,1,2,2, �) ; V = {3, 4, 5} ; W = {1, 2}.
Iteraţia 3:
x = 3, V = {4, 5}, W = {1, 2, 3}.
Iteraţia 4:
x = 4, (4, 6) �A, 6 �U: U = 0/ , V = {4, 5,6},
p = (0,1,1,2,2,4), l = (0,1,1,2,2,3); V = {5,6};
W = {1,2,3,4}.
Iteraţia 5:
x =5, V={6}, W = {1,2,3,4,5}.
Iteraţia 6:
x = 6, V = 0/ , W = {1,2,3,4,5,6}.
Np = {1,2,3,4,5,6} = W.

Arborescenţa parcurgere BF este prezentată în figura 2.

Figura 2

Drumul unic de la nodul 1 la nodul 6 se obţine cu PROCEDURA DRUM în modul


următor:
y = 6 este ultimul nod al drumului;
Iteraţia 1: x = p(6) = 4, y = 4.
Iteraţia 2: x = p(4) = 2, y = 2.
Iteraţia 3: x = p(2) = 1, y = l.
Drumul este: 1,2,4,6.

4.2. Parcurgerea DF a grafurilor


Parcurgerea se face "mai întâi în adâncime". În engleză "depth first" (DF).
În algoritmul parcurgerii DF (algoritmul PDF) se folosesc aceleaşi notaţii ea în
algoritmul PBF cu deosebirea că, în locul tabloului unidimensional de lungime l se utilizează
tablourile timp unidimensionale t1 şi t2 care au fiecare n elemente. Mulţimea nodurilor vizitate şi
neanalizate V este organizată ca structură de date, ca stivă.
30
Algoritmul PDF este următorul:
(1) PROGRAM PDF;
(2) BEGIN;
(3) U := N-{s}; V := {s}; W := 0/ ;
(4) FOR toţi y �N DO p(y) := 0;
(5) t := 1; t1(s) := 1; t2(s) := �;
(6) FOR toţi y �U DO t1(y) := �; t2(y) := �;
(7) WHILE V �0/ DO
(8) BEGIN
(9) se selectează cel mai nou nod x introdus in V;
(10) IF există arc (x,y) �A şi y �U
(11) THEN U := U - {y}; V := V �{y}; p (y) := x;
t := t + 1; t1(y) := t
(12) ELSE V := V-{x}; W := W �{x}; t:=t+1;t2(x):=t;
(13) END;
(14) END.

Din liniile (10), (11), (12) ale algoritmului PDF rezultă că elementul t1(y) reprezintă
momentul când y devine nod vizitat şi neanalizat şi elementul t2(x) reprezintă momentul când x
devine nod vizitat şi analizat.
Se va studia în continuare algoritmul parcurgerii totale DF (algoritmul PTDF).
Algoritmul PTDF este următorul:
(1) PROGRAM PTDF;
(2) BEGIN;
(3) U:=N-{s}; V:={s}; W:= 0/ ;
(4) FOR toţi y �N DO p(y) := 0;
(5) t:=1; t1(s):= l; t2(s):= �;
(6) FOR toţi y �U DO t1(y) := �; t2(y) := �;
(7) WHILE W≠N DO
(8) BEGIN
(9) WHILE V≠ 0/ DO
(10) BEGIN
(11) se selectează cel mai nou nod x introdus în V;
(12) IF există arc (x,y) �A şi y �U
(13) THEN U := U - {y}; V := V �{y};
p (y) := x; t := t + 1; t1(y) := t;
(14) ELSE V:= V - {x}; W:= W �{x};
t:= t + 1; t2 (x) := t;
(15) END;
(16) se selectează s �U; U := U - {s}; V := {s};
t:=t+l; t1(s):=t;
(17) END;
(18) END.

Fie mulţimea S = {s s �N , s selectate în linia (3) şi linia (16)}.


Teorema 6.
Algoritmul PTDF este convergent şi determină mulţimile nodurilor accesibile din s, s�S.
Demonstraţie. Se demonstrează la fel ca Teorema 2.
Teorema 7.
Algoritmul PTDF are complexitatea O(m).
Demonstraţie. Evident că. algoritmul PTDF are complexitatea O(m).
Un digraf G ˆ = (N, ˆ se numeşte pădure dacă este format din una sau mai multe
ˆ A)
ˆ = (N,
arborescenţe. Un graf neorientat G ˆ se numeşte pădure dacă este format din unul sau
ˆ A)
mai mulţi arbori.

31
În parcurgerea totală DF, dacă subgraful predecesor Gp = (Np,Ap),
N p = {y p(y) �0} �S, A p = {(p(y), y) y �N p - S} este o pădure şi Np = W, atunci
Gp se numeşte pădure parcurgere DF.
Teorema 8.
Algoritmul PTDF determină elementele tabloului p astfel încât subgraful
predecesor Gp = (Np,Ap) este o pădure parcurgere DF.
Demonstraţie. Se demonstrează analog ca Teorema 5.
Exemplul 2.
Se aplică algoritmul PTDF digrafului

Figura 3

Iniţializări:
s = 1,U = {2,3,4,5,6, ,8}, V = {1}, W = 0/ , p
p = (0,0,0,0,0,0,0,0), t = 1,
t1 = (1, �, �, �, �, �, �, �),
t2 = ( �, �, �, �, �, �, �, �).
Iteraţia 1:
x = 1, (1,2) �A, 2 �U: U = {3,4,5,6,7,8}, V = {1,2},
p = (0,1,0,0,0,0,0,0), t = 2,
tl = (1,2, �, �, �, �, �, �, �).
Iteraţia 2:
x = 2, (2,3) �A,3 �U: U = {4,5,6,7,8}, V = {1,2,3},
p = (0,1,2,0,0,0,0,0), t = 3,
t1 = (1, 2, 3, �, �, �, �, �).
Iteraţia 3:
x = 3, (3,4) �A,4 �U: U = {5,6,7,8}, V = {1,2,3,4},
p = (0,1,2,3,0,0,0,0), t = 4, t1 = (1,2,3,4, �, �, �, �).
Iteraţia 4:
x = 4: V = {1,2, 3}, W = {4}, t = 5,
t2 = ( �, �, �,5, �, �, �, �).
Iteraţia 5:
x = 3: V = {1,2}, W = {4,3}, t = 6,
t2 = ( �, �,6,5, �, �, �, �).
Iteraţia 6:
x = 2, (2,5) �A, 5 �U: U = {6,7,8},
V = {1,2,5}, p = (0,1,2,3,2,0,0,0), t = 7,
tl = (1,2,3,4,7, �, �, �).
Iteraţia 7:
x = 5: V = {1,2}, W = {4,3,5}, t = 8,
t2 = ( �, �,6,5,8, �, �, �).
Iteraţia 8:
x = 2 : V = {1}, W = {4,3,5,2}, t = 9,
t2 = ( �,9,6,5,8, �, �, �).

32
Iteraţia 9:
x = 1: V = 0, W = {4,3,5,2,l}, t = 10,
t2 = (10,9,6,5,8, �, �, �).
Actualizări:
s = 6, U = {7,8}, V = {6}, t = 11, t1 = (1,2,3,4,7,11, �, �)
Iteraţia 10:
x = 6, (6, 7) �A, 7 �U : U = {8}, V = {6, 7},
p = (0,1,2,3,2,0,6,0), t = 12,
t1 = (1,2,3,4,7,11,12, �).
Iteraţia 11:
x = 7 : V = {6}, W = {4, 3,5,2,1,7}, t = 13,
t2 = (10,9,6,5,8,00,13, �).
Iteraţia 12:
x = 6, (6,8) �A,8 �U: U = 0/ , V = {6,8}, p = (0,1,2,3,2,0,6,6),
t = 14, tl = (1,2,3,4,7,11,12,14).
Iteraţia 13:
x = 8 : V = {6}, W = {4, 3, 5, 2,1, 7,8}, t = 15,
t2 = (10,9,6,5,8, �,13,15).
Iteraţia 14:
x = 6: V = 0/ , W = {4,3,5,2,1,7,8,6}, t = 16,
t2 = (10,9,6,5,8,16,13,15).

4.3. Aplicaţii
4.3.1. Sortarea topologică
Teorema 9. Un digraf G = (N,A) este fără circuite dacă şi numai dacă orice parcurgere
totală DF a lui G nu produce arce de revenire.
Demonstraţie. Presupunem că digraful G este fără circuite. Prin reducere la absurd
presupunem ca o parcurgere totală DF a lui G produce arce de revenire ( R �0/ ). Fie arcul
(z, x) �R . În acest caz nodul z este un descendent al nodului x în pădurea parcurgere DF. Deci
o
există un drum D de la x la z în G. Atunci D = D �{z,x} este un circuit în G şi aceasta
contrazice ipoteza că digraful G este fără circuite.
Reciproc, presupunem că o parcurgere totală DF a digrafului G nu produce arce de
o
revenire ( R �0/ ). Prin reducere la absurd presupunem că G conţine un circuit D . Fie x
o o o
primul nod vizitat din D şi fie arcul (z,x) �D . Deoarece nodurile x, z �D rezultă că există
o
un drum D de la x la z. De asemenea x fiind primul nod vizitat din D rezultă că nodul z
devine un descendent al nodului x la pădurea PDF. Deci (z,x) este un arc de revenire ce
contrazice ipoteza R = 0/ .
Sortarea topologică a unui digraf G = (N,A) fără circuite constă într-o ordonare
liniară a nodurilor din N astfel încât dacă (x, y) �A atunci x apare înaintea lui y în ordonare.
Algoritmul sortare topologică (algoritmul ST) se obţine din algoritmul PTDF făcând
următoarele două completări:
(1) în partea de iniţializări (liniile (3)-(6)) se iniţializează o listă a nodurilor;
(2) în linia (16) după calculul lui t2(X), nodul x se introduce la începutul listei.
La terminarea algoritmului ST, lista furnizează sortarea topologică a digrafului G =
(N,A) fără circuite şi nodurile x sunt plasate în listă în ordinea descrescătoare a timpilor t2(X).

33
4.3.2. Componentele conexe ale unui graf
Definiţia 1. Un digraf G = (N,A) se numeşte conex dacă pentru oricare două noduri x,
y există un lanţ care are aceste două noduri drept extremităţi.
Noţiunea de conexitate are sens şi pentru grafuri neorientate.
Definiţia 2. Se numeşte componentă conexă a unui digraf G = (N,A) un subgraf
G' = (N',A') al lui G, care este conex şi care este maximal în raport cu incluziunea faţă de această
proprietate (oricare ar fi x �N ' = N - N ' , subgraful G 'x generat de N 'x = N '�{x} nu mai este
conex).
O componentă conexă G' = (N',A') a unui digraf G = (N,A) se poate identifica cu
mulţimea N' care generează subgraful G'.
Deoarece în problema conexiunii sensul arcelor nu contează se va considera că grafurile
sunt neorientate. Dacă G = (N,A) este digraf atunci i se asociază graful neorientat G ˆ = (N, ˆ ,
ˆ A)
unde N̂ = N, A ˆ = {[x, y] (x, y) �A şi / sau (y, x) �A }. Este evident că G şi Ĝ au aceleaşi
componente conexe.
Algoritmul componentelor conexe (algoritmul CC) este o adaptare a algoritmului PTDF
aplicat unui graf neorientat G = (N,A). Nu se calculează tablourile timp t1, t2 şi prin p notăm o
variabilă scalară a cărei valoare reprezintă numărul componentelor conexe. Algoritmul CC este
următorul:
(1) PROGRAM CC;
(2) BEGIN
(3) U:= N - {s}; V:= {s}; W:= 0/ ; p:= l; N' = {s};
(4) WHILE W ≠ N DO
(5) BEGIN
(6) WHILE V≠ 0/ DO
(7) BEGIN
(8) se selectează cel mai nou nod x introdus în V;
(9) IF există muchie [x, y] �A şi y �V
(10) THEN U:= U-{y}; V:=V �{y};N':=N' �{y}
(11) ELSE V:= V-{x}; W:=W �{x};
(12) END;
(13) se tipăresc p şi N';
(14) se selectează s �U; U:=U-{s}; V:={s};
p:= p+1;N':={s};
(15) END;
(16) END.

La terminarea algoritmului pot exista cazurile:


(Cl) se tipăreşte o singură componentă conexă şi în acest caz graful G = (N,A) este conex;
(C2) se tipăresc mai multe componente conexe şi în acest caz graful G = (N,A) nu este
conex, obţinându-se toate componentele conexe ale lui G.
Teorema 10.
Algoritmul CC determină componentele conexe ale unui graf neorientat G = (N,A).
Demonstraţie.
La terminarea execuţiei ciclului WHILE se determină mulţimea N' a tuturor nodurilor
accesibile printr-un lanţ cu aceeaşi extremitate, nodul s. Mulţimea N' generează evident o
componentă conexă G' = (N',A').
Deoarece la terminarea execuţiei algoritmului avem W = N rezultă că algoritmul CC
determină toate componentele conexe ale lui G = (N,A).
Teorema 11.
Algoritmul CC are complexitatea O(m).
Demonstraţie. Algoritmul CC are aceeaşi complexitate cu a algoritmului PTDF, adică
34
O(m).
Exemplul 3.
Fie digraful din figura 4.
Pentru a determina componentele conexe ale acestui digraf se transformă într-un graf
neorientat reprezentat în figura 5 căruia i se aplică algoritmul CC.

Figura 4

Figura 5

Iniţializări:
s = 1, U = {2,3,4,5,6,7,8}, V = {1}, W = 0/ , p = 1, N'={1}.
Iteraţia 1:
x = 1, [1,2] �A, 2 �U : U = {3,4,5,6,7,8}, V = {1,2}, N'={1,2}.
Iteraţia 2:
x = 2, [2,3] �A, 3 �U: U={4,5,6,7,8}, V={1,2,3}, N'={1,2,3}.
Iteraţia 3:
x=3, [3,4] �A, 4 �U: U={5,6,7,8}, V={1,2,3,4}, N'={1,2,3,4}.
Iteraţia 4:
x = 4: V = {1,2,3}, W = {4}.
Iteraţia 5:
x = 3: V = {1,2}, W = {4,3}.
Iteraţia 6:
x = 2, V = {1}, W = {4,3,2}.
Iteraţia 7:
x = 1 : V = 0/ , W = {4, 3, 2, 1}.
Se tipăresc:
p = 1 şi N' = {1,2,3,4}
Actualizări:
s = 5, U = {6,7,8}, V = {5}, p = 2, N' = {5}.
Iteraţia 8:
x = 5, [5,6] �A, 6 �U : U = {7,8}, V = {5,6}, N' = {5,6}.
Iteraţia 9:
x = 6, [6,8] �A, 8 �U : U = {7}, V = {5,6,8}, N' = {5,6,8}.
Iteraţia 10:
x = 6, [8, 7] �A, 7 �U: U = 0/ , V = {5,6,8,7}, N'={5,6,8,7}.

Iteraţia 11:
x = 7 : V = {5,6,8}, W = {4,3,2,1,7}.

35
După încă trei iteraţii se obţine
V = 0/ , W = {4,3,2,1,7,8,6,5}
Se tipăresc
p = 2 şi N' = {5,6,8,7}
şi execuţia algoritmului se opreşte.

Discutii finale:
Sa se aplice parcurgerea BF a grafurilor, parcurgerea DF a grafurilor, sa se determine
componentele conexe ale unui graf dat.
Tema propusa:
Sa se proiecteze programe intr-un limbaj de programare pentru realizarea parcurgerii BF a
grafurilor, parcurgerii DF a grafurilor, determinarii componentelor conexe.

Capitolul 5
PROBLEME DE DRUM ÎN (DI)GRAFURI

Cuvinte cheie
Drum minim, conexiune, drum si ciclu Hamiltonian.
Obiectivele invatarii
Dupa parcurgerea acestui capitol va trebui:
Sa se cunoasca problema celui mai scurt drum, algoritmul lui Dijkstra, algoritmul Bellman-Ford,
algoritmul Floyd-Warshall, probleme de conexiune, teorema lui Menger, problema drumului si
ciclului Hamiltonian, algoritmul lui Prim, algoritmul lui Kruskal.

5.1. Problema celui mai scurt drum


În teoria grafurilor, problema celui mai scurt drum constă în găsirea unui drum astfel
încât suma “costurilor” muchiilor constituente să fie minimă. Un exemplu îl constituie găsirea

36
celei mai rapide modalităţi de a trece de la o locaţie la alta pe o hartă; în acest caz nodurile sunt
reprezentate de către locaţiile respective, iar muchiile reprezintă segmentele de drum, şi sunt
ponderate, costurile constituind timpul necesar parcurgerii acelui segment.
Formal, fiind dat un graf ponderat (adică, o mulţime de vârfuri V, o mulţime a muchiilor
E, şi o funcţie de cost
f :E R
cu valori reale) şi un element v al lui V, să se găsească un drum P de la v la fiecare v  din V
astfel încât
 f ( p)
pP

să fie minim între toate drumurile ce leagă v de v  .


Uneori mai poate fi recunoscută sub numele de problema drumului cel mai scurt
corespunzător perechii singulare, cu scopul deosebirii acesteia de următoarele generalizări:
 problema drumului cel mai scurt corespunzător sursei unice, o problemă mai
generală, în care trebuie să găsim cele mai scurte drumuri de la un nod sursă v la
toate celelalte noduri ale grafului.
 problema drumului cel mai scurt corespunzător tuturor perechilor reprezintă o
problemă şi mai generală, în care trebuie să găsim cele mai scurte drumuri între
oricare pereche de noduri (vârfuri) v, v  din graf.
Ambele generalizări amintite au algoritmi mai performanţi în practică decât simpla rulare
a algoritmului corespunzător drumului cel mai scurt în cazul perechii-unice (singulare) pentru
toate perechile relevante de vârfuri.

Algoritmi
Cei mai importanţi algoritmi care rezolvă această problemă sunt:
 Algoritmul lui Dijkstra – rezolvă problema sursei unice, dacă toate muchiile sunt
ponderate pozitiv Acest algoritm poate genera cele mai scurte drumuri de la un
anumit punct de placare s la toate celelalte noduri.
 Algoritmul Bellman-Ford – rezolvă problema sursei unice şi pentru costuri
negative ale muchiilor.
 Algoritmul de căutare A* - rezolvă problema drumurilor cele mai scurte în cazul
sursei unice, folosind euristica, în încercarea accelerării căutării.
 Algoritmul Floyd-Warshall – rezolvă problema celor mai scurte drumuri
corespunzătoare tuturor perechilor.
 Algoritmul lui Johnson - rezolvă problema celor mai scurte drumuri
corespunzătoare tuturor perechilor; poate fi mai rapid ca Algoritmul Floyd-
Warshall, în cazul grafurilor rare.

Aplicaţii
Algoritmii ce rezolvă problema celui mai scurt drum se aplică, în mod evident, pentru a
găsi, în mod automat, adrese între diferite locaţii fizice, cum ar fi spre exemplu instrucţiuni legate
de şofat oferite de GPS – uri sau programele web de mapare (Mapquest).
Dacă reprezentăm, spre exemplu, o maşină abstractă nedeterministă sub forma unui graf,
în care vârfurile descriu state, iar muchiile descriu posibile tranziţii, algoritmii de identificare a
celui mai scurt drum pot fi folosiţi pentru a găsi o secvenţă optimală de alegeri, astfel încât să
ajungă într-un stat prestabilit, sau pentru a minimiza timpul necesar pentru a ajunge în acel stat.

37
5.1.1. Arborele Steiner

Soluţia pentru 3 puncte; punctul Steiner este cel din mijloc – a se remarca faptul
că nu există conexiuni directe între A, B, C

Soluţia pentru 4 puncte – a se remarca faptul că există 2 puncte Steiner

Problema Arborelui Steiner este, aproximativ, similară problemei arborelui parţial de


cost minim: fiind dată o mulţime V de vârfuri, interconectaţi aceste puncte prin intermediul unui
graf de lungime minimă, unde lungimea reprezintă suma lungimilor tuturor muchiilor. Diferenţa
între Problema Arborelui Steiner şi Problema Arborelui Parţial de Cost Minim constă în faptul că
în cadrul Arborelui Steiner pot fi adăugate grafului iniţial vârfuri şi muchii intermediare, cu

38
scopul reducerii lungimi arborelui parţial. Aceste vârfuri nou introduse, în scopul reducerii
lungimii totale a conexiunii, sunt cunoscute sub numele de Puncte Steiner sau Vârfuri Steiner.
S-a demonstrat că acea conexiune rezultantă este un arbore, numit şi Arborele Steiner. Pot exista
mai mulţi arbori Steiner pentru o mulţime dată de vârfuri iniţiale.
Problema originală a fost formulată în forma cunoscută sub numele de Problema
Arborelui Euclidean Steiner: Fiind date N puncte în plan, se cere să se conecteze prin
intermediul liniilor, valoarea rezultantă a acestora fiind minimă, astfel încât oricare două puncte
sunt interconectate, fie printr-un segment de linie, fie via alte puncte, respectiv alte segmente de
dreaptă.
Pentru Problema Euclidean Steiner, punctele adăugate grafului (Punctele Steiner) trebuie
să aibă gradul trei, iar cele trei muchii incidente corespunzătoare trebuie să formeze trei unghiuri
de 120 de grade. Rezultă că numărul maxim de Puncte Steiner pe care le poate avea un Arbore
Steiner este de N-2, unde N reprezintă numărul iniţial de puncte considerate.
Se poate încă generaliza până la Problema Metrică a Arborelui Steiner. Fiind dat un
graf ponderat G(S,E,w) ale cărui vârfuri corespund unor puncte în spaţiul metric, iar „costul”
muchiilor este reprezentat de distanţele în spaţiu, se cere să se găsească un arbore de lungime
totală minimă, ai cărui vârfuri constituie o supermulţime a mulţimii S, mulţime a vârfurilor
grafului G.
Versiunea cea mai generală o constituie Arborele Steiner în grafuri: Fiind dat un graf
ponderat G(V,E,w) şi o submulţime de vârfuri S  V găsiţi un arbore de cost minim care include
toate nodurile mulţimii S.
Problema Metrică a Arborelui Steiner corespunde problemei Arborelui Steiner în grafuri,
unde graful are un număr infinit de noduri, toate fiind puncte în spaţiul metric.
Problema arborelui Steiner are aplicaţii în design-ului reţelelor. Majoritatea versiunilor
Problemei Arborelui Steiner sunt NP – complete, i.e., gândite ca fiind computaţional-dificile. În
realitate, una dintre acestea se număra printre cele 21 de probleme iniţiale ale lui Karp, NP –
complete. Unele cazuri restrictive pot fi rezolvate într-un timp polinomial. În practică se folosesc
algoritmii euristici.
O aproximare comună a Problemei Arborelui Euclidian Steiner este reprezentată de
calcularea arborelui parţial de cost minim Euclidian.

5.1.2. Algoritmul lui Dijkstra


Algoritmul lui Dijkstra, după numele celui care l-a descoperit, expertul în calculatoare
Edsger Dijkstra, este un algoritm greedy care rezolvă problema celui mai scurt drum cu o singură
sursă pentru un graf orientat, care nu are muchii ponderate negativ.
Spre exemplu, dacă vârfurile grafului reprezintă oraşe, iar costurile muchiilor reprezintă
distanţele de parcurs între perechi de oraşe conectate printr-un drum direct, algoritmul lui Dijkstra
poate fi folosit pentru depistarea celui mai scurt traseu între cele două oraşe.
Datele de intrare necesare implementării algoritmului sunt: un graf orientat ponderat G şi
un vârf sursă s în G. Vom nota cu V mulţimea tuturor vârfurilor grafului G. Fiecare muchie a
grafului reprezintă o pereche ordonată de vârfuri (u, v), semnificaţia acesteia fiind legătura între u
şi v. Mulţimea tuturor muchiilor este notată cu E. Costurile muchiilor sunt date de funcţia de cost
w : E � [0, �) ; astfel, w(u , v) reprezintă costul muchiei (u,v). Costul unei muchii poate fi
închipuit ca o generalizare a distanţei între aceste două vârfuri. Costul unui drum între două
vârfuri este dat de suma tuturor costurilor muchiilor componente. Pentru o pereche dată de vârfuri
s şi t din V, algoritmul găseşte drumul de cost minim între s şi t (i.e. cel mai scurt drum).
Algoritmul poate fi folosit, în aceeaşi măsură pentru depistarea drumurilor de cost minim între
vârful sursă s şi toate celelalte vârfuri ale grafului.

39
Descrierea algoritmului
Algoritmul funcţionează reţinând, pentru fiecare vârf v, costul d  v  al celui mai scurt
drum găsit până în acel moment între s şi v. Iniţial, această valoare este 0, pentru vârful sursă s (
d  s  = 0 ), respectiv infinit pentru restul vârfurilor, sugerând faptul că nu se cunoaşte nici un
drum către aceste noduri (vârfuri) ( d  v  =  pentru fiecare v din V, exceptând s). La finalul
algoritmului, d  v  va reprezenta costul celui mai scurt drum de la s la v – sau infinit, dacă nu
există un astfel de drum.
Algoritmul presupune existenţa a două mulţimi de vârfuri S şi Q. Mulţimea S conţine
toate vârfurile pentru care se cunoaşte valoarea d  v  , valoare ce corespunde costului celui mai
scurt drum, iar mulţimea Q conţine toate celelalte vârfuri . Mulţimea S este, iniţial,goală (nu are
elemente), iar cu fiecare pas un vârf din mulţimea Q devine element al mulţimii S. Acest vârf
este ales astfel încât d  v  să corespundă celei mai mici valori. Odată cu „mutarea” vârfului u în
mulţimea S, algoritmul „relaxează” fiecare muchie de forma (u,v). Aceasta înseamnă că, pentru
fiecare vecin al lui v sau al lui u, algoritmul verifică dacă poate optimiza drumul (la v) cunoscut
ca fiind cel mai scurt până la acel moment, urmând drumul cel mai scurt de la sursa s la u,
traversând în cele din urmă muchie (u, v). Dacă acest nou drum este mai bun (în sensul unui cost
mai mic), algoritmul actualizează d  v  , atribuindu-i valoarea mai mică.

Execuţia algoritmului Dijkstra asupra unui graf mic, demonstrând două operaţii de relaxare
Pe măsură ce se găsesc drumuri mai scurte, costul estimat este redus, iar sursa se
relaxează. Eventual, drumul cel mai scurt, dacă există, se relaxează la maximum.

Pseudocodul
În algoritmul ce urmează, u := extract_min(Q) caută vârful u în mulţimea vârfurilor
Q, care are cea mai mică valoare asociată dist[u]. Vârful este scos din mulţimea Q şi returnat
utilizatorului. length(u, v) calculează distanţa între cele două vârfuri vecine u şi v alt de pe
linia 10 reprezintă lungimea drumului de la rădăcină la v, dacă ar fi să treacă prin u. Dacă acest
drum este mai scurt decât drumul considerat în momentul respectiv ca fiind cel mai scurt, acel
drum curent este înlocuit cu acest alt drum.
1 function Dijkstra(Graph, source):
2 for each vârf v in Graph:
3 dist[v] := infinity
4 previous[v] := undefined
5 dist[source] := 0
6 Q := copy(Graph)
7 while Q is not empty:
8 u := extract_min(Q)

9 for each vecin v of u:


10 alt = dist[u] + length(u, v)
11 if alt < dist[v]
12 dist[v] := alt
13 previous[v] := u

Dacă, însă ne interesează doar un drum mai scurt între vârfurile sursă şi ţintă, căutarea
poate înceta la punctul 9 dacă u=target. Acum putem „citi” cel mai scurt drum de la sursă la ţintă
prin iterare:

40
1 S := empty sequence
2 u := target
3 while este definit previous[u]
4 inserează u la începutul of S
5 u := previous[u]

Acum secvenţa S reprezintă lista vârfurilor ce constituie unul dintre cele mai scurte
drumuri de la sursă la ţintă, sau secvenţa nulă dacă un astfel de drum nu există.
O problemă mult mai generală ar fi aceea a determinării tuturor celor mai scurte drumuri
între sursă şi ţintă (pot fi mai multe astfel de drumuri, de aceeaşi lungime). În acest caz, în locul
memorării unui singur nod la fiecare „intrare” previous[], se vor păstra toate vârfurile ce satisfac
condiţia de relaxare. Spre exemplu, dacă atât r cât şi sursa sunt conectate (sunt în legătură) cu
ţinta şi ambele aparţin unor celor mai scurte drumuri distincte, ce ating ţinta (deoarece costul
muchiilor este acelaşi în ambele cazuri), atunci vom adăuga ambele vârfuri – r şi sursă – valorii
anterioare [target]. Când algoritmul este complet, structura de date previous[] va descrie un graf,
care este subgraf al grafului iniţial din care au fost înlăturate unele muchii. Proprietatea esenţială
va fi dată de faptul că dacă algoritmul a rulat cu un anumit vârf de început, atunci fiecare drum de
la acel vârf către oricare alt vârf, în noul graf, va fi cel mai scurt între nodurile respective în
graful original, iar toate drumurile de aceiaşi lungime din garful original vor fi prezente în graful
rezultant. Astfel, pentru a găsi aceste drumuri scurte între oricare două vârfuri date vom folosi
algoritmul de găsire a drumului în noul graf, asemenea depth-first search (căutării în adâncime).
Timpul de rulare
Timpul de rulare al algoritmului lui Dijkstra într-un graf cu |E| muchii şi |V| noduri poate
fi exprimat ca o funcţie de E şi V , folosind notaţia O.
Cea mai simplă implementare a algoritmului lui Dijkstra stochează vârfurile mulţimii Q
într-o listă de legătură ordinară sau într-un tablou, iar operaţia Extract-Min(Q) este o simplă
căutare liniară a vârfurilor mulţimii Q. În acest caz, timpul de rulare este O( V 2  E ) .
Pentru cazul grafurilor rare, adică, grafuri cu un număr de muchii mult mai mic decât
2
V , algoritmul Dijkstra se poate implementa într-un mod mult mai eficient, prin stocarea
grafului sub forma listelor de adiacenţă şi folosirea heap binar sau heap Fibonaci pe post de coadă
cu priorităţi în implementarea funcţiei Extract-Min. Cu heap binar algoritmul necesită un timp de
rulare de ordinul O(( E  V ) log V ) (dominat de către O( E log V ) presupunând că
E  V - 1 ), iar heap Fibonaci îmbunătăţeşte acest timp la O( E  V log V ) .

5.1.3. Probleme similare şi algoritmi


Funcţionalitatea algoritmului original al lui Dijkstra poate fi extinsă dacă se efectuează
anumite schimbări. De exemplu, în unele cazuri este de dorit a se prezenta unele soluţii ce nu sunt
chiar optimale din punct de vedere matematic . Pentru a obţine o listă consistentă de astfel de
soluţii mai puţin optimale, se calculează, totuşi, încă de la început, soluţia optimă. Se elimină, din
graf, o singură muchie ce apare în soluţia optimă, iar soluţia optimă a acestui nou graf este
calculată. La întoarcere, fiecare muchie a soluţiei originale este suprasaturată, iar drept urmare se
calculează un nou cel mai scurt drum. Soluţiile secundare astfel obţinute sunt înşiruite imediat
după prima soluţie optimă.
OSPF (open shortest path first) reprezintă o implementare reală a algoritmului lui
Dijkstra, în rout-area internet-ului.
Spre deosebire de algoritmul lui Dijkstra, algoritmul Bellman-Ford poate fi folosit şi în
cazul grafurilor ce au muchii cu costuri negative, atât timp cât graful nu conţine nici un ciclu

41
negativ care se poate atinge din vârful sursă s. (Prezenţa unor astfel de cicluri sugerează faptul că
nu există ceea ce numim cel mai scurt drum, având în vedere că valoarea descreşte de fiecare dată
când ciclul este traversat.)
Algoritmul A* este o generalizare a algoritmului Dijkstra, care reduce mărimea
subgrafului care urmează să fie explorat, aceasta în cazul în care sunt disponibile informaţii
adiţionale, menite să micşoreze „distanţa” către ţintă.
Procesul care stă la baza algoritmului lui Dijkstra este similar procesului greedy, folosit în
cazul algoritmului lui Prim.
Scopul algoritmului lui Prim îl constituie găsirea arborelui parţial de cost minim corespunzător
unui graf.

5.1.4. Probleme legate de drum


■ Drumul Hamiltonian şi probleme legate de cicluri
■ Arborele parţial de cost minim
■ Problema inspecţiei drumului (cunoscută şi sub numele de „Problema Poştaşului
Chinez”)
■ Cele Şapte Poduri din Königsberg
■ Problema celui mai scurt drum
■ Arborele Steiner
■ Problema Comisului Voiajor (NP - completă)

5.1.5. Algoritmul Bellman-Ford


Algoritmul Bellman –Ford calculează cele mai scurte drumuri de la un vârf-sursă către
celelalte vârfuri ale unui digraf ponderat (unde unele muchii pot avea costuri negative).
Algoritmul lui Dijkstra rezolvă aceeaşi problemă, chiar cu un timp de execuţie mai mic, însă
necesită muchii ale căror costuri să fie nenegative. Astfel, algoritmul Bellman – Ford se foloseşte
doar atunci când există costuri negative ale muchiilor.
Potrivit lui Robert Sedgewick, „Valorile negative intervin în mod natural în momentul în
care se reduc alte probleme la probleme de studiu a drumului cel mai scurt”, şi oferă ca exemplu
specific problema reducerii complexităţii -NP a drumului Hamiltonian. Dacă un graf conţine un
ciclu având valoare negativă, atunci nu există soluţie; Bellman – Ford rezolvă acest caz.
Algoritmul Bellman – Ford, în structura sa de bază, este similar algoritmului Dijkstra, dar
în locul unei selecţii de tip greedy a nodului minim poderat, apelează la simpla relaxare a
muchiilor, acest proces executându-se de V - 1 ori, unde V reprezintă numărul vârfurilor
dintr-un graf. Aceste repetări permit propagarea distanţelor minime în graf, ţinând cont de faptul
că, în absenţa ciclurilor negative, cel mai scurt drum poate vizita fiecare nod cel mult o dată. Spre
deosebire de abordarea greedy, care depinde de anumite consideraţii structurale derivate din
costurile pozitive, această abordare directă se extinde la cazul general.
Timpul de rulare al algoritmului Bellman – Ford este de ordinul O(|E|).
procedure BellmanFord(list vertices, list edges, vertex source)
// Pasul 1: Iniţializarea grafului
for each vertex v in vertices:
if v is source then v.distance := 0
else v.distance := infinity
v.predecessor := null
// Pasul 2: Relaxarea repetitivă a muchiilor
for i from 1 to size(vertices):
for each edge uv in edges:
u := uv.source
v := uv.destination //uv este muchia de la u la v
if v.distance > u.distance + uv.weight:

42
v.distance := u.distance + uv.weight
v.predecessor := u
// Depistarea ciclurilor negative
for each edge uv in edges:
u := uv.source
v := uv.destination
if v.distance > u.distance + uv.weight:
error "Graful cinţine un ciclu negativ"

Demonstraţia corectitudinii
Corectitudinea algoritmului poate fi arătată cu ajutorul inducţiei. Propoziţia care va fi
demonstrată prin inducţie este dată de următoarea:
Lemă.
După i repetiţii ale buclei for:
• Dacă Distance(u) nu este infinită, atunci este egală cu lungimea unui anumit drum de la s
la u;
• Dacă există un drum de la s la u cu cel mult i muchii, atunci Distance(u) corespunde cel
mult lungimii celui mai scurt drum de la s la u cu cel mult i muchii.
Demonstraţie.
Pentru etapa I, considerăm i = 0 şi momentul apriori ciclului for considerându-l ca fiind
executat pentru prima dată. Apoi, pentru vârful sursă, source.distance=0, ceea ce este corect.
Pentru alte vârfuri u, u.distance=infinity, ceea este deopotrivă corect deoarece nu există nici un
drum de la sursă la u cu 0 muchii.
Pentru pasul inductiv, demonstrăm pentru început prima parte. Considerând un moment în
care distanţa la un vârf este dată de: v.distance:=u.distance+uv.weight. Prin presupunere
inductivă, u.distance este lungimea unui drum oarecare de la sursă la u. Astfel,
u.distance+uv.weight este lungimea drumului de la sursă la v, care nu părăseşte drumul de la
sursă la u şi ajunge la v.
Pentru cea de-a doua parte, considerăm cel mai scurt drum de la sursă la u cu cel mult i
muchii. Fie v ultimul vârf înaintea lui u pe acest drum. Atunci, porţiunea de drum de la sursă la v
este cel mai scurt drum de la sursă la v cu cel mult i-1 muchii. Prin presupunere inductivă,
v.distance, după i-1 cicluri, are cel mult lungimea acestui drum. De aceea, uv.weight+v.distance
are cel mult lungimea drumului de la s la u. La ciclul cu numărul i, u.distance este comparat cu
uv.weight+v.distance, şi se egalează cu această cantitate dacă uv.weight+v.distance este mai mică.
De aceea, după i cicluri, u.distance are cel mult lungimea celui mai scurt drum de la sursă la u,
drum ce foloseşte cel mult i muchii.
Când i egalează numărul vârfurilor grafului, fiecare drum va fi cel mai scurt între toate
vârfurile, doar dacă nu există cicluri negative. Dacă există totuşi un ciclu ponderat negativ şi
accesibil de la sursă, atunci dat fiind un drum oarecare, există unul mai scurt, deci nu există un
cel mai scurt drum. Altfel, cel mai scurt drum nu va include nici un ciclu (deoarece ocolirea
ciclului ar presupune scurtarea drumului), pentru ca fiecare drum mai scurt să viziteze fiecare nod
cel mult o dată, iar numărul de muchii corespunzător să fie mai mic decât numărul vârfurilor
grafului.
Aplicaţii în rutare
O variantă distribuită a algoritmului Bellman – Ford se foloseşte în protocoalele de rutare
distanţă-vector, de exemplu Protocolul de Rutare a Informaţiei (RIP)(Routing Information
Protocol). Algoritmul constă din următorii paşi:
1. Fiecare nod calculează distanţa între “sine” şi toate celelate noduri şi stochează această
informaţie ca un tabel.
2. Fiecare nod îşi trimite tabelul corespunzător tuturor celorlalte noduri.

43
3. În momentul în care un nod primeşte un astfel de tabel de la vecinii săi, calculează cele
mai scurte căi către toate celelalte noduri şi actualizează propriul tabel astfel încât să fie reflectate
toate schimbările survenite.
Marele dezavantaj al algoritmului Bellman-Ford în aceste condiţii constă în:
• Măsurarea incorectă
• Schimbările în topologia reţelei nu sunt reflectate în timp util, odată cu
actualizarea succesivă a tabelelor nodurilor.
• Numărarea la infinit (proces ce survine ca urmare a eşecului transmiterii tabelelor)

Implementare
Următorul program implementează algoritmul Bellman-Ford în C.
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
/* Să considerăm INFINIT-ul o valoare întreagă, pentru a nu interveni confuzia în
valorarea reală, chiar şi cea negativă*/
#define INFINITY ((1 << 14)-1)
typedef struct {
int source;
int dest;
int weight;
} Edge;
void BellmanFord(Edge edges[], int edgecount, int nodecount, int source)
{
int *distance = (int*) malloc(nodecount * sizeof(*distance));
int i, j;
for (i=0; i < nodecount; i++)
distance[i] = INFINITY;
/* distanţa nodului sursă este presetată ca fiind nulă */
distance[source] = 0;
for (i=0; i < nodecount; i++) {
for (j=0; j < edgecount; j++) {
if (distance[edges[j].source] != INFINITY) {
int new_distance = distance[edges[j].source] + edges[j].weight;
if (new_distance < distance[edges[j].dest])
distance[edges[j].dest] = new_distance;
}
}
}
for (i=0; i < edgecount; i++) {
if (distance[edges[i].dest] > distance[edges[i].source] + edges[i].weight) {
puts("S-au detectat cicluri cu muchii ponderate negativ (cu costuri
negative)!");
free(distance);
return;
}
}
for (i=0; i < nodecount; i++) {
printf("Cea mai scurtă distanţă dintre nodurile %d şi %d este %d\n",
source, i, distance[i]);
}
free(distance);
return;
}
int main(void)
{
/* Acest test ar trebui să genereze distanţele 2, 4, 7, -2, and 0. */
Edge edges[10] = {{0,1, 5}, {0,2, 8}, {0,3, -4}, {1,0, -2},
{2,1, -3}, {2,3, 9}, {3,1, 7}, {3,4, 2},
{4,0, 6}, {4,2, 7}};
BellmanFord(edges, 10, 5, 4);

44
return 0;
}
5.1.6. Algoritmul de căutare A*
În ştiinţa calculatoarelor, A* este un algoritm de căutare a grafurilor de tipul “best-first”,
care găseşte drumul de cost de minim de la un nod iniţial la un nod “ţintă” (din una sau mai multe
ţinte posibile).
Foloseşte o funcţie euristică distanţă-plus-cost (notată de regulă cu f ( x ) ) pentru a
determina ordinea în care sunt vizitate nodurile arborelui. Euristic-ul distanţă-plus-cost reprezintă
o sumă de două funcţii: funcţia cost-drum (notată de obicei cu g ( x ) , care poate fi, sau, nu
euristică) şi o “estimare euristică” admisibilă a distanţei către ţintă (notată de regulă cu h( x ) ).
Funcţia cost-drum g ( x ) determină costul de la nodul de start la nodul curent.
Având în vedere faptul că h( x ) , parte a funcţiei f ( x ) , trebuie să fie euristic admisibilă,
trebuie să se “subestimeze” distanţa către ţintă. Astfel, pentru o aplicaţie ca rout-area, h( x ) ar
putea reprezenta distanţa în linie dreaptă la ţintă, ţinând cont şi de faptul că, din punct de vedere
fizic, este cea mai mică distanţa posibilă între oricare două noduri.
Algoritmul a fost descris pentru prima dată în anul 1968 de către Peter Hart, Nils Nilsson,
respectiv Bertram Raphael. Algoritmul era numit algoritmul A. Având în vedere faptul că se face
apel doar la comportamentul optimal pentru un anumit euristic, a fost numit A* .
Descrierea algoritmului
A* caută toate drumurile de la nodul de start, oprindu-se în momentul în care s-a găsit
drumul cel mai scurt la nodul ţintă. Ca toţi algoritmii de căutare informaţionali, cercetează mai
întâi drumurile ce par a conduce la ţintă. Ceea ce prezintă A* în plus faţă de căutarea greedy de
tip best-first este reprezentat de faptul că ia în considerare distanţa deja parcursă
Începând cu un anumit nod (iniţial), algoritmul extinde nodul cu cea mai mică valoare a
lui f ( x ) - nodul care are cel mai mic cost-per-beneficiu. A* menţine o mulţime de soluţii parţiale
- noduri frunză neextinse -, stocată într-o coadă cu priorităţi. Prioritatea asociată unui drum x este
determinată de funcţia f ( x ) = g ( x )  h( x ) . Funcţia “continuă” până când o ţintă are o valoare
corespunzătoare f ( x ) mai mică decât a oricărui nod din coadă (sau până când arborele va fi fost
parcurs în totalitate). Multe alte ţinte pot fi trecute cu vederea dacă există un drum care putea
conduce la o „ţintă” având „costul” mai mic.
Cu cât f ( x ) are o valoare mai mică, cu atât prioritatea este mai mare (astfel, s-ar putea
folosi o min-heap pentru a implementa coada)
function A*(start,goal)
var closed := the empty set
var q := make_queue(path(start))
while q is not empty
var p := remove_first(q)
var x := the last node of p
if x in closed
continue
if x = goal
return p
add x to closed
for each y in successors(x)
enqueue(q, p, y)
return failure

45
Mulţimea închisă poate fi omisă (transformând algoritmul de căutare într-unul mai
maleabil) dacă, fie existenţa soluţiei este garantată, fie membrul successors este adaptat
ciclurilor (respinse).
Proprietăţi
Asemenea căutării „bredth-first”, A* este completă, în sensul că va găsi întotdeauna o
soluţie, în cazul în care aceasta există.
Dacă funcţia euristică h este admisibilă, adică nu supraestimează costul minim actual de
„atingere a scopului”, atunci A* însuşi este admisibil (sau optimal) dacă nu se foloseşte o
mulţime închisă. Dacă se foloseşte o astfel de mulţime închisă, h ar trebui să fie de asemenea
monotonă (sau consistentă) pentru A* astfel încât să fie optimală. A fi admisibil înseamnă că
funcţia euristică nu supraestimează ,niciodată , costul trecerii de la un nod la vecinii săi, în timp
ce a fi monoton înseamnă că dacă există o conexiune de la nodul A la nodul C, respectiv o
legătură de la nodul A la nodurile B şi C, costul estimat de la A la C va fi, întotdeauna, mai mic
sau egal cu cel estimat de la A la B + costul estimat de la B la C. (Monotonia este cunoscută şi
sub numele de inegalitate triunghiulară). Formal, pentru toate drumurile (x, y), unde y este un
succesor al lui x:
g ( x )  h( x )  g ( y )  h( y ) .
* este deopotrivă eficient pentru orice euristic h, aceasta însemnând că nici un alt
A
algoritm ce foloseşte acelaşi euristic nu va extinde mai puţine noduri decât A* , exceptând doar
cazul în care există câteva soluţii parţiale pentru care h prezice cu exactitate costul drumului
optimal.
Optimalitatea în grafurile arbitrare nu garantează performanţe mai mari ca algoritmii
simpli de căutare, care deţin mai multe informaţii legate de acest domeniu. Spre exemplu, într-un
mediu de tip „labirint”, singura posibilitate prin care se poate atinge scopul ar putea necesita o
primă parcurgere (ce evită „ţinta”), întorcându-se ulterior la „ţintă”. Astfel, în acest caz, probarea
prioritară a nodurilor din imediata apropiere a „destinaţiei” ar putea implica un cost ridicat în
ceea ce priveşte timpul implicat.
Cazuri speciale
În general vorbind, depth-first search şi bredth-first search reprezintă două cazuri speciale
(particulare) ale algoritmului A* . Algoritmul lui Dijkstra, un alt exemplu de algoritm de tip best-
first search (căutare prioritară), reprezintă un caz special al A* , unde h( x ) = 0 "x . Pentru
depth-first search (parcurgerea în adâncime), putem considera că există un „contabilizator” C,
iniţializat cu o valoare foarte mare. De fiecare dată când se procesează un nod îi ataşăm C
corespunzător tuturor vecinilor săi astfel descoperiţi. După fiecare astfel de assign-are, micşorăm
„contabilizatorul” C cu o unitate. Astfel, cu cât un nod este „descoperit” mai repede, cu atât
valoarea h(x) corespunzătoare este mai mare.
De ce A* este „admisibil” şi optimal din punct de vedere computaţional
A* este atât admisibil, iar, pe de altă parte, implică şi mai puţine noduri decât orice alt
algoritm de căutare având acelaşi euristic, aceasta deoarece A* porneşte de la cost aproximativ
„optim” al drumului ce parcurge toate nodurile, către „ţintă” („optim” însemnând că acel cost
final va fi cel puţin la fel de mare cu cel estimat).
Când A* finalizează căutarea, a găsit, prin definiţie, un drum al cărui cost actual este mai
mic decât costul estimat al oricărui alt drum ce parcurge nodurile. Având în vedere, însă, faptul că
aceste estimări sunt optimiste, A* poate ignora toate aceste noduri „deschise”. Cu alte cuvinte,

46
A* nu va omite niciodată posibilitatea existenţei unui drum având un cost mai mic, fiind astfel
admisibil.
Să presupunem acum că un algoritm oarecare de căutare A finalizează căutarea găsind un
drum al cărui cost nu este mai mic decât cel estimat. Algoritmul A nu poate exclude posibilitatea
existenţei unui drum al cărui cost prin acel nod să fie mai scăzut, bazându-se pe informaţia
euristică pe care o deţine. Astfel, atât timp cât A poate considera mai puţine noduri decât A* , nu
poate fi admisibil. Deci, A* reprezintă algoritmul de căutare cu cele mai puţine noduri ce poate fi
considerat ca fiind admisibil.
Complexitate
Complexitatea în timp a lui A* depinde de euristic. Potrivit celui mai sumbru scenariu,
numărul nodurilor „extinse” este de ordin exponenţial, în ceea ce priveşte lungimea soluţiei (cel
mai scurt drum), însă este de ordin polinomial atunci când funcţia euristică h satisface următoarea
condiţie:
| h(x) - h* (x) |�O(log h * (x))
unde h* reprezintă euristicul optimal, i.e. costul exact ce-l implică drumul de la x la „ţintă”. Cu
alte cuvinte, eroarea corespunzătoare lui h nu ar trebui să crească mai rapid decât logaritmul
„euristicului perfect” h* , ce returnează distanţa reală de la x la „ţintă”.
O chestiune şi mai problematică a A* decât cea legată de complexitatea în timp, o
constituie uzul de memorie. În cel mai rău caz, ar trebui să memoreze un număr exponenţial de
noduri. S-au elaborat mai multe variante ale algoritmului A* astfel încât să poată face faţă acestei
probleme, printre care amintim: „adâncirea” iterativă A* (ID A* ), memoria–graniţă (la limită)
A* (M A* ), respectiv varianta simplificată a memoriei –graniţă (la limită) A* (SM A* ) şi best-
first search varianta recursivă (RBFS).

5.1.7. Algoritmul Floyd-Warshall


În ştiinţa calculatoarelor, algoritmul Floyd-Warshall (întâlnit uneori şi sub denumirea de
algoritmul Roy-Floyd sau algoritmul WFI, încă din anul în care acest algoritm a fost descris de
către Bernard Roy (1959)) reprezintă un algoritm de analiză a grafului, în vederea găsirii celor
mai scurte drumuri într-un graf ponderat orientat. O singură execuţie a algoritmului va determina
cel mai scurt drum între toate perechile de vârfuri. Algoritmul Floyd-Warshall reprezintă un
exemplu de programare dinamică.
Algoritm
Algoritmul Floyd-Warshall compară toate drumurile posibile ale grafului între fiecare
pereche de vârfuri. Poate realiza acest lucru prin intermediul a doar V 3 comparaţii (acest lucru

este remarcabil, ţinând cont de faptul că ar putea exista V 2 muchii în graf, fiecare combinaţie de
astfel de muchii fiind testată). Acest lucru este posibil prin îmbunătăţirea incrementală a estimării
celui mai scurt drum între două vârfuri, până când estimarea este considerată a fi optimă.
Considerăm un graf G, cu nodurile corespunzătoare V, fiecare dintre acestea fiind
numerotat de la 1 la n. Mai mult, fie funcţia shortestPath(i,j,k) ce returnează cel mai scurt drum
posibil de la i la j, folosind doar vârfurile de la 1 la k, pe post de puncte intermediare de-a lungul
drumului. Acum, fiind dată această funcţie, scopul nostru îl constituie găsirea celui mai scurt
drum de la fiecare i la fiecare j, folosind doar nodurile numerotate de la 1 la k+1.
Există două candidate la statutul de cel mai scurt drum, şi anume: fie adevăratul cel mai
scurt drum, ce foloseşte doar noduri ale mulţimii (1…k), fie există un anume drum ce uneşte i de
47
k+1, pe acest k+1 de j, ce este mai bun. Ştim că cel mai bun drum de la i la j, care foloseşte doar
nodurile mulţimii (1…k) este definit de shortestPath(i,j,k), şi este evident faptul că dacă ar exista
un drum mai bun de la i la k+1, respectiv la j, atunci lungimea acestui drum ar reprezenta
concatenarea celui mai scurt drum de la i la k+1 (folosind vârfuri ale mulţimii (1…k)), respectiv
a celui mai scurt drum de la k+1 la j (folosindu-se deopotrivă vârfurile mulţimii (1…k)).
Astfel, putem defini shortestPath(i,j,k) în termenii următoarei formule recursive:
shortestPath(i, j, k) = min(shortestPath(i, j, k - 1)  shortestPath(i, k, k - 1) 
shortestPath(k, j, k - 1));
shortestPath(i, j, 0) = edgeCost(i, j);
Această formulă constituie „inima” lui Floyd Warshall. Algoritmul funcţionează calculând
mai întâi shortestPath(i,j,1) pentru toate perechile de tipul (i,j), folosind acest rezultat, ulterior,
pentru a calcula shortestPath(i,j,2) pentru toate perechile de tipul (i,j), etc. Acest proces continuă
până când k = n, iar drumul cel mai scurt corespunzător tuturor perechilor (i,j), folosind
nodurile intermediare, va fi fost găsit.
Pseudocodul
În mod convenabil, când se calculează cazul de ordinul k, se poate rescrie informaţia
salvată la calculul corespunzător etapei k-1. Acesta înseamnă că algoritmul foloseşte memorie
pătratică. (A se lua în considerare condiţiile de iniţializare!):
1 /* Fie o funcţie edgeCost(i,j) ce returnează costul muchiei ce uneşte vârfurile i
şi j
2 (infinit dacă nu există).
3 Presupunem de asemenea că n reprezintă numărul nodurilor iar edgeCost(i,i)=0
4 */
5
6 int path[][];
7 /* O matrice 2-Dimensională. La fiecare pas (etapă) a algoritmului, path[i][j]
constituie cel mai scurt drum
8 de la i la j folosind valorile intermediare ale mulţimii(1..k-1). Fiecare drum [i]
[j] este iniţializat la
9 edgeCost(i,j).
10 */
11
12 procedure FloydWarshall ()
13 for k: = 1 to n
14 begin
15 for each (i,j) in (1..n)
16 begin
17 path[i][j] = min ( path[i][j], path[i][k]+path[k][j] );
18 end
19 end
20 endproc

Comportamentul în cazul ciclurilor negative


Pentru un rezultat numeric semnificativ, Floyd-Warshall presupun că nu există cicluri
negative (de fapt, între oricare două perechi de vârfuri care reprezintă parte constituentă a unui
ciclu negativ, drumul cel mai scurt nu poate fi definit în mod corect deoarece drumul poate fi
infinit de mic). Totuşi, dacă există cicluri negative, Floyd-Warshall poate fi folosit pentru
identificarea acestora. Dacă se rulează algoritmul încă odată, unele drumuri pot să scadă, însă nu
se garantează că, între toate vârfurile, drumul corespunzător va fi afectat de aceeaşi manieră.
Dacă numărul de pe diagonală matricei drumului este negativ, este necesar şi suficient ca acest
vârf să aparţină unui ciclu negativ.
Analiza

48
Găsirea tuturor n 2 ai Wk din cei ai Wk -1 necesită 2n 2 operaţii. Ţinând cont de faptul
că am considerat, iniţial, W0 = WR , respectiv am calculat secvenţele matricelor cu elemente 0 şi
1 de ordin n
W1 , W2 , K , Wn = M * ,
R
numărul total de operaţii efectuate este
n  2n 2 = 2 n 3 .
Deci, complexitatea algoritmului este de ordinul O(n 3 ) şi poate fi rezolvat cu ajutorul
unei „maşini” deterministe într-un timp de ordin polinomial.
Aplicaţii şi generalizări
Algoritmul Floyd-Warshall poate fi folosit, printre altele, la rezolvarea următoarelor
probleme:
 Cele mai scurte drumuri în grafuri orientate (algoritmul Floyd)
 „Închiderea” tranzitivă a grafurilor orientate (algoritmul Warshall). În formularea
originală a algoritmului a lui Warshall, graful nu este ponderat şi este reprezentat cu
ajutorul unei matrice de adiacenţă booleană. Mai mult, operaţia de adunare este
înlocuită de conjuncţia logică (AND) iar operaţia de scădere de disjuncţia logică
(OR).
 Găsirea unei expresii regulare, indicând limbajul regulat, acceptat de către un
automat finit (algoritmul lui Kleene)
 Inversarea matricelor reale (algoritmul Gauss-Jordan)
 Rout-area optimală. În cazul acestei aplicaţii preocuparea principală o constituie
găsirea drumului caracterizat de flux maxim între două vârfuri. Aceasta reprezintă
că, în loc să considerăm minimul ca în cazul pseudocodului de mai sus, vom fi
interesaţi de maxim. Costurile muchiilor constituie restricţii în ceea ce priveşte
fluxul. Costurile drumului reprezintă „blocaje”. Astfel, operaţia de sumare de mai
sus este înlocuită cu operaţia corespunzătoare minimului.
 Testarea bipartiţiei unui graf neorientat.

5.1.8. Algoritmul lui Johnson


Algoritmul lui Johnson reprezintă o modalitate de găsire a celor mai scurte drumuri
între toate perechile de vârfuri ale unui graf rar orientat. El permite ca, costurile unor muchii să
fie numere negative, însă nu permite existenţa ciclurilor ponderate negativ.
Descrierea algoritmului
Algoritmul lui Johnson constă în următorii paşi:
1. Pentru început, se adaugă un nod nou q mulţimii iniţiale a nodurilor, legat, prin
muchii de ponderi nule, de toate celelalte noduri.
2. În cea de-a doua etapă, se foloseşte algoritmul Bellman-Ford, începând cu vârful
nou introdus q, în vederea găsirii, pentru fiecare vârf în parte, cel mai puţin
costisitor h(v) a unui drum de la q la v. Dacă în această etapă se găseşte un ciclu
negativ, algoritmul se opreşte.
3. În continuare, muchiile grafului iniţial sunt re-ponderate, folosind valorile
calculate de algoritmul Bellman-Ford: unei muchii ce leagă u şi v, având lungimea
w(u, v), îi este ataşată noua lungime w (u, v)+h(u)-h (v).
4. În final, pentru fiecare nod s, se face apel la algoritmul lui Dijkstra cu scopul de a
găsi cele mai scurte drumuri de la s la toate celelalte noduri ale grafului re-
ponderat.

49
În graful re-ponderat, toate drumurile între o pereche de noduri s respectiv t au o aceeaşi
cantitate adăugată h(s)-h(t), astfel că un drum cel mai scurt în graful iniţial rămâne cel mai scurt
în graful modificat şi vice versa. Totuşi, datorită modului de calcul al valorilor h(v), toate
lungimile muchiilor modificate sunt nenegative, asigurând optimalitatea drumurilor găsite prin
intermediul algoritmului lui Dijkstra. Distanţele în graful iniţial pot fi calculate cu ajutorul
distanţelor calculate cu algoritmul lui Dijkstra, în graful re-ponderat, inversând transformarea de
re-valorare.
Analiza
Complexitatea în timp a algoritmului, folosind heap Fibonacci în implementarea
algoritmului lui Dijkstra, este de ordinul O(| V |2 log | V |  | V || E |) : algoritmul foloseşte un
timp de ordinul O(| V || E |) pentru etapa Bellman-Ford a algoritmului, respectiv de ordinul
O ( | V | log | V |  | E |) pentru fiecare din cele |V| apelări ale algoritmului lui Dijkstra. Astfel,
când graful este rar, timpul total poate fi mai rapid decât cel corespunzător algoritmului Floyd-
Warshall, care rezolvă aceeaşi problemă într-un timp de ordinul O(| V |3 ) .

5.2. Probleme de conexiune. Teorema lui Menger şi aplicaţii


Definiţie. Fie G = (V,E) (di)graf şi X, Y �V . Numim XY – drum în G orice drum D în G
de la un vârf x �X la un vârf y �Y , astfel încât V (D) ∩ X = {x} şi V (D) ∩ Y = {y}.
Vom nota cu D(X,Y;G) mulţimea tuturor XY - drumurilor în G. Să observăm că dacă
x �X �Y atunci drumul de lungime 0, D = {x} este XY -drum.
Vom spune că drumurile D1 şi D2 sunt disjuncte dacă
V (D1) ∩ V (D2) = 0/ .
Probleme practice din reţelele de comunicaţie, dar şi unele probleme legate de conexiunea
grafurilor şi digrafurilor, necesită determinarea unor mulţimi de XY - drumuri disjuncte şi cu
număr maxim de elemente.
Vom nota cu p(X,Y;G) numărul maxim de XY – drumuri disjuncte în (di)graful G, număr
ce a fost stabilit de Menger.
Definiţie. Fie G = (V,E) un digraf şi X, Y �V . Numim mulţime XY-separatoare în G o
mulţime Z �V astfel încât "D �D(X, Y;G) � V (D) ∩ Z ≠ ∅.
Notăm cu
S(X,Y;G) = {Z | Z XY - separatoare în G},
k(X,Y;G) = min {|Z|;Z �S(X, Y ;G)}.
Din definiţie, rezultă următoarele proprietăţi imediate ale mulţimilor XY - separatoare:
(a) Dacă Z �S(X,Y;G) atunci " D �D(X,Y;G), D nu este drum în G − Z.
(b) X, Y �S(X,Y;G).
(c) Dacă Z �S(X,Y;G) atunci " A astfel încât Z �A �V avem A�S(X,Y;G).
(d) Dacă Z �S(X,Y;G) şi T �S(X,Z;G) sau T �S(Z,Y;G) atunci T �D(X,Y;G).
Dăm fără demonstraţie următorul rezultat.
Teoremă.
Fie G = (V,E) (di)graf şi X, Y �V . Atunci
p(X,Y;G) = k(X,Y;G).
Remarcăm:
1) Egalitatea min-max din enunţul teoremei este interesantă şi conduce la rezultate
importante, în cazuri particulare.
2) Teorema se poate demonstra şi algoritmic ca o consecinţă a teoremei fluxului maxim -
secţiunii minime.

50
Forma echivalentă (a teoremei de mai sus) care a fost enunţată şi demonstrată iniţial de
Menger este:
Teoremă.
Fie G = (V,E) un (di)graf şi s, t �V, astfel încât s � t, st �E. Există k drumuri intern
disjuncte de la s la t în graful G dacă şi numai dacă îndepărtând mai puţin de k vârfuri diferite de
s şi t, în graful rămas există un drum de la s la t.
Notăm că două drumuri sunt intern disjuncte dacă nu au vârfuri comune cu excepţia
extremităţilor.
Am definit un graf G p-conex ( p �N* ) dacă G = Kp sau dacă |G| > p şi G nu poate fi
deconectat prin îndepărtarea a mai puţin de p vârfuri.
Avem şi rezultatul.
Corolar.
Un graf G este p-conex dacă G = Kp sau "st �E(G) există p drumuri intern disjuncte de
la s la t în G.
Determinarea numărului k(G) de conexiune a grafului G (cea mai mare valoare a lui p
pentru care G este p-conex) se reduce deci la determinarea lui
max p({s},{t}; G)
st �E(G)
problemă care se poate rezolva în timp polinomial.
Un caz particular interesant al teoremei 1, se obţine atunci când G este un graf bipartit iar
X şi Y sunt cele două clase ale bipartiţiei:
Teoremă. (Konig)
Dacă G = (S,R;E) este un graf bipartit, atunci cardinalul maxim al unui cuplaj (o mulţime
independentă de muchii) este egal cu cardinalul minim al unei mulţimi de vârfuri incidente cu
toate muchiile grafului.

5.3. Structura grafurilor p-conexe


Lemă.
Fie G = (V,E) p-conex, |V|≥p+1,U �V, |U| = p şi x �V-U. Există în G p U – drumuri cu
singurul vârf comun x.
Lemă.
Dacă G = (V,E) este un graf p - conex, p ≥ 2, atunci oricare ar fi două muchii e1 şi e2 şi p -
2 vârfuri x1, x2, . . . , xp−2 există un circuit în G care le conţine.
Teoremă. (Dirac)
Dacă G = (V,E) este un graf p-conex, p≥2, atunci prin orice p vârfuri ale sale trece un
circuit.
Pe baza acestei teoreme, se poate demonstra o condiţie suficientă de hamiltonietate.
Teoremă.
Fie G p-conex. Dacă α(G)≤p atunci G este hamiltonian.

5.4. Problema drumului Hamiltonian


În teoria grafurilor Problema drumului Hamiltonian, respectiv cea a Ciclului
Hamiltonian reprezintă probleme de determinare a existenţei unui drum Hamiltonian, respectiv a
unui ciclu Hamiltonian într-un graf dat (orientat sau nu). Ambele probleme sunt NP- complete.
Există o relaţie simplă între cele două probleme. Problema Drumului Hamiltonian pentru
un graf G este echivalentă cu problema Ciclului Hamiltonian într-un graf H obţinut din G prin
adăugarea unui nou nod, ce va fi conectat cu toate nodurile grafului iniţial G.

51
Problema Ciclului Hamiltonian este un caz special al problemei Comis Voiajorului,
obţinută prin setarea distanţei între două oraşe la o anumită valoare finită, dacă acestea sunt
adiacente, respectiv infinite dacă cele două oraşe nu sunt adiacente.
Problemele Ciclului Hamiltonian orientat sau neorientat reprezintă două din cele 21 de
probleme NP – complete ale lui Karp. Garey şi Johnson au arătat la scurt timp după aceasta, în
anul 1974, că problema Ciclului Hamiltonian orientat rămâne NP – completă pentru grafurile
planare, iar problema ciclului Hamiltonian neorientat rămâne NP – completă pentru grafurile
planare cubice.
Algoritmul aleatoriu
Un algoritm aleatoriu pentru un Ciclu Hamiltonian, care este destul de rapid pentru
ambele tipuri de grafuri, este următorul: Se începe într-un nod oarecare, şi se continuă dacă există
un vecin nevizitat. Dacă nu mai există vecini nevizitaţi, iar drumul rezultat nu este Hamiltonian,
se alege un vecin la întâmplare, urmând o rotaţie folosindu-se pe post de pivot vecinul în cauză.
Are loc următorul rezultat:
Teorema 1.
Fie G un graf cu cel puţin trei vârfuri. Dacă, pentru un s, G este s-conex şi conţine o
mulţime neindependentă cu mai mult de s vârfuri, atunci G are un circuit Hamiltonian.
Această teoremă ne arată că graful complet bipartit K(s,s+1) este s-conex, conţine
mulţimi neindependente cu mai mult de s+1 vârfuri şi nu are circuit Hamiltonian. Similar, graful
Petersen este 3-conex, conţine mulţimi neindependente cu mai mult de patru vârfuri şi nu are
circuit Hamiltonian.
Demonstraţie.
Fie G ce satisface ipoteza Teoremei 1. Evident, G conţine un circuit; fie C cel mai lung
circuit. Dacă G nu are circuit Hamiltonian, atunci există un vârf x, x �C. Deoarece G este s-
conex, există s drumuri începând din x şi terminând în C, care sunt perechi disjunctive despărţite
din x şi partajează cu C chiar în vârfurile lor terminale x1,x2,…,xs. Pentru " i=1,2,…,s , fie yi
succesorul lui xi într-un ciclu ordonat fix al lui C. Nici un yi nu este adiacent cu x – altfel am putea
înlocui muchiile xiyi în C prin drumul de la xi la yi în afara lui C (către x) şi am obţine un circuit
mai lung. Cu toate acestea, G nu conţine mulţimi independente cu s+1 vârfuri şi deci există o
muchie yiyj. Şterge muchiile xiyi, xjyj din C şi adăugă muchia yiyj împreună cu drumul de la xi la xj
în afara lui C. În acest sens obţinem un circuit mai lung decât C, ceea ce este o contradicţie.
Fie G un graf cu n vârfuri , n  3 . G nu conţine vârfuri cu grad mai mic decât k unde k
1
este un întreg astfel încât k � ( n  2 ) . Atunci G ori are un circuit Hamiltonian, ori este
3
separabil, ori are k+1 vârfuri independente.
Ca o consecinţă simplă a teoremei 1 obţinem:
Teorema 2.
Fie G un graf s-conex fără mulţimi independente de s+2 vârfuri. Atunci G are un circuit
Hamiltonian.
Demonstraţie.
Într-adevăr, dacă G satisface ipoteza Teoremei 2, atunci G+x (graful obţinut din G prin
adăugarea lui x şi reunindu-l cu toate vârfurile lui G) satisface ipoteza Teoremei 1 cu s+1 în loc
de s. Aşadar G+x are un circuit Hamiltonian şi G are un drum Hamiltonian. Graful bipartite
complet K(s,s+2) arată că Teorema 2 este evidentă.
Tehnica utilizată în demonstrarea Teoremei 1 ne dă de asemenea
Teorema 3.
Fie G un graf s-conex ce nu conţine s vârfuri independente. Atunci G este Hamiltonian –
conex (i.e. fiecare pereche de vârfuri este unită printr-un drum Hamiltonian).

52
5.5. Problema Ciclului Hamiltonian
Punerea problemei
Problema Ciclului Hamiltonian (PCH) diferă de Problema Comis-Voiajorului (PCV) prin
faptul că graful nu este neapărat complet şi în plus nu se cere ca ciclul să aibă costul minim.
Fie G = (V,U) un graf conex neorientat. Fiecărei muchii i se ataşează un cost strict pozitiv.
Ca urmare, graful va fi reprezentat prin matricea costurilor C, având drept componente:
��0, dacă muchia (i, j) există;
ci, j = �
�0, dacă nu există muchia (i, j);
Costul unui ciclu este definit ca sumă a costurilor ataşate muchiilor componente.
Definiţie. Se numeşte ciclu hamiltonian un ciclu care trece exact o singura dată prin
fiecare vârf.
Pentru determinarea ciclurilor Hamiltoniene vom folosi metoda backtracking. Astfel, dacă
N = card(V), atunci o soluţie oarecare a problemei se poate scrie sub forma unui vector X =
(x1,x2,...,xN+1) .
Condiţiile de continuitate ce trebuie satisfăcute în construcţia soluţiei sunt:
- x1 = xN+1;
- xi ≠ xj, " (i,j) cu i �j;
- (xi, xi+1) �U, " i �{1,...,N}.

Pentru a nu obţine de mai multe ori acelaşi ciclu, se poate fixa x1=1. Fie alese x1,..., xk-1 cu
k �{2,...,N}. Atunci, condiţiile de continuitate, care stabilesc dacă o valoare a lui xk poate conduce
la o soluţie posibilă, sunt următoarele:
- $ muchie între vârfurile xk-1 şi xk, cu xk �{x1,..., xk-1}
- xN trebuie să îndeplinească şi condiţia ca (xN, x1) �U.

Procedura de calcul [1]


Procedura PCH (N, C, X)
/* i este varful din care incepe constructia ciclului */
x[1]=1
x[2]=1
k=2
while k>1
v=0
while x[k]<N
x[k]=x[k]+1
PROC1(c,X,N,k,v)
if v=1 then
exit
endif
repeat
if v=0 then
k=k-1
else
if k=N then
if c[N,1]< µ then
x[N+1]=1
write x
endif
else
k=k+1
x[k]=1
endif
endif

53
repeat
return
end

Procedura PROC1(c,N,X,k,v)
v = 0
if c[x[k-1],x[k]] = µ then
return
endif
for i=2, k-1
if x[k] = x[i] then
return
endif
repeat
v = 1
return
end
Nu se cunosc condiţii necesare şi suficiente direct verificabile pentru a stabili dacă într-un
graf dat există un ciclu hamiltonian.
Are loc următorul rezultat
Teoremă.
N
Într-un graf cu cel puţin 3 vârfuri, cu proprietatea că gradul fiecărui vârf este � , unde
2
N = |V|, există un ciclu hamiltonian.
Demonstraţie.
Fie G=(V,U) un graf cu proprietatea din enunţ. Să presupunem prin absurd că el nu
conţine nici un ciclu hamiltonian. Fie H=(V,D) graful obţinut din G prin adăugarea de noi muchii
între vârfurile neadiacente atâta timp cât acest lucru este posibil, fără ca astfel să se obţină vreun
N
ciclu hamiltonian. Bineînţeles că şi în H fiecare vârf are gradul � .
2
Graful H nu este complet, deoarece într-un graf complet există evident un ciclu
hamiltonian. Există deci două vârfuri i,j � V, neadiacente în H. Printr-o renumerotare a
vârfurilor, putem considera că i = 1 şi j = N.
Din modul de construcţie al grafului H rezultă că adăugarea muchiei (1, N) ar conduce la
apariţia unui ciclu hamiltonian.
Rezultă deci că există în H un lanţ L = {1, i1, ..., iN - 2, N} cu {i1, ..., iN-2} = {2,...,N-
1}.
Fie i j1 ,..., i jk vârfurile adiacente vârfului 1.
N
Evident k � .
2
Vârful N nu este adiacent cu nici unul dintre vârfurile i jI -1 ,..., i jk -1 pentru că dacă N ar fi
adiacent cu i j atunci s-ar obţine următorul ciclu hamiltonian: sublanţul lui L ce uneşte pe 1 cu
s -1
ij , muchia (i js -1 , N ) sublanţul din L ce uneşte pe N cu i js , muchia (i js , I ) , ceea ce nu este
s -1
posibil. Rezultă că vârful N nu poate fi adiacent decât cu
N N
N - ( k  1 ) �N - - 1 = - 1
2 2
N
vârfuri, ceea ce duce la o contradicţie, deoarece fiecare vârf din graful H are cel puţin gradul ,
2
prin ipoteză.

54
/* Program de construire a unui circuit hamiltonian de cost minim */
#include <stdio.h>
#include <conio.h>
#define max 21
#define inf 10000
int k,n,i,j,cont;
int c[max][max]; /* matricea costurilor deplasarilor intre 2 noduri*/
char nume[max][20]; /* numele nodurilor */
int cost_curent, cost_minim;
int x[max]; /* solutia curenta */
int y[max]; /* solutia de cost minim */
int PotContinua()
{
/* nodul curent (x[k]) trebuie sa fie vecin cu anteriorul nod */

if (c[x[k]][x[k-1]]==inf) return 0;
/* ultimul nod trebuie sa fie vecin cu primul */

if (k==n) if (c[x[n]][x[1]]==inf) return 0;


/* trebuie sa nu se mai fi trecut prin acest nod */
for (i=1; i<k; i++)
if (x[i]==x[k]) return 0;
return 1;
}
void main()
{
clrscr();
printf("Problema circuitului hamiltonian de cost minim\n\n");
printf("Scrieti numarul de noduri: ");
scanf("%d",&n);
for (i=1; i<=n; i++)
{
printf("Scrieti numele nodului %d: ",i);
scanf("%s",&nume[i]);
}
for (i=1; i<=n-1; i++)
for (j=i+1; j<=n; j++)
{
printf("Care este costul legaturii de la %s la %s (0=infinit) ?
",nume[i],nume[j]);
scanf("%d",&c[i][j]);
if (c[i][j]==0) c[i][j]=inf;
c[j][i]=c[i][j];
}
x[1]=1;
k=2;
x[k]=1;
cost_minim=inf;
while (k>1)
{
cont=0;
while ((x[k]<n) && (!cont))
{
x[k]++;
cont=PotContinua();
}
if (cont)
if (k==n)
{
cost_curent=0;
for (i=1; i<n; i++)
cost_curent+=c[x[i]][x[i+1]];
cost_curent+=c[x[n]][x[1]];
if (cost_curent<cost_minim)
{

55
cost_minim=cost_curent;
for (i=1; i<=n; i++)
y[i]=x[i];
}
}
else x[++k]=1;
else --k;
}
printf("\n\nCircuitul de cost minim este: \n");
for (i=1; i<=n; i++)
printf("%s -> ", nume[y[i]]);
printf("%s", nume[y[1]]);
printf("\nCostul sau este : %d\n",cost_minim);
getch();
}

5.6. Arborele parţial de cost minim

Arborele parţial de cost minim a unui graf planar. Fiecare muchie este etichetată cu
„costul” corespunzător, care, în exemplul de faţă, este egal cu lungimea sa.
Fiind dat un graf conex, neorientat, un arbore parţial al acestui graf este un subgraf, care
este un arbore. Putem atribui, de asemenea, fiecărei muchii, o valoare, care este reprezentată de
un număr, ce indică cât de „dezavantajoasă” este.
Un arbore parţial de cost minim sau arbore parţial minim ponderat este un arbore
parţial având „valoarea” mai mică sau cel mult egală cu „valoarea” tuturor celorlalţi arbori
parţiali. Mai general, orice graf neorientat are o pădure parţială de cost minim.
Un astfel de exemplu l-ar putea constitui o companie de cablu TV, care îşi „desfăşoară”
cablurile într-un cartier nou. Dacă există o clauză conform căreia compania ar trebui să îngroape
cablurile doar într-o anumită porţiune, atunci aceasta s-ar putea reprezenta cu ajutorul unui graf,
în care, prin intermediul drumurilor se vor conecta aceste regiuni. Unele dintre aceste drumuri ar
putea fi mai costisitoare, fiind mai lungi, sau fiind nevoie ca acele cabluri să fie îngropate mai
adânc; aceste drumuri se vor reprezenta cu ajutorul muchiilor al căror costuri ataşate vor fi mai
mari. Un arbore parţial corespunzător acestui graf l-ar constitui o submulţime a acelor drumuri
care nu au cicluri dar conectează toate imobilele. Ar putea exista mai mulţi astfel de arbori
parţiali. Un arbore parţial de cost minim ar fi reprezentat de acela al cărui cost total ar fi cel mai
mic.
Proprietăţi
P1) Posibilă multiplicitate
Ar putea exista mai mulţi arbori parţiali de cost minim având acelaşi cost; în particular,
dacă toate aceste valori sunt egale, fiecare arbore parţial este minim.

56
P2) Unicitatea
Dacă fiecare muchie are un cost distinct, atunci există un unic arbore parţial de cost
minim. Demonstraţia acestui lucru este trivială şi se poate face folosind inducţia.
P3) Subgraful de cost minim
Dacă costurile sunt nenegative, atunci un arbore parţial de cost minim reprezintă, de
fapt, subgraful de cost minim ce „conectează” toate nodurile, ţinând cont şi de faptul că
subgrafurile ce conţin cicluri au, implicit, o valoare totală mai mare
P4) Proprietatea ciclului
Pentru orice ciclu C al grafului, dacă costul unei muchii e  C este mai mare ca
valoarea tuturor celorlalte muchii din C, atunci această muchie nu poate aparţine unui MST
(Minimal Spanning Tree (Arbore Parţial de Cost Minim)).
P5) Proprietatea tăieturii
Pentru orice tăietură C din graf, dacă costul unei muchii e din C este mai mic decât toate
costurile celorlalte muchii din C, atunci această muchie aparţine tuturor MST – urilor (Arborilor
parţiali de cost minim) corespunzători grafului. Într-adevăr, presupunând contrariul, i.e., e nu
aparţine unui MST T1, atunci adăugând e lui T1 va rezulta un ciclu, care ,implicit, trebuie să mai
conţină o altă muchie e2 din T1 în tăietura C. Înlocuirea muchiei e2 cu e ar da naştere unui
arbore având un cost mai mic.
Algoritmi
Primul algoritm de găsire a unui arbore parţial de cost minim a fost conceput de către
omul de ştiinţă ceh Otakar Borüvka în anul 1926 (algoritmul lui Borüvka). Astăzi se folosesc, cu
precădere doi algoritmi, şi anume: Algoritmul lui Prim, respectiv Algoritmul lui Kruskal. Toţi
aceşti trei algoritmi sunt algoritmi „greedy” ,al căror timp de rulare este de ordin polinomial,
astfel că problema găsirii unor astfel da arbori se încadrează în clasa P. Un alt algoritm „greedy”,
ce-i drept nu prea folosit, este algoritmul reverse - delete, opusul algoritmului lui Kruskal.
Cel mai rapid algoritm de găsire a arborelui parţial de cost minim a fost elaborat de către
Bernard Chazelle, şi se baza pe cel al lui Borüvka. Timpul său de rulare este de ordinul
O(ea(e, v)) , unde e reprezintă numărul muchiilor, v reprezintă numărul vârfurilor, iar a este
funcţionala clasică, inversa funcţiei Ackermann. Funcţia a creşte foarte încet, astfel că în scopuri
practice poate fi considerată o constantă nu mai mare ca 4; aşadar algoritmul lui Chazelle se
apropie (d.p.d.v. al timpului de rulare) de O(e) .
Care este cel mai rapid algoritm ce poate rezolva această problemă? Aceasta este una din
cele mai vechi întrebări deschise a ştiinţei calculatoarelor. Există, în mod cert, o limită inferioară
liniară, având în vedere faptul că trebuie să examinăm cel puţin toate costurile. Dacă aceste
costuri ale muchiilor sunt întregi, atunci algoritmii determinişti sunt caracterizaţi de un timp de
rulare de ordinul O(e) . Pentru valorile generale, David Karger a propus un algoritm aleatoriu al
cărui timp de rulare a fost preconizat ca fiind liniar.
Problema existenţei unui algoritm determinist al cărui timp de rulare să fie liniar, în cazul
costurilor oarecare, este încă deschisă. Cu toate acestea, Seth Pettie şi Vijaya Ramachandran au
găsit un posibil algoritm determinist optimal pentru arborele parţial de cost minim, complexitatea
computaţională a acestuia fiind necunoscută.
Mai recent, cercetătorii şi-au concentrat atenţia asupra rezolvării problemei arborelui
parţial de cost minim de o manieră „paralelă”. De exemplu, lucrarea pragmatică, publicată în anul
2003 „Fast Shared-Memory Algorithms for Computing the Minimum Spanning Forest of Sparse
Graphs" a lui David A. Bader şi a lui Guojing Cong, demonstrează un algoritm care poate calcula
MST de cinci ori mai rapid pe 8 procesoare decât un algoritm secvenţial optimizat. Caracteristic,
algoritmii paraleli se bazează pe algoritmul lui Borüvka – algoritmul lui Prim, dar mai ales cel al
lui Kruskal nu au aceleaşi rezultate în cazul procesoarelor adiţionale.

57
Au fost elaboraţi mai mulţi astfel de algoritmi de calculare a arborilor parţiali de cost
minim corespunzători unui graf „mare”, care trebuie stocat pe disc de fiecare dată. Aceşti
algoritmi de stocare externă, aşa cum sunt descrişi în "Engineering an External Memory
Minimum Spanning Tree Algorithm", a lui Roman Dementiev et al., pot ajunge să opereze cel
puţin de două până la cinci ori mai lent ca un algoritm tradiţional in-memory; ei pretind că
„problemele aferente unui arbore parţial de cost minim masiv, ce ocupă mai multe hard disk-uri,
pot fi rezolvate pe un PC.” Se bazează pe algoritmi de sortare - stocare externă eficienţi şi pe
tehnici de contracţie a grafului, folosite în scopul reducerii eficiente a mărimii acelui graf .

5.7. Algoritmul lui Prim


Algoritmul lui Prim este un algoritm care găseşte un arbore parţial de cost minim pentru
un graf conex. Aceasta înseamnă că găseşte o submulţime de muchii care formează un arbore, ce
include toate nodurile, iar valoarea tuturor muchiilor arborelui corespunzător este minimizată.
Algoritmul a fost descoperit în anul 1930 de către matematicianul Vojtêch Jarník, iar mai apoi, în
mod independent, de către cercetătorul în domeniul calculatoarelor Robert C. Prim, în anul 1957,
respectiv redescoperit de Dijkstra în anul 1959. De aceea mai este numit, uneori, Algoritmul
DJP sau Algoritmul Jarnik.

Descriere
Algoritmul măreşte în permanenţă dimensiunea arborelui iniţial, care conţine un singur
vârf, astfel încât la sfârşitul parcurgerii algoritmului acesta (n.arborele) să se fi extins la toate
nodurile.
 Date de intrare: Un graf conex ponderat G = (V,E)
 Iniţializare: V  =  x , unde x este un nod arbitrar din V, E  =  
 Repetă până când V  = V :
• Alegeţi o muchie (u,v) din E, având valoare minimă, astfel încât u este
din V  iar v nu aparţine mulţimii V  (dacă există mai multe muchii ce au
aceeaşi valoare, alegerea uneia dintre ele este arbitrară)
• Adăugaţi v la V  , adăugaţi (u,v) mulţimii E 
 Date de ieşire: G = ( V � , E�) este arborele parţial de cost minim
Complexitate în timp
Complexitatea în timp (total) pentru:
- căutarea cu ajutorul matricei de adiacenţă este |V|2;
- heap binar (ca în pseudocodul de mai jos) şi lista de adiacenţă este:
O((|V| + |E|) log(|V|)) = |E| log(|V|)
- heap Fibonaci şi lista de adiacenţă este:
|E| + |V| log(|V|)
O simplă implementare ce foloseşte o matrice de adiacenţă pentru reprezentarea grafului,
şi care cercetează un tablou de costuri pentru a găsi muchia de cost minim ce urmează a fi
adăugată, necesită un timp de rulare de ordinul O(|V|2). Folosind o structură de date simplă de tip
heap binar, respectiv o reprezentare cu ajutorul listei de adiacenţă, se poate demonstra că
algoritmul lui Prim rulează într-un timp de ordinul
O(| E | log | V |) ,
unde |E| reprezintă numărul muchiilor, iar |V| reprezintă numărul vârfurilor. Apelând la mai
sofisticata heap Fibonacci, acest timp de rulare poate fi redus până la
O(| E |  | V | log | V |) ,

58
semnificativ mai rapid pentru grafurile destul de dense (|E| este W(| V | log | V |) ).
Exemple.

Acesta este graful ponderat, iniţial. Nu este arbore


Nevizitaţi: C, G
Fringe: A, B, E, F
Mulţimea soluţiilor: D

Cel de-al doilea nod ales este nodul cel mai apropiat lui D: A, cu un cost de 5.
Nevizitaţi: C, G
Fringe: B, E, F
Mulţimea soluţiilor: A, D

Următorul nod ales este acela situat în imediata apropiere fie a nodului D fie a lui A. B se află la
„o depărtare” 9 de D, respectiv 7 faţă de A, E la 15, iar F la 6. 6 este cea mai mică valoare, astfel
că vom marca vârful F şi arcul DF.
Nevizitaţi: C
Fringe: B, E, G
Mulţimea soluţiilor: A, D, F

59
Se marchează nodul B, care se află la o „distanţă” 7 de A. De data aceasta arcul DB va fi colorat
cu roşu , deoarece atât nodul B cât şi nodul D au fost marcate, deci nu mai pot fi folosite.
Nevizitaţi: null
Fringe: C, E, G
Mulţimea soluţiilor: A, D, F, B

În acest caz, putem alege între C, E şi G.C se află la o „distanţa” de 8 faţă de B, E la 7 faţa de B,
iar G la 11 faţă de F. E este cel mai apropiat, astfel că va fi marcat vârful E şi arcul EB. Alte două
arce au fost colorate cu roşu, deoarece ambele legau noduri deja marcate
Nevizitaţi: null
Fringe: C, G
Mulţimea soluţiilor: A, D, F, B, E

În acest caz, singurele vârfuri „disponibile” sunt C şi G. C se află la o „distanţă” 5 de E, iar G la


9 faţa de E. Se alege aşadar C, fiind marcat odată cu arcul EC.
Nevizitaţi: null
Fringe: G
Mulţimea soluţiilor: A, D, F, B, E, C

Vârful G este singurul vârf rămas. Se află la o ”distanţa” 11 de F, respectiv 9 faţă de E. E este mai
aproape, astfel că îl marcăm împreună cu arcul EG. La acest moment toate vârfurile vor fi fost
marcate, arborele parţial de cost minim fiind marcat cu verde. În acest caz, are o valoare de 39.
Nevizitaţi: null
Fringe: null
Mulţimea soluţiilor: A, D, F, B, E, C, G

Pseudocodul
Iniţializare

60
Date de intrare: Un graf, o funcţie ce returnează „costurile” muchiilor – funcţia
costurilor, respectiv un nod iniţial.
Starea iniţială a tuturor vârfurilor: „nevizitaţi”, mulţimea iniţială de vârfuri ce urmează a
fi adăugaţi arborelui, plasându-le într-o Min-heap cu scopul de extrage „distanţa minimă” din
graful minim.
for each vertex in graph
set min_distance of vertex to ∞
set parent of vertex to null
set minimum_adjacency_list of vertex to empty list
set is_in_Q of vertex to true
set distance of initial vertex to zero
add to minimum-heap Q all vertices in graph.

Algoritm
În descrierea algoritmului de mai sus:
cel mai apropiat vârf este Q[0], acum ultima „adăugare”
fringe este v în Q unde distanţa lui v <  după extragerea celui mai apropiat vârf
nevizitat este v din Q pentru care distanţa corespunzătoare v =  , după extragerea
celui mai apropiat vârf

Bucla while „se termină” în momentul în care „gradul” minim returnează null. Lista de
adiacenţă este astfel construită încât permite „întoarcerea” pe un graf direcţionat.

Complexitatea în timp: |V| pentru buclă, log(|V|) pentru funcţia de întoarcere


while latest_addition = remove minimum in Q
set is_in_Q of latest_addition to false
add latest_addition to (minimum_adjacency_list of (parent of latest_addition))
add (parent of latest_addition) to (minimum_adjacency_list of latest_addition)

Complexitate în timp: |E| / |V|, număr mediu de vârfuri


for each adjacent of latest_addition
if (is_in_Q of adjacent) and (weight-function(latest_addition, adjacent) <
min_distance of adjacent)
set parent of adjacent to latest_addition
set min_distance of adjacent to weight-function(latest_addition, adjacent)

Complexitate în timp: log (|V|), înălţimea heap


update adjacent in Q, order by min_distance

Demonstraţia corectitudinii
Fie P un graf conex, ponderat.
La fiecare iteraţie a algoritmului lui Prim, trebuie să se găsească o muchie ce leagă un vârf
ce aparţine subgrafului de un altul din afara acestuia. Având în vedere faptul că P este conex, va
exista întotdeauna un drum către orice vârf.
Ceea ce rezultă în urma parcurgerii algoritmului lui Prim este un arbore Y, explicaţia fiind
dată de faptul că muchia şi vârful adăugate lui Y sunt conexe. Fie Y1 un arbore parţial de cost
minim al lui Y.
Dacă Y1 = Y atunci Y este un arbore parţial de cost minim.

61
Altfel, fie e prima muchie adăugată la „construcţia” lui Y, muchie ce nu este în Y1 , şi fie
V mulţimea vârfurilor conectate prin intermediul muchiilor adăugate înaintea muchiei e. Atunci
unul dintre vârfurile ce compun muchia e se va găsi în V iar celălalt nu. Ţinând cont de faptul că
Y1 este un arbore parţial al lui P, există un drum în Y1 ce uneşte aceste două vârfuri. Pe măsură
ce se parcurge acest drum, trebuie să se găsească o muchie f ce uneşte un vârf din V de un altul ce
nu se găseşte în V. Acum, la momentul iteraţiei în care e este adăugată lui Y, există posibilitatea
ca şi f să fi fost adăugată, acest lucru fiind posibil în eventualitatea deţinerii unui cost mai mic
decât cel al muchiei e.

Dat fiind faptul că f nu a fost adăugată deducem că


w ( f ) �w ( e ) .
Fie Y2 graful obţinut prin înlăturarea muchiei f, respectiv adăugarea muchiei e din Y1 .
Se arată uşor faptul că Y2 este conex, are acelaşi număr de muchii ca şi Y1 , iar costul
total al muchiilor constituente nu-l depăşeşte pe cel al lui Y1 , astfel că este de asemenea un
arbore parţial de cost minim al lui P, conţinând muchia e şi toate celelalte muchii ce o precedau
în construcţia V.
Repetând paşii anteriori vom putea obţine un arbore parţial de cost minim al lui P, identic
cu Y.
Aceasta demonstrează că Y este un arbore parţial de cost minim.

5.8. Algoritmul lui Kruskal


Algoritmul lui Kruskal este un algoritm în teoria grafurilor, care determină arborele
parţial de cost minim pentru un graf conex ponderat. Aceasta înseamnă că determină o
submulţime de muchii care formează un arbore ce include fiecare vârf, iar valoarea totală a
costurilor ataşate muchiilor arborelui este minimizată. Dacă graful nu este conex, atunci
algoritmul determină o pădure parţială de cost minim (câte un arbore parţial de cost minim
pentru fiecare componentă conexă). Algoritmul lui Kruskal reprezintă un exemplu de algoritm
„greedy”.

Este un exemplu pentru algoritmul lui Kruskal


Principiul de funcţionare (al algoritmului):
■ „construieşte” o pădure F (o mulţime de arbori), în care fiecare nod al grafului
simbolizează un arbore individual
■ „construieşte” o mulţime S, ce conţine toate muchiile grafului
■ cât timp S este nevidă
■ se şterge o muchie având valoarea cea mai mică din mulţimea S

62
■ dacă acea muchie leagă doi arbori diferiţi, atunci adaugă aceşti arbori pădurii,
combinându-i într-unul singur
■ altfel, elimină muchia respectivă
Acest algoritm a apărut pentru prima dată în Proceedings of the American Mathematical
Society, în anul 1956, şi a fost scris de către Joseph Kruskal.
Funcţionare
Ţinând cont de faptul că |E| reprezintă numărul muchiilor grafului, iar |V| reprezintă
numărul vârfurilor grafului, se poate demonstra că timpul de rulare al algoritmului lui Kruskal
este de ordinul O(| E | log | E |) , sau, echivalent, de ordinul O(| E | log | V |) , în cazul structurilor
de date simple. Aceşti timpi de rulare sunt echivalenţi deoarece:
■ |E| este cel mult | V |2 , iar
log | V |2 = 2 log | V |
este
O(log | V |) .
■ Dacă ignorăm vârfurile izolate, care vor constitui propriile componente ale arborilor
parţiali de cost minim, | V |�2 | E | , astfel că log | V | este O(log | E |) .
Putem obţine aceasta astfel: se sortează, pentru început, muchiile, după costuri folosind o
sortare de comparare, al cărui timp de rulare este de ordinul O(| E | log | E |) ; acest lucru permite
pasului „elimină o muchie de cost minim din S ” să opereze într-un timp constant. În continuare,
folosim o mulţime de separaţie a structurilor de date, astfel ca să se poată contabiliza vârfurile şi
apartenenţa la anumite componente. Este nevoie de execuţia a O(| E |) operaţii, pentru „găsirea”
operaţiilor şi a unei posibile uniuni pentru fiecare muchie în parte. Chiar şi o simplă structură de
date de tip mulţime de separaţie, cum ar fi pădurile pot executa O(| E |) operaţii într-un timp de
ordinul O(| E | log | V |) . Astfel, timpul total este
O(| E | log | E |) = O(| E | log | V |) .
Dacă muchiile sunt deja sortate sau pot fi sortate într-un timp liniar, algoritmul poate
folosi structuri de date de tip mulţime de separaţie mult mai sofisticate pentru a rula într-un timp
de ordinul O(| E || a (V) |) , unde a reprezintă inversul unei funcţii ponderate-singular
Ackermann, ce creşte foarte încet.

Exemplu

Acesta este graful iniţial. Numerele ataşate muchiilor indică valoarea acestora. Nici unul
dintre aceste arce nu este colorat.

63
AD şi CE sunt cele mai „scurte” arce, având lungimea 5, iar AD a fost ales arbitrar, astfel că
apare colorat

Oricum, CE este , la momentul actual, cel mai scurt arc care nu formează buclă, de lungime 5,
astfel că este colorat.

Arcul următor, DF de lungime 6, este colorat, pe baza aceluiaşi principiu.

Următoarele cele mai scurte arce sunt AB şi BE, ambele având lungimea 7. Se alege în mod
arbitrar AB, şi se colorează. Arcul BD se colorează cu roşu, deoarece ar forma o buclă ABD
dacă ar fi ales.

64
Procesul continuă colorând următorul cel mai scurt arc, BE de lungime 7. Mult mai multe arce
sunt colorate cu roşu în această etapă:BC deoarece ar forma bucla BCE, DE deoarece ar
forma bucla DEBA, respectiv FE deoarece ar forma FEBAD.

În cele din urmă, procesul se încheie cu arcul EG de lungime 9, găsindu-se arborele parţial de
cost minim.
Demonstraţia corectitudinii
Fie P un graf conex, ponderat şi fie Y subgraful lui P, rezultat al algoritmului. Y nu poate
conţine cicluri, odată ce această din urmă muchie adăugată ciclului respectiv ar fi aparţinut unui
subarbore şi nu ar fi făcut legătura între doi arbori diferiţi. Y nu poate fi neconex , având în vedere
faptul că prima muchie întâlnită ce uneşte două din componentele lui Y este aleasă de către
algoritm. Astfel, Y este arbore parţial al lui P.
Rămâne de demonstrat faptul că arborele parţial Y este de cost minim:
Fie Y1 un arbore parţial de cost minim. Dacă Y = Y1 atunci Y este un arbore parţial de
cost minim. Altfel, fie e prima muchie considerată de către algoritm, muchie ce este în Y dar nu
este în Y1 . Y1  e conţine un ciclu, deoarece nu se poate adăuga o muchie unui arbore parţial
astfel încât să continuăm să avem un arbore. Acest ciclu conţine o altă muchie f ,care, în etapa în
care e a fost adăugată, nu a fost luată în considerare. Aceasta din pricina faptului că în acest caz e

nu ar fi conectat arbori diferiţi, ci două ramuri ale aceluiaşi arbore. Astfel, Y2 = Y1  e \ f este, de

asemenea, un arbore parţial. Valoarea sa totală este mai mică sau cel mult egală cu valoarea totală
a lui Y1 . Aceasta se întâmplă deoarece algoritmul „vizitează” muchia e înaintea muchiei f, şi
drept urmare w ( e ) �w ( f ) . Dacă se întâmplă ca aceste valori să fie egale, considerăm
următoarea muchie e, ce se găseşte în Y dar nu în Y1 . Dacă nu mai sunt astfel de muchii,
valoarea lui Y este egală cu cea a lui Y1 , deşi sunt caracterizaţi de mulţimi diferite de muchii, iar
Y este de asemenea un arbore parţial de cost minim. În cazul în care valoarea lui Y2 este mai
mică decât valoarea lui Y1 , putem concluziona că acesta din urmă ( Y1 ) nu este un arbore
parţial de cost minim, iar presupunerea conform căreia există muchii e, f cu w ( e ) < w ( f ) este
falsă. De aceea, Y este un arbore parţial de cost minim (egal cu Y1 sau cu o altă mulţime de
muchii, dar având aceeaşi valoare).
Pseudocodul
65
1 function Kruskal(G)
2 for each vertex v in G do
3 Define an elementary cluster C(v) ← {v}.
4 Initialize a priority queue Q to contain all edges in G, using the weights as
keys.
5 Define a tree T ← Ø //T will ultimately contain the edges of the
MST
6 // n is total number of vertices
7 while T has fewer than n-1 edges do
8 // edge u,v is the minimum weighted route from/to v
9 (u,v) ← Q.removeMin()
10 // prevent cycles in T. add u,v only if T does not already contain an edge
consisting of u and v.
// Note that the cluster contains more than one vertex only if an edge
containing a pair of
// the vertices has been added to the tree.
12 Let C(v) be the cluster containing v, and let C(u) be the cluster containing u.
13 if C(v) ≠ C(u) then
14 Add edge (v,u) to T.
15 Merge C(v) and C(u) into one cluster, that is, union C(v) and C(u).
16 return tree T

Discutii finale:
Sa se determine cel mai scurt drum, sa se aplice algoritmul lui Dijkstra, algoritmul Bellman-Ford,
algoritmul Floyd-Warshall pentru un graf dat, sa se determine conexiunea pe un graf dat, sa se
arate existenta drumului si ciclului Hamiltonian, sa se aplice algoritmul lui Prim, algoritmul lui
Kruskal pentru un graf dat.
Tema propusa:
Sa se proiecteze programe aplicand algoritmul lui Dijkstra, algoritmul Bellman-Ford, algoritmul
Floyd-Warshall, algoritmul lui Prim, algoritmul lui Kruskal.

66
Capitolul 6
PROBLEME DE FLUXURI ÎN REŢELE

Cuvinte cheie
Fluxul maxim, cost minim.
Obiectivele invatarii
Dupa parcurgerea acestui capitol va trebui:
Sa se cunoasca problema fluxului maxim, problema fluxului de cost minim, algoritmul lui Ford-
Fulkerson.

6.1. Problema fluxului maxim


Numim reţea (de transport) cu intrarea s şi ieşirea t, 4 – uplul R = (G,s,t,c) unde:
 G = (V, E) este un digraf;
 -
 s, t ι V, s t, d G (s) > 0, d G (t) > 0 ;
 c : E � R  , c(e) este capacitatea arcului e.
Vom presupune că
V = {1, 2, ..., n} ( n �N* )
şi că
|E| = m.
Extindem funcţia c la
c : V �V � R 
prin:
c(ij), ij �E

c((i, j)) = �
0,
� ij �E
şi vom nota c((i,j)) = cij .
Definiţie. Numim flux în reţeaua R = (G,s,t,c) o funcţie x : V �V � R care satisface
(i) 0 �� xij "δcij , ij V V ;
� x ji - � x ij = 0, "i �V - {s, t} .
(ii) j�V j�V
Dacă ij �E atunci x ij se numeşte fluxul (transportat) pe arcul ij.
Evident, condiţia (i) cere ca fluxul pe orice arc să fie nenegativ şi subcapacitar, iar
condiţia (ii) (legea de conservare a fluxului) cere ca suma fluxurilor pe arcele care intră în vârful i
să fie egală cu suma fluxurilor pe arcele care ies din vârful i.

67
Cu convenţia făcută la extensia funcţiei de capacitate, se observă că pentru perechile
(i, j) care nu sunt arce în reţea condiţia (i) impune ca fluxul să fie 0, şi evident cele doua
definiţii sunt echivalente.
Dacă se sumează relaţiile (ii) (pentru i �V - {s, t} ) se obţine:
� �
0 = � �� x ji - � xij �= � � x ji - � � xij 
i ����
�j V
s, t �
� i s,t j s,t
j V � i s,t j s,t
Definiţie.
� �� �
 � x si  � x ti - � x is - � x it = - � �x is - �x si �- ��x it - �x ti �
,
i �s,t i �s, t i �s,t i �s, t �i i � �i i �
Dacă x este un flux în reţeaua R = (G,s,t,c) se numeşte valoarea fluxului x numărul
v(x) = � x jt - � x tj
j�V j�V
.
v(x) se poate interpreta ca fiind fluxul net care ajunge în ieşirea reţelei sau (conform egalităţii
obţinute mai sus) fluxul net care iese din intrarea reţelei.
În orice reţea R = (G,s,t,c) există un flux, fluxul nul x ij = 0 "ij , de valoare 0.
Problema fluxului maxim
Dată R = (G,s,t,c) o reţea, să se determine un flux de valoare maximă.
Problema se poate formula, ca o problemă de programare liniară:
�max v


��x ji - �x ij = 0, "i �s, t
�j j

��x js - �x sj = - v
�j j
� x - x =v
�� jt � tj
�j j
�0 �x ij �cij "ij

Definiţie. Dacă P este un drum în G , multigraful suport al digrafului G, şi e = vi v j este
o muchie a lui P atunci:
- dacă e corespunde arcului vi v j al lui G, e se numeşte arc direct al drumului P;
- dacă e corespunde arcului v j vi al lui G, atunci e se numeşte arc invers.
Definiţie. Fie R = (G,s,t,c) şi x flux în R. Se numeşte C-drum (în R relativ la fluxul x) un
drum D în G cu proprietatea că "ij �E(D) :
x ij < cij dacă ij este arc direct;
x ji > 0 dacă ij este arc invers.
Dacă D este un C – drum şi ij �E(D) , se numeşte capacitate reziduală a lui ij (relativ la
C – drumul D) numărul

�cij - x ij , ij arc direct în D
r(ij) = �
�x ji , ij arc invers în D.
Capacitatea reziduală a drumului D este:

68
r(D) = min r(e) .
e�E(D)
Definiţie. Se numeşte drum de creştere a fluxului x, în reţeaua R = (G,s,t,c), un C-drum
de la s la t.
Se arată că:
Lemă.
Dacă D este un drum de creştere a fluxului x în reţeaua R = (G,s,t,c), atunci
1
x = x �r(D) definit prin
�x , ij �E(D)
ij


x1ij = �x ij  r(D), ij �E(D), ij arc direct în D

�x - r(D), ji �E(D), ji arc invers în D
� ij
este flux în R şi v(x1 ) = v(x)  r(D) .
Rezultă că dacă x admite un drum de creştere atunci x nu este flux de valoare maximă.
Definiţie. Fie R = (G,s,t,c). Se numeşte secţiune în reţeaua R, o partiţie (S,T) a lui V cu
s �S şi t �T .
Capacitatea secţiunii (S,T) este
c(S, T) = � � cij
i�S j�T
(suma capacităţilor arcelor de la S la T).
Se arată că:
Lemă.
Dacă x este un flux în R = (G,s,t,c) şi (S,T) este o secţiune a reţelei, atunci
v(x) = � � (x ij - x ji )
i�S j�T
(valoarea fluxului este egală cu fluxul net ce trece prin orice secţiune)
Lemă. Dacă x este un flux în R = (G,s,t,c) şi (S,T) este o secţiune, atunci v(x) ≤ c(S,T).
Teoremă. (Teorema drumului de creştere)
Un flux x este de valoare maximă într-o reţea R, dacă şi numai dacă, nu există drumuri de
creştere a fluxului x în reţeaua R.
Să observăm că "i �S şi "j �T avem:
- dacă ij �E atunci x ij = cij şi
- dacă ji �E atunci x ji = 0

(altfel, C-drumul de la s la i se poate extinde la un C-drum de la s la j).


Deci, conform lemei precedente:
v(x) = � � (x ij - x ji ) = � � (cij - 0) = c(S, T)
i�S j�T i�S j�T
şi prin urmare x este flux de valoare maximă.
Teoremă. (Teorema fluxului întreg)
Dacă toate capacităţile sunt întregi, atunci există un flux de valoare maximă cu toate
componentele întregi (flux întreg de valoare maximă).
Demonstraţie.
Fie algoritmul

69
1: x 0 � 0; i � 0;
2: while ( $Pi drum de creştere relativ la x i ) do
{
x i 1 � xi �r(Pi ) ;
i + +
}

Se observă că ” x i are componente întregi” este un invariant al algoritmului (din definiţia


lui r(Pi ) , dacă toate capacităţile sunt întregi, rezultă că r(Pi ) este întreg în ipoteza că x i este
întreg) şi că la fiecare iteraţie a pasului 2 valoarea fluxului curent creşte cu măcar o unitate, deci
pasul 2 se repetă de cel mult c({s}, V − {s}) �Z  ori. Fluxul final obţinut este de valoare
maximă.
Observaţie. Algoritmul, descris mai sus, este finit şi în cazul capacităţilor raţionale.
Teoremă. ( Ford-Fulkerson)
Valoarea maximă a unui flux în reţeaua R =(G,s,t,c) este egală cu capacitatea minimă a
unei secţiuni a reţelei.
Demonstraţie.
Dacă dispunem de un algoritm care, pornind de la un flux iniţial x 0 ( x 0 există
întotdeauna, de exemplu x 0 = 0), construieşte într-un număr finit de paşi un flux x, care nu
admite drumuri de creştere, atunci secţiunea construită în demonstraţia teoremei antecedente
satisface împreună cu x enunţul teoremei.
Pentru cazul capacităţilor raţionale algoritmul descris satisface această condiţie.
Pentru cazul capacităţilor reale există un algoritm, datorat lui Edmonds şi Karp.
Algoritmul lui Ford şi Fulkerson pentru aflarea unui flux de valoare maximă
Se va folosi un procedeu de etichetare a vârfurilor reţelei, în vederea depistării drumurilor
de creştere a fluxului curent x. Dacă nu există drumuri de creştere, fluxul va fi de valoare
maximă.
Eticheta atribuită unui vârf j �V are trei componente (e1 , e2 , e3 ) unde e1 �V �{0} ,
e 2 �{direct, invers}; e3 �R  şi au următoarea semnificaţie:
- dacă e 2 = direct şi e1 = i atunci
$ un C-drum P de la s la j cu ultimul arc ij, arc direct şi r(P) = e3 ;
- dacă e 2 = invers şi e1 = i atunci
$ un C-drum P de la s la j cu ultimul arc ij, arc invers şi r(P) = e3 .
Iniţial, se etichetează sursa s cu eticheta (0, . , �) . Celelalte vârfuri primesc etichetă prin
”cercetarea” vârfurilor deja etichetate:
Dacă i este un vârf etichetat, atunci "j �V :
- dacă j neetichetat,
ij �E şi x ij < cij
atunci j se etichetează
e = (i, direct, min(e3 [i], cij - x ij )) ;
- dacă j neetichetat,
ji �E şi x ji > 0
atunci j se etichetează

70
e = (i,invers, min(e3 [i], x ji )) .
Evident, în acest fel se respectă semnificaţia celor trei componente ale etichetelor.
Numim această procedură etichetare(i).
Atunci când în procedura de etichetare s-a atribuit etichetă vârfului t, s-a obţinut un drum
de creştere P a fluxului curent, de capacitate reziduală r(P) = e3 [t] şi ale cărui vârfuri se
depistează în O(n) explorând prima componentă a etichetelor. Modificarea fluxului x �r(P) se
execută în acest mers înapoi, tot în O(n).
Pentru noul flux se reia procedura de etichetare.
Dacă toate vârfurile etichetate au fost cercetate şi nu s-a reuşit etichetarea vârfului t,
rezultă că fluxul curent nu admite drumuri de creştere, este deci de valoare maximă, iar dacă S =
mulţimea vârfurilor etichetate atunci (S, V − S) este o secţiune de capacitate minimă.
Descrierea algoritmului

1. Se alege x = (x ij ) flux iniţial (de exemplu fluxul nul);


Se etichetează s cu (0, . , �) .
2. while ( $ vârfuri etichetate necercetate) do
{
”alege” un vârf etichetat şi necercetat i;
etichetare(i);
if (t a primit etichetă) then
{
modifică fluxul pe drumul dat de etichete;
şterge toate etichetele;
etichetează s cu (0, . , �)
}
}
3. S � {i i are etichetă}
T � V -S
x este flux de valoare maximă.
(S,T) este secţiune de capacitate minimă.

Complexitatea algoritmului
Pentru fiecare creştere a fluxului, sunt necesare cel mult 2m (m = |E|) inspecţii de arce în
vederea etichetării.
Dacă toate capacităţile sunt întregi atunci vor fi necesare cel mult v (v = valoarea fluxului
maxim) creşteri succesive. Rezultă că algoritmul are complexitatea O(mv).
Dacă U este o margine superioară a capacităţilor arcelor atunci
v ≤ (n − 1)U
((n − 1)U este o margine superioară a capacităţii secţiunii
({s}, V −{s})),
deci algoritmul are complexitatea
O(nmU).
Dezavantajele algoritmului sunt legate de neconvergenţa în cazul capacităţilor iraţionale
(deşi practic, în implementări nu este cazul), şi de faptul ca mărimile capacităţilor influenţează
comportarea sa, acestea neconstituind o măsură a volumului datelor de intrare.

6.2. Fluxuri de cost minim


Fie R = (G,s,t,c) o reţea şi x un flux de la s la t în R.
Considerăm a : E � R o funcţie de cost care asociază fiecărui arc ij �E , a(ij) = a ij
costul (transportului unei unităţi de flux) pe arcul ij.

71
Costul fluxului x se defineşte ca fiind
a(x) = �a ij x ij
i, j
.
Problema fluxului de cost minim
Dată R o reţea, v �R  şi a : E � R funcţie de cost, să se determine x 0 flux în R astfel
încât
a(x 0 ) = min  a(x) x flux în R, v(x) = v .
Observăm că, dacă v nu depăşeşte valoarea fluxului maxim în reţeaua R, atunci problema
are întotdeauna soluţii, a(x) fiind liniară, iar mulţimea fluxurilor de valoare dată v fiind mărginită
şi închisă în R m .
Definiţie. Fie x un flux în R = (G,s,t,c) şi a : E � R o funcţie de cost.
Dacă P este un C-drum în R relativ la fluxul x, atunci costul drumului P se defineşte
a(P) = � a ij - � a ji
ij�P ij�P .
ij direct ij invers
Dacă C este un C-drum închis, a(C) se calculează după aceeaşi formulă, după stabilirea
unui sens de parcurgere a lui C (este posibil ca ambele sensuri de parcurgere ale lui C să satisfacă
definiţia unui C-drum).
1. Din definiţia dată, rezultă că dacă P este drum de creştere relativ la fluxul x, atunci
x1 = x �r(P) este un flux de valoare v(x1 ) = v(x)  r(P) şi de cost a(x)  r(P) �a(P) .
2. Dacă C este un C-drum închis relativ la x, atunci x1 = x �r(C) este un flux de valoare
v(x1 ) = v(x) şi de cost a(x1 ) = a(x)  r(C) �
a(C) .
Dacă a(C) < 0 atunci x1 este un flux de aceeaşi valoare ca şi x, dar de cost strict mai mic.
Teoremă.
Un flux de valoare v este de cost minim dacă şi numai dacă nu admite C - drumuri închise
de cost negativ.
Demonstraţie.
Necesitatea este evidentă din observaţia anterioară.
Suficienţa. Fie x un flux de valoare v, care nu admite C - drumuri închise de cost
negativ.
Fie x* un flux de valoare v, de cost minim astfel încât
D (x, x * ) = min{D(x, x ') x ' flux de valoare v şi cost minim}

 
unde D(x, x ') = | ij x ij �x 'ij | .
Dacă D (x, x* ) = 0 rezultă că x = x* şi deci x este de cost minim.
* *
Dacă D (x, x* ) > 0 , fie ij astfel încât x ij �x ij . Presupunem 0 �x ij < x ij �cij (astfel,
raţionamentul este similar). Din legea de conservare a fluxurilor rezultă că
$jk �E astfel încât 0 �x jk < x*jk �c jk
sau
$kj �E astfel încât 0 �x*kj < x kj �c jk .
Repetând acest raţionament, deoarece numărul vârfurilor este finit, se va obţine C, un C-
drum închis relativ la x în R.
72
Considerând sensul invers de parcurgere pe C se obţine un C-drum C ' , închis relativ la
x* .
Deoarece a(C) ≥ 0 din ipoteză, iar a( C ' ) = – a(C), rezultă, din necesitatea teoremei ( x*
este de cost minim), că a(C) = 0.
Modificând fluxul x* cu δ( C ' ) pe C ' , unde
d(C ') = min{ min x kj - x*kj , min x *jk - x jk }
kj direct în C ' kj invers în C '
se obţine un flux x ' cu
v(x ') = v(x * ) = v , a(x ') = a(x* )  d(C ') �
a(C ') = a(x * ) ,
deci de cost minim, dar, cu D (x, x ') < D(x, x * ) , contradicţie.
Deci D (x, x* ) = 0 .
Teoremă.
Dacă x este un flux de valoare v şi de cost minim iar P0 este un drum de creştere, astfel
încât
a(P0 ) = min{a(P) P drum de creştere relativ la x}
atunci x1 = x �r(P0 ) este un flux de valoare v(x1 ) = v  r(P0 ) şi de cost minim.
Linia demonstraţiei este următoarea: presupunând prin reducere la absurd că x1 nu este
de cost minim, atunci x1 admite un C – drum închis C de cost negativ. Cum x era flux de cost
minim rezultă că E(C) ǹ E(P0 ) 0/ .
Dacă ij �E(C) �E(P0 ) , atunci va rezulta că P0 �C - ij conţine un drum de creştere
relativ la x de cost mai mic decât P0 .
Un drum de creştere de cost minim poate fi depistat cu ajutorul algoritmilor de drum
minim.
Dacă x este un flux în R şi a : E � R este funcţia de cost atunci considerând a ij = �
dacă ij �E (caz în care x ij = 0 ), construim
a ij ,
� x ij < cşi
ij x ji =0


�min{a ij , -a ji }, x ij < cşi
ij x ji >
0
aij = �
�-a ji , x ij = cşi
ij x ji >
0
� xij = cşi
�,
� ij x ji =
0

Un drum de pondere minimă de la s la t în raport cu ponderile aij corespunde unui drum


minim de creştere în R relativ la fluxul x.
Un circuit de pondere negativă în raport cu ponderile aij corespunde unui C – drum
închis în R relativ la x, de cost negativ.
Rezultă, următorul algoritm pentru rezolvarea problemei fluxului de cost minim.
Algoritm generic de rezolvare a problemei fluxului de cost minim
{
0: Se consideră x = ( x ij ) un flux cu valoarea v ' �v ;
{x poate fi fluxul nul sau un flux y determinat cu
ajutorul algoritmului de flux maxim şi apoi

73
v
considerând x = y}
v(y)
1: while ( $ circuite de pondere < 0 relativ la aij ) do
{ determină un astfel de circuit;
modifică fluxul pe acest circuit
}
2: while v(x) < v do
{ aplică un algoritm de drum minim în raport cu
ponderile aij pentru depistarea unui C – drum
P de cost minim;
x � x �min(r(P), v - v(x))
}
}

Complexitatea pentru pasul 2 este O( n 3 v ), dacă se pleacă de la fluxul nul. Se poate


dovedi că pasul 1 se poate implementa astfel ca numărul iteraţiilor să fie O( nm 2 log n ).
În continuare se prezintă o nouă descriere a algoritmului lui Ford şi Fulkerson şi se aplică
acest algoritm pe un exemplu.

6.3. Algoritmul Ford-Fulkerson


Algoritmul Ford-Fulkerson (denumit astfel după L.R. Ford, Jr şi D.R. Fulkerson)
calculează fluxul maxim într-o reţea de fluxuri. A fost publicat 1956.
Ideea algoritmului este foarte simplă: Atâta timp cât există un drum de la sursă la ţintă, cu
capacităţi disponibile pe toate muchiile drumului, vom trimite flux pe unul din aceste drumuri. Se
găseşte, ulterior, un astfel de drum şi aşa mai departe. Un drum cu capacităţi disponibile se
numeşte drum de creştere.
Algoritmul
Se dă un graf G = (V,E), având capacitatea c(u,v) şi fluxul f(u,v)=0 pentru muchia de la u
la v. Vrem să găsim fluxul maxim de la sursa s la ţinta t. După fiecare pas al algoritmului se
menţin următoarele:
■ f ( u , v )  c( u, v ) . Fluxul de la u la v nu depăşeşte capacitatea.
■ f ( u , v ) = - f ( v, u ) . Se menţine fluxul net între u şi v. Dacă în realitate a unităţi trec de
la u la v, iar b unităţi trec de la v la u, menţinem f ( u, v ) = a - b şi f ( v, u ) = b - a .
■  f ( u, v ) = 0  f ( u ) =
v
in f out ( u ) . Pentru toate nodurile u, exceptând s şi t. Fluxul ce intră

într-un nod trebuie să fie egal cu cel ce iese din nodul respectiv.
Aceasta înseamnă că fluxul prin reţea este ceea ce numim „flux legal” după fiecare etapă
parcursă a algoritmului. Definim reţeaua reziduală G f (V , E f ) ca fiind reţeaua cu capacitate
c f ( u , v ) = c( u , v ) - f ( u , v )
şi fără flux. A se ţine cont de faptul că nu se ştie cu certitudine că
E = Ef ,
deoarece se poate întâmpla ca, trimiţând flux pe (u, v) acesta să se blocheze (satureze), însă dă
naştere unei noi muchii (v, u) în reţeaua reziduală.
Algoritmul Ford-Fulkerson
Date de intrare: Graful G cu capacitatea c, nodul sursă s şi nodul ţintă t.
Date de ieşire: Un flux maxim f de la s la t
1. f ( u, v ) � 0 pentru toate muchiile (u, v)
2. Cât timp există un drum P de la s la t, astfel încât
c f (u, v) > 0 pentru toate muchiile ( u, v ) � P :

74
1. Găsirea c f  
( p ) = min c f ( u, v ) ( u, v ) � p
2. Pentru fiecare muchie ( u, v ) � P
1. f ( u, v ) � f ( u, v )  c f ( p )
(Se trimite flux de-a lungul drumului)
2. f ( v, u ) � f ( v, u ) - c f ( p)
(Fluxul ar putea fi „întors” ulterior)

Drumul poate fi găsit cu ajutorul, spre exemplu, metodei breadth-first search sau al
metodei depth-first search în graful G f (V , E f ) . Dacă se foloseşte cel dintâi, algoritmul se
numeşte Edmonds-Karp.
Complexitate
Fluxul maxim va fi atins în momentul în care nu vor mai fi găsite drumuri ce permit
creşterea fluxului corespunzător. Cu toate acestea, nu există certitudinea unei astfel de situaţii,
astfel că singurul lucru ce se poate garanta este acela că, odată cu parcurgerea algoritmului,
rezultatul este cel corect. În cazul în care algoritmul rulează la infinit, s-ar putea întâmpla ca
fluxul să nici nu conveargă către fluxul maxim. Dar această se poate întâmpla doar în cazul
valorilor iraţionale atribuite fluxului. Când capacităţile sunt întregi, timpul de rulare al
algoritmului Ford-Fulkerson este de ordinul O ( | E | *f ) , unde |E| reprezintă numărul de muchii
ale grafului, iar f reprezintă fluxul maxim al grafului. Acesta deoarece fiecare drum de creştere
poate fi găsit într-un timp de ordinul O ( | E |) , mărind fluxul cu o cantitate întreagă, care este cel
puţin 1.
O variantă a algoritmului Ford-Fulkerson , ce garantează finalitatea şi un timp de rulare
independent de valoarea fluxului maxim, este algoritmul Edmonds Karp, a cărui timp de rulare
este de ordinul O(| V || E |2 ) .
Exemplu
Următorul exemplu indică primii paşi ai algoritmului Ford-Fulkerson într-o reţea cu 4
vârfuri, sursa fiind A, iar ţinta (nodul terminal) D. Drumurile de creştere sunt găsite cu ajutorul
metodei depth-first search (căutării în adâncime), unde vecinii sunt vizitaţi în ordine alfabetică.
Acest exemplu imaginează cel mai sumbru comportament al algoritmului. La fiecare pas, este
trimis un flux având valoarea 1 de-a lungul reţelei. A se vedea că dacă s-ar fi folosit o căutare de
tipul breadth-first search, ar fi fost nevoie de doar doi paşi.

Drum Capacitate Fluxul în reţea rezultat

Fluxul iniţial în reţea

75
min(cf(A,B),cf(B,C),cf(C,D)) =
min(c(A,B) f(A,B),
c(B,C) − f(B,C),
A,B,C,D
c(C,D) − f(C,D))
= min(1000 − 0,
1 − 0,1000− 0) = 1

min(cf(A,C),cf(C,B),cf(B,D)) =
min(c(A,C) − f(A,C),
c(C,B) − f(C,B),
A,C,B,D
c(B,D) − f(B,D))
= min(1000 − 0,
0 − ( − 1),1000 − 0) = 1

După încă1998 paşi….

Reţeaua finală a fluxului

A se remarca modul în care fluxul este „împins înapoi” de la C la B, în momentul în care


se găseşte drumul A, B, C, D.

Discutii finale:
Sa se rezolve problema fluxului maxim, problema fluxului de cost minim, sa se aplice algoritmul
lui Ford-Fulkerson pentru un graf dat.
Tema propusa:
Sa se proiecteze programe utilizand algoritmul lui Ford-Fulkerson.

76
Capitolul 7
NUMĂRUL DE STABILITATE ŞI
DENSITATEA UNUI GRAF

Cuvinte cheie
Mulţimi stabile, clici.
Obiectivele invatarii
Dupa parcurgerea acestui capitol va trebui:
Sa se cunoasca notiunile de mulţimi stabile şi clici. Sa se cunoasca problema clicii, a mulţimii
independente si sa se cunoasca determinarea mulţimilor stabile maximale.

7.1. Mulţimi stabile şi clici


Definiţie. Numim mulţime stabilă în graful G o mulţime de vârfuri S �X pentru
care
[S]G = [S, 0]
/ ,
adică, x, y �S � [x, y] �U . Notăm cu P familia mulţimilor stabile în graful G. O mulţime
stabilă S care este maximală în raport cu relaţia de incluziune între mulţimi se numeşte mulţime
stabilă maximală, adică S �S' � S' �P ( S' �X ).
Numărul
a(G) = max | S |
S�P
se numeşte numărul de stabilitate al grafului G, iar o mulţime S �P cu | S | = a(G) va fi numită
sistem de stabilitate al grafului G.
Definiţie. Se numeşte clică în graful G o mulţime de vârfuri C �X care generează
un subgraf complet în graful G, adică x, y �C � [x, y] �U .
Se numeşte densitatea grafului G, numărul
w(G) = max | C | ,
C�C
unde C este familia clicilor grafului G.
Observaţii.
a) Pentru orice mulţime stabilă S �P şi orice clică C �C are loc relaţia:
| S ǣC | 1 .
b) Orice mulţime stabilă în graful G este o clică în graful său complementar G şi invers.
Avem evident:
a(G) = w(G) şi w(G) = a(G) .
Un graf G cu n vârfuri şi m muchii va fi notat cu G(n,m). Una dintre cele mai importante
probleme privind densitatea unui graf este următoarea:

77
Fie n şi k < n două numere naturale. Care este cel mai mic număr z k 1 (n) cu
proprietatea:
m �z k 1 (n) � w(G(n, m)) �k  1 ?
Pentru a răspunde la această întrebare să considerăm:
n = t� kr,
�n�
unde t = � �, 0 �r �k - 1 şi să construim graful Tnk în felul următor:
�k�
Graful T k are n vârfuri repartizate în clasele S1 ,S2 ,...,Sk cu
n
�t  1, i = 1, 2,..., r
| Si | = �
�t, i = r  1,..., k,
iar două vârfuri diferite vor fi unite printr-o muchie dacă şi numai dacă aparţin la clase diferite.
Se constată uşor că numărul muchiilor grafului Tnk este:
k(k - 1)t 2 r(r - 1)
f k (n) =  r(k - 1)t 
2 2
k - 1 2 2 �� r
= (n - r )  �� .
2k ��2

7.2. Problema mulţimii independente


În matematică, problema mulţimii independente (independent set problem (IS)) este
recunoscută ca o problemă de teoria grafurilor sau/şi combinatorică. Problema mulţimii
independente este de tipul NP – completă.
Descriere
Fiind dat un graf G, o mulţime independentă reprezintă o submulţime a nodurilor grafului
considerat, noduri neadiacente. Cu alte cuvinte, subgraful indus de aceste noduri nu are muchii, ci
doar vârfuri izolate. În aceste condiţii, problema mulţimii independente cere: Fiind dat un graf G
şi un întreg k, are G o mulţime independentă de mărime cel puţin k?
Problema de optimizare corespunzătoare este cunoscută sub numele de problema
mulţimii independente maxime, care încearcă să găsească cea mai extinsă mulţime independentă
dintr-un graf. Odată găsită soluţia problemei decizionale, se poate face uz de căutarea binară
pentru a rezolva problema iniţială, apelarea soluţiei fiind de ordinul O(log V ) . Se cunoaşte
faptul că pentru această problemă nu avem un algoritm de aproximare al factorului constant dacă
P  NP .
Algoritmi
Cel mai simplu algoritm pentru mulţimile independente examinează fiecare submulţime
de vârfuri de mărime cel puţin k, verificând dacă este independentă sau nu. Acest lucru implică
un timp de ordin polinomial dacă acest k egalează numărul vârfurilor, sau dacă este mai mic cu o
unitate ca acesta, dar nu dacă reprezintă jumătate din numărul vârfurilor.
O problemă mult mai uşor de rezolvat este aceea a găsirii unei mulţimi independente
maximale, care să nu fie conţinută în nici o altă astfel de mulţime independentă. Pornim de la un
singur vârf. Găsim un vârf neadiacent celui considerat iniţial şi-l adăugăm mulţimii respective,
apoi găsim un alt nod neadiacent niciunui vârf menţionat anterior ş.a.m.d. până când nu mai
găsim astfel de noduri. La acel moment mulţimea este maximal independentă. Se cunosc
algoritmi mult mai complecşi, de listare a tuturor mulţimilor independente maximale, dar, în
general, numărul unor astfel de mulţimi poate fi foarte mare.
78
Demonstraţia NP – complet
Se poate observa uşor că problema este de tipul NP (aparţine clasei NP), ţinând cont de
faptul că, dacă avem o submulţime de vârfuri, putem verifica dacă există muchii între oricare din
două vârfuri într-un timp polinomial. Pentru a arăta că problema este de tipul dificultate – NP
(NP – dificil), vom folosi o „reducere” a unei alte probleme de tipul NP – complet.
Presupunem că se cunoaşte deja rezultatul lui Cook, conform căruia problema satisfacerii
booleene este NP – completă. Orice formulă booleană poate fi redusă, în mod eficient, la forma
sa normală conjunctivă (conjunctive normal form CNF). În forma normală conjunctivă:
■ Formula este o conjuncţie (and ( şi)) de propoziţii.
■ Fiecare propoziţie reprezintă o disjuncţie (or(sau)) de literali.
■ Fiecare literal reprezintă fie o variabilă fie negaţia acesteia.
Spre exemplu, următoarea formulă constituie o formă CNF, unde ~ denotă negaţia:
( x1 sau ~ x2 sau ~ x3 ) şi ( x1 sau x2 sau x4 )
O astfel de formulă este satisfăcătoare dacă putem atribui valori adevărat / fals fiecărei
variabile în parte astfel încât cel puţin un literal din fiecare propoziţie să aibă valoarea de adevăr
adevărat. Spre exemplu, orice atribuire a lui x2 cu valoarea de adevăr fals, respectiv a lui x4
cu valoarea de adevăr adevărat satisface formula mai sus amintită.
În cele ce urmează, se va prezenta o reducere de tipul mai mulţi - la unul (timp -
polinomial), de la CNF- la problema mulţimii independente. Mai întâi, se ataşează câte unu nod
pentru fiecare literal din formulă; se includ duplicate ale vârfurilor pentru apariţii multiple. Se
trasează o muchie între:
1. Oricare doi literali, fiecare reprezentând negaţia celuilalt.
2. Oricare doi literali, ce se află în aceeaşi propoziţie.
Astfel, în exemplul de mai sus, x2 ar fi adiacent cu ~ x2 , primul x1 ar fi adiacent cu ~
x2 , iar cel de-al doilea x1 ar fi adiacent cu x4 .

Graful rezultat în urma reducerii, pentru exemplul de mai sus


Rămâne de văzut dacă acest graf are o mulţime independentă de mărime cel puţin k, unde
k reprezintă numărul propoziţiilor, dacă şi numai dacă formula rămâne satisfăcătoare.
Să presupunem că avem o atribuire ce satisface formula iniţială. În aceste condiţii putem
alege un literal din fiecare propoziţie, care devine adevărată prin atribuirea acestei valori. Această
mulţime este independentă, deoarece include un literal din fiecare propoziţie (nu există muchii de
tipul 2), şi deoarece nici o atribuire nu transformă atât literalul cât şi negaţia acesteia propoziţii
adevărate(nu există muchii de tipul 1). Pe de altă parte, însă, presupunând că avem o mulţime
independentă de mărime k, sau mai mare; nu poate conţine oricare doi literali în aceeaşi
propoziţie, odată ce acestea constituie perechi adiacente. Dar, ţinând cont de faptul că există cel
puţin k noduri şi k propoziţii, trebuie să avem cel puţin unul în fiecare propoziţie (de fapt exact
1). De asemenea nu se poate întâmpla să coexiste un literal şi negaţia sa, deoarece există muchii
între acestea. Această înseamnă că este uşor să alegem o atribuire astfel încât toate cele k
propoziţiile să devină adevărate, această atribuire satisfăcând formula iniţială. Ceea ce face ca
reducerea la mulţimea independentă să fie atât de simplă este capacitatea muchiilor de a exprima,
în graf, constrângerile, dar şi necesitatea de a nu alege simultan un literal şi negaţia sa. Problema
colorării grafurilor „beneficiază”, de asemenea, de această proprietate.
79
7.3. Problema clicii
În teoria complexităţii computaţionale, problema clicii este o problemă teoretică în
grafuri, de complexitate NP. Problema se numără printre problemele iniţiale, de complexitate NP,
prezentate de către Richard Karp în cadrul seminarului din 1972 intitulat „Reductibility Among
Combinatorial Problems”. Această problemă a fost, de asemenea, menţionată şi în articolul lui
Cook, o introducere în teoria problemelor NP - complete.

Graf conţinând clică de mărime 3


Clica într-un graf reprezintă o mulţime de vârfuri adiacente perechi, adică subgraful indus,
care este un graf complet.
În cele din urmă, problema clicii constă în determinarea existenţei unei clici într-un graf, a
cărei mărime să fie cel puţin k (ordin predefinit). Odată identificate k sau mai multe vârfuri ce
formează o clică, este trivial să demonstrăm acest lucru, fapt ce-i justifică complexitatea NP.
Problema corespunzătoare de optimizare, problema clicii maxime, constă în găsirea celei mai
mari clici din graf.
Complexitatea NP a problemei clicii derivă din complexitatea NP a problemei mulţimii
independente, deoarece există o clică de mărime k (cel puţin) dacă şi numai dacă există o mulţime
independentă de mărime k (cel puţin) în graful complementar. Acest lucru este uşor de observat,
ţinând cont de faptul că dacă un subgraf este complet, atunci subgraful complementar nu are
muchii.
Algoritmi
Un algoritm „primitiv” de găsire a clicilor într-un graf constă în examinarea fiecărui
subgraf ce conţine cel puţin k vârfuri, şi îndeplinirea condiţiei de formare a unei clici. Algoritmul
este polinomial (complexitate polinomială) dacă acel k coincide cu numărul vârfurilor, sau cu o
constantă mai puţin, dar nu dacă k este, să spunem, jumătate din numărul total al vârfurilor.
Numărul total al clicilor de mărime k a unui graf de mărime |V| este egal cu
� | V |� | V |!
� �= .
�k � k!(| V | -k)!
În mod euristic am începe prin considerarea fiecărui nod ca fiind o clică, iar mai apoi să
se unească aceste clici în altele mai mari până când acest lucru nu mai este posibil.
Două clici A şi B pot fuziona dacă fiecare vârf din clica A este adiacent fiecărui vârf din
clica B. Aceasta presupune un cost, în ceea ce priveşte timpul, liniar (liniar în numărul
muchiilor), dar poate eşua în găsirea unei clici mari, întrucât două sau mai multe părţi a clicii
mari vor fi fost fuzionat anterior, prin intermediul unor vârfuri care nu aparţin clicii.
Se găseşte, totuşi, cel puţin o clică maximală, care nu este conţinută în nici o altă clică
mai mare.
Unele cazuri mai speciale, pot fi rezolvate într-un timp mai scurt decât cel exponenţial.
Pentru k = 3, algoritmul are o complexitate O(n1,41 ) , unde n reprezintă numărul muchiilor.

7.4. Determinarea mulţimilor stabile maximale

80
Fie G = [X,U] un graf simplu cu mulţimea de vârfuri X = {x1 , x 2 ,..., x n } . Notăm cu
S n �S familia mulţimilor stabile maximale ale grafului G. Există mai mulţi algoritmi pentru
determinarea familiei Sn. În continuare vom prezenta algoritmul lui Bednarek şi Taulbee. Pentru
acesta să punem pentru 1 �k �n :
X k = {x1 , x 2 ,..., x k },
G k = [X k ]G ,
X k 1 = {y | y �X k , [y, x k 1 ] �U},
şi fie S k familia mulţimilor stabile maximale ale grafului G k .
Algoritmul B – T. (Bednarek şi Taulbee)
1. Se consideră X1 = {x1}, S1 = {x1} (k = 1).
2. Se determină familia I k = {T | T = S � Yk  1, S � Sk } şi familia I 'k a
mulţimilor maximale faţă de incluziune din I k .
3. Se determină familia S* după cum urmează:
k 1
Pentru fiecare S � Sk punem

S �{x k  1} � S*k  1 , dacă S � Yk  1


sau dacă S � / Yk  1 punem S � S*k  1 şi {x k  1} �(S � Yk  1) � S*k  1 atunci şi numai atunci
când S � Yk  1 � I 'k .
Familia S* conţine numai mulţimile specificate mai sus.
k 1
4. Se determină familia Sk  1 a mulţimilor maximale din S* .
k 1
5. Algoritmul se termină când k = n – 1.
În ultima fază obţinem familia Sn a mulţimilor stabile maximale în graful Gn = G. Pentru
justificarea acestui algoritm demonstrăm următoarea afirmaţie:
Teoremă.
Pentru fiecare k = 1, 2, …, n – 1 familia Sk+1 furnizată de algoritmul B – T., este familia
tuturor mulţimilor stabile maximale ale grafului Gk+1.
Demonstraţie.
Presupunem că Sk este familia mulţimilor stabile maximale ale grafului G k. Este suficient
să arătăm că orice element al familiei S*k 1 este o mulţime stabilă a grafului G k+1 şi că orice
mulţime stabilă maximală a acestui graf aparţine familiei S*k 1 .
Prima parte a afirmaţiei rezultă din modul de construcţie al familiei S*k 1 , deoarece S*k 1
conţine mulţimi sau submulţimi din Sk (care fiind stabile în Gk, sunt stabile şi în graful Gk+1) la
care se adaugă elementul xk+1 dacă şi numai dacă xk+1 nu este adiacent în Gk+1 cu nici unul dintre
elementele mulţimii sau submulţimii considerate.
Fie acum S o mulţime stabilă maximală a grafului Gk+1. Dacă x k 1 �S atunci S �S k şi
/ Yk 1 . Rezultă, conform pasului 3 al algoritmului (alternativa a doua) că S �S*k 1 .
S�
Dacă x k 1 �S atunci T = S - {x k 1} este stabilă în graful Gk şi T �Yk 1 .
Dacă T �S k , atunci
S = T �{x k 1} �S*k 1 .
În cazul când T �S k există o mulţime T ' �S k cu T �T ' . Avem

81
T �T '�Yk 1
şi din cauza maximalităţii lui S rezultă că
T = T '�Yk 1 , T ' �
/ Yk 1 , T '�Yk 1 �I 'k .
Deci S = (T '�Yk 1 ) �{x k 1} �S*k 1 , şi astfel, teorema este demonstrată.
Exemplu.
Să se determine familia S7 pentru graful din figură:

1. X1 = {1}, S1 = {1} (k = 1)
Y2 = {1}
2. I1 = {{1}} = I '1
3. S*2 = {{1, 2}}
4. S2 = {{1,2}}
X2 = {1,2}, S2 = {{1,2}} (k = 2)
Y3 = {1}
2. I2 = {{1}} = I '2
3. / Y3 şi {1,2} �Y3 �I '2 . Deci S*3 = {{1, 2},{1,3}} .
Avem {1,2} �
4. S3 = S*3
Y4 = {2}
2. I3 = {{2}, 0/ }
I '3 = {{2}}
3. S*4 = {{1, 2},{2, 4},{1,3}}
4. S 4 = S*4
Y5 = 0/
2. I4 = { 0/ , 0/ , 0/ , 0/ }, I '4 = {0}
/
3. S*5 = {{1, 2},{5},{2, 4},{5},{1,3},{5}}
4. S5 = {{1, 2},{2, 4},{1,3},{5}}
Y6 = {1,2,3,4,5}
2. I5 = {{1,2},{2,4},{1,3},{5}} = I '5
3. S*6 = {{1, 2, 6},{2, 4, 6},{1,3, 6},{5, 6}}
S6 = {{1, 2, 6},{2, 4,6},{1,3, 6},{5, 6}}
Y7 = {1,2,3,4,5}
2. I6 = {{1,2},{2,4},{1,3},{5}} = I '6

82
3. S*7 = {{1, 2, 6},{1, 2, 7},{2, 4, 6},{2, 4, 7},{1,3, 6},
{1,3, 7},{5, 6},{5, 7}} = S7 .
Deoarece k = n – 1 = 6, algoritmul se opreşte. Familia mulţimilor stabile maximale în
graful G considerat este S7, iar numărul de stabilitate este a(G) = 3 .
Observaţii.
a) Determinare familiei Cn a clicilor maximale în graful G revine, pe baza observaţiei b)
anterioare, la determinarea familiei mulţimilor stabile maximale în graful G (complementarul
grafului G).
b) Determinarea familiei Sn şi a familiei Cn este necesară printre altele în problema
găsirii unei acoperiri minimale a mulţimii vârfurilor unui graf cu mulţimi stabile (problema
colorării vârfurilor) şi respectiv cu clici.

Discutii finale:
Sa se determine mulţimile stabile şi clicile pentru un graf dat. Sa se determine mulţimile stabile
maximale intr-un graf dat.
Tema propusa:
Sa se proiecteze un program pentru detarminarea multimilor stabile maximale.

83
Capitolul 8
PROBLEME DE DESCOMPUNERI ÎN GRAFURI

Cuvinte cheie
Descompuneri în grafuri, descompunerea slabă a unui graf, număr cromatic.
Obiectivele invatarii
Dupa parcurgerea acestui capitol va trebui:
Sa se stie despre tipuri de descompuneri în grafuri, despre descompunerea slabă a grafurilor,
despre colorarea grafurilor, despre numărul cromatic, despre algoritmul welsh – powell, despre
polinomul cromatic.

8.1. Tipuri de descompuneri în grafuri


În acest paragraf ne referim la evoluţia descompunerii în grafuri, din care prezentăm
câteva din rezultatele, deja obţinute, despre tipuri de descompuneri în grafuri şi anume:
descompunerea în care o anumită caracteristică numerică a grafului are proprietatea de aditivitate,
descompunerea în care legea de adiacenţă între submulţimile partiţiei este cunoscută,
descompunerea în funcţie de operaţia de compoziţie, G-descompunerea şi în final,
descompunerea substituţie şi partiţionarea vârfurilor.
Fie G un graf cu mulţimea vârfurilor V(G) = V şi mulţimea muchiilor
E(O}= E.
În teoria grafurilor, o clasă foarte largă de probleme se referă la descompunerea (partiţia)
mulţimii vârfurilor V în submulţimile Xi , i �I , astfel încât proprietăţi de următoarele tipuri au
loc (Olaru, Antohe):
1) Subgraful indus [Xi ] (i �I) trebuie să aibă proprietăţi prescrise; de exemplu, problema
colorării grafurilor în care Xi trebuie să fie mulţimi stabile şi problema acoperirii grafurilor cu
clici;
2) Descompunerea în care o anumită caracteristică numerică f a grafului G trebuie să aibă
proprietatea de aditivitate, adică relaţia �f([Xi ]G ) = f(G) are loc;
i�I
3) Legea de adiacenţă între submulţimile Xi , X j ,i �j , trebuie să fie cunoscută. Apare ca o
problemă de descompunere referitoare la o operaţie de un tip oarecare, care este, esenţial, legea
de adiacenţă; o astfel de descompunere este numită descompunere în funcţie de un tip de
operaţie.
Apar de asemenea combinaţii ale tipului 1) şi 3); în astfel de descompuneri, subgrafurile
[Xi] trebuie să satisfacă nişte condiţii (de indecompozabilitate, coindecompozabilitate). Există de
asemenea probleme care sunt combinaţii ale tipurilor 1) şi 2) şi acestea sunt în acelaşi timp
impuse fiecărui subgraf. Aici este inclusă problema grafurilor perfecte.

8.1.1. Descompunerea de tip 2


Rezultatele următoare au fost obţinute de Olaru.
Propoziţia 1.
Orice graf care admite o clică drept cutset este a - decompozabil.
Propoziţia 2.

84
Grafurile a - critice nu au cutset care să fie clică.
Propoziţia 3.
Dacă G este un graf a - decompozabil atunci există o a - descompunere în
a
componente - indecompozabile care sunt tare stabile.
Teorema 1.
Pentru un graf G cu a (G) �2, următoarele afirmaţii sunt echivalente:
(i) G este minimal imperfect (adică este minimal critic);
(ii) G este minimal tare stabil;
(iii) G este minimal cu: c(G - x) < c(G), "x �V(G) .
{(i) echivalent (ii): E. Olaru, (i) echivalent (iii): W. Wessel }.
Propoziţia 4.
Un graf G este perfect dacă şi numai dacă singurele subgrafuri a - indecompozabile
ale lui G sunt clici.
Propoziţia 5.
Un graf G este perfect a - decompozabil dacă şi numai dacă el conţine un graf parţial a -
critic şi perfect a - decompozabil.
Propoziţia 6.
Un graf G a - critic este perfect a - decompozabil dacă şi numai dacă componentele sale
conexe sunt clici.
Remarcăm că orice graf admite cel puţin un graf parţial a - critic. Un criteriu de
decompozabilitate este următorul.
Propoziţia 7.
Un graf G este a - decompozabil dacă şi numai dacă el are cel puţin un graf parţial a -
critic decompozabil.

8.1.2. Descompunerea de tip 3


Descompunerea de tip 3 înseamnă descompunerea mulţimii vârfurilor V în submulţimile
Xi , i �I astfel încât "i, j ι I, i j , Xi şi X j sunt total adiacente în G sau G (adică Xi ~ X j în
G sau G ).
Acest tip de descompunere coincide cu descompunerea cu respectarea operaţiei graf-
adiacenţă (X-join), introdusă de Sabidussi. O astfel de descompunere se numeşte S-
descompunere, iar factorii Xi , i �I , S-componente.
Orice graf admite o S-descompunere în care toate S-componentele au numai un element,
numită banală.
Definiţie. Un graf G se numeşte S-indecompozabil dacă el admite numai S-
descompunerea banală, altfel se numeşte S-decompozabil.
Observăm că un graf decompozabil poate fi prezentat în diverse forme ca S-
descompunere de subgrafuri indecompozabile. Este, deci necesar a considera altfel S-
descompunerea, proprie (Olaru, Antohe).
Definiţie. O S-descompunere în subgrafuri indecompozabile este proprie dacă ea conţine
un număr minim de S-componente indecompozabile banale.
Teorema 2. (Olaru, Antohe).
Orice graf finit decompozabil admite o S-descompunere proprie, unică până la un
izomorfism.
Definiţie. Un subgraf [A] a unui graf G este numit coindecompozabil dacă A este
omogenă şi este maximală cu această proprietate.
Teorema 3. (Olaru, Antohe).

85
Orice graf (finit sau nu) admite o unică S-descompunere în care S-componentele sunt
coindecompozabile.

8.1.3. Descompunerea în funcţie de compoziţie


Această descompunere este în funcţie de operaţia booleană numită compoziţie, care a fost
introdusă de Harary şi investigată de Sabidussi, care se mai numeşte şi produs lexicografic.
Reamintim definiţia acestei operaţii. Date fiind două grafuri G 1 = (V1,E1) şi G2 = (V2,E2),
compoziţia, notată G = G 1[G2] se defineşte astfel: V(G) = V1 �V2 şi pentru
"u = (u1 , u 2 ) ι V1 V2 , "v = (v1 , v 2 ) ι V1 V2 , uv �E(G) dacă şi numai dacă u1v1 �E1 sau (
u1 = v1 şi u 2 v 2 �E 2 ).
Rezultatele următoare se găsesc în (Golumbic, Shamir).
Fie H0 un graf neorientat cu n vârfuri v1 ,..., v n şi fie H1 ,..., H n n grafuri disjuncte.
Graful compoziţie H = H0 [H1 ,..., H n ] este format astfel.
Pentru i, j = 1, …, n, înlocuim vârful v i din H0 cu graful Hi şi luăm fiecare vârf din Hi
adiacent la fiecare vârf din Hj ori de câte ori vi este adiacent lui vj în H0.
Formal, pentru Hi = (Vi,Ei) definim H = (V,E) astfel:
V = U Vi
i �1
şi
E = U Ei �{xy | x �Vi , y �Vj şi v v �E } .
i j 0
i �1
Mai notăm E = E 0 [E1 ,..., E n ] .
H0 se numeşte factor extern a lui H, Hi, i = 1, …, n se numesc factori interni ai lui H.
Un graf neorientat G = (V,E), care prin orientarea fiecărei muchii obţinem graful orientat
(V,F) care satisface următoarea condiţie: dacă ab �F şi bc �F implică ac �F ( "a, b, c �V ), se
numeşte graf de comparabilitate (Golumbic, Shamir).
Teorema 4.
Fie G = G 0 [G1 ,..., G n ] , unde Gi sunt grafuri neorientate disjuncte. Atunci G este graf de
comparabilitate dacă şi numai dacă Gi i = 0, l, ..., n este graf de comparabilitate.
Definiţie. Un graf se numeşte decompozabil dacă el poate fi exprimat ca o
compoziţie nebanală de subgrafuri induse; altfel, el se numeşte indecompozabil.
Pentru orice graf există compoziţia banală: G = K1[G].
Formal, G = (V,E) este decompozabil dacă există o partiţie V = V1  ...  Vn a vârfurilor
în submulţimi nevide disjuncte două câte două cu 1 < r < |V| astfel încât G = G R [G V ,..., G V ]
1 v

pentru orice mulţime de reprezentanţi R = {x1 ,..., x n } , xi din Vi. O astfel de partiţie induce o
descompunere proprie a lui G.
G are o mulţime partitivă nebanală dacă şi numai dacă G este decompozabil.

8.1.4. G-descompunerea
Un graf orientat este o pereche G = (V,E), unde V este o mulţime nevidă, iar E este o
mulţime de perechi ordonate de elemente distincte ale lui V. Elementele lui E se numesc arce.
Pentru un graf orientat G = (V,E), notăm cu G -1 = (V, E -1 ) , graful invers, unde
E -1 = {(x, y) | (y, x) �E} .

86
Fie G = (V,E) un graf. Un graf orientat H =(V,F) (având aceeaşi mulţime de vârfuri ca şi
G) cu proprietatea că F �F-1 = 0/ şi F �F-1 = E se numeşte orientarea grafului G. (H se obţine
asociind o orientare fiecărei muchii a lui G).
În acest paragraf prezentăm un algoritm pentru G - descompunere.
Fie R o relaţie binară pe mulţimea muchiilor unui graf neorientat G = (V,E), definită
astfel:
abRxy dacă şi numai dacă ori a = x şi by �E ori b = y şi ax �E.
Închiderea reflexivă şi tranzitivă R' a lui R defineşte o relaţie de echivalenţă pe E, ale
cărei clase de echivalenţă se numesc clase de implicaţii. Dacă A este o clasă de implicaţie atunci
 = A �A -1 este închiderea simetrică a lui A.
ˆ  ...  B
Pentru G = (V ,E), un graf, o partiţie a lui E în k mulţimi: E = B ˆ se numeşte G-
1 k
descompunere dacă Bi sunt clase de implicaţii din graful G = (V, B ˆ B ˆ ˆ
i i 1  ...  Bk ), "i = 1, k .
O secvenţă de muchii [x1 y1 ,..., x n y n ] se numeşte schemă de descompunere pentru G
ˆ  ...  B
dacă există o G-descompunere E = B ˆ astfel încât x y �B , i = 1, k .
1 k i i i
Are loc următorul algoritm de descompunere:
Input: Un graf G = (V,E).
Output: O G-descompunere; Un număr k;
ˆ ,..., B
k mulţimi de muchii din G: B ˆ
1 k

begin
fie E1 := E
i := 1
loop:
Alege arbitrar o muchie ei = x i yi � Ei .
Determină clasele de implicaţii Bi din Ei conţinând xiyi
ˆ
Fie E i  1 := Ei - B i
If E i  1 = 0/ then
Fie k := i
stop
else
Incrementează i cu 1
go to loop
end if

end

8.1.5. Descompunerea substituţie şi partiţionarea vârfurilor


Un graf conex, nenul, care nu conţine vârfuri separatoare se numeşte bloc (Behzad,
Chartrand, Foster). Un bloc al unui graf G este un subgraf indus al lui G care este el însuşi un
bloc şi care este maximal cu respectarea acestei proprietăţi.
Operaţia numită partiţionarea vârfurilor este definită în cele ce urmează.
Presupunem că sunt date un graf G = (V,E) şi o partiţie iniţială P a lui V în blocurile
disjuncte B1 ,..., Bk . Problema partiţionării grafului cere a găsi partiţia având puţine blocuri a lui
V, fie P' = (E1, ..., Ej), astfel încât:
1) Fiecare Ei este o submulţime a unui Bh;
2) x, y �Ei implică N(x) - E i = N(y) - E i .

87
Condiţia a doua poate fi reformulată astfel. Fiecare bloc Ei verifică: pentru fiecare i ori x ~
Ei ori x ~/ Ei pentru "x �V - Ei , adică Ei este modul.
R. McConnell şi Spinrad au descoperit un algoritm O(m+n) pentru partiţionarea
vârfurilor.
Vârful v separă (splitează) un bloc B ori de cate ori v este utilizat pentru a divide B în
blocurile B �N(v) şi B - N(v) .
Descompunerea modulară (Jamison, Olariu) sau descompunerea substituţie (descoperită
independent de mai mulţi autori printre care R. H. Mohring şi F. J. Radamacher) înseamnă
partiţionarea unui graf G în subgrafuri, fiecare din ele este un modul într-un subgraf al lui G.
În particular, graful însuşi şi mulţimile cu un singur vârf sunt considerate module.
Algoritmul următor partiţionează un graf în O(mlogn) timp. Algoritmul alege un vârf v
care ori
i) nu a fost folosit înainte ca element de splitare ori
| B' |
ii) v ι B, | B | , B' care a conţinut v ultima dată când v a fost ales ca element de
2
separare ori
iii) este într-un bloc care nu va fi separat în viitor.
Vârful v este folosit să separe fiecare bloc care nu conţine v. Separarea cauzată de v poate
fi executată în O(|N(v) |).
Pentru a separa pe B, prima dată verificăm dacă v nu este în B; apoi loca1izăm vecinii lui
v într-un nou bloc B' , care este păstrat "legat" de B până la sfârşitul separării cauzată de v.
Dacă nici un vârf nu satisface criteriul i) sau ii) pentru alegerea elementului de separare,
orice vârf din cel mai mare bloc activ C rămas, va satisface criteriul iii). Orice alt vârf v a încercat
să separe C, deoarece v a fost separat de C la un moment dat şi este acum într-un bloc care este
cel mult de lungimea lui C.
Algoritmul propriu-zis:
Fie partiţia iniţială ( B1,..., B j );
Pentru fiecare bloc Bi execută
lastused[Bi] = cea mai mare valoare;
{ folositultimadata[Bi] = cea mai mare valoare}
READY = (B1,..., B j);
Cât timp (G este nevid) execută
Cât timp (READY � 0/ ) execută
C = orice bloc din READY;
lastused[C] = |C|;
pentru fiecare vârf x din C execută
separă toate blocurile exceptând C în funcţie de
adiacenţa la x;

pentru fiecare bloc B separat în B, B2 prin x execută


lastused[B2] = lastused[B];
dacă |B| � lastused[B]/2 atunci
adaugă B la READY;
dacă |B2| � lastused[B2]/2 atunci
adaugă B2 la READY;
C = cel mai mare bloc rămas;
returnează C ca parte a partiţiei finale;
foloseşte fiecare vârf din C pentru a separa celelalte blocuri;
G = G – C.

Prezentăm în continuare un algoritm de complexitate O(mlogn) dat de McConnell pentru


găsirea descompunerii substituţie a unui graf. Această metoda de descompunere se bazează pe

88
algoritmul de partiţionare de mai sus. Algoritmul lucrează în două faze. Prima fază găseşte o
submulţime de module din arborele de descompunere utilizând partiţionarea şi produce o ordine
pe vârfuri astfel încât celelalte module vor fi găsite (specificate) prin alegerea diferitelor vârfuri
ca elemente de separare.
Descriem procedura de bază. Ori de câte ori procesul de partiţionare returnează o mulţime
S de lungime mai mare decât 1, S este modul al lui G. Alegem orice vârf s din S, împărţim S în s
şi S – s şi reîncepem procedura de partiţionare. Păstram un arbore a mulţimii partiţiilor care se
produc în timpul acestei proceduri. Iniţial, arborele are o rădăcină, s ca descendentul direct stâng
şi V – s ca descendentul direct din dreapta. Ori de cate ori o mulţime este separată în vecini şi
nevecini, luăm un nou nod intern cu vecinii drept descendentul direct stâng şi nevecinii drept
descendentul direct drept. Dacă S a fost specificat (menţionat, găsit) ca a fi un modul, marcăm
nodul intern corespunzător lui S.
Notăm că această procedură va găsi modulele care nu conţin un vârf arbitrar ales s, dar nu
va găsi modulele conţinând s. Acestea vor fi găsite de faza a doua. Ideea este de a ordona
vârfurile astfel încât întotdeauna alegem vârfuri pentru separare pentru faza a doua care nu apar
împreună cu s într-un submodul. Numărăm vârfurile din timpul traversării standard postordine, în
care mulţimea descendenţilor copilului (descendentului direct) drept dintr-un nod dat va fi mai
mare decât a descendenţilor nodului stâng al aceluiaşi copil (descendent direct).
Faza a doua a procedurii este aceeaşi cu prima, excepţie fiind atunci când avem un modul
S, întotdeauna alegem un vârf x cu cel mai mare număr din S cu respectarea procedurii de
ordonare din prima fază, separând S în x şi S – x şi continuăm ca mai sus. Timpul total luat
pentru găsirea celui mai mare număr de vârfuri este O(nlogn), astfel se termină cele două faze în
O(mlogn) timp.
Arborele de descompunere este produs prin combinarea modulelor găsite în timpul celor
două faze.
Fie un modul M. Daca există un descendent direct c al lui M care nu formează muchii cu
nici un alt descendent direct al lui M din G, ştergem c din M şi adăugăm un nod etichetat P cu c
drept un descendent direct şi M drept celălalt descendent direct. Similar, dacă există un
descendent direct c ' al lui M, extremitate a tuturor muchiilor cu toţi ceilalţi descendenţi direcţi ai
lui M, ştergem c' din M şi adăugăm un nod S cu c' drept un descendent direct şi M drept celălalt
descendent direct. Aceasta ia O(n+m) timp. Unim arborii găsiţi în fiecare din cele două stagii
astfel. Traversăm arborele în ordinea primul în adâncime. Dacă întâlnim un nod serie (care
corespunde unui modul M astfel încât [M]G este neconex) sau paralel (care corespunde unui
modul astfel încât [M]G este neconex) x, care are un ascendent direct p de acelaşi tip, luăm toţi
descendenţii direcţi ai lui x descendenţi direcţi ai lui p.
În final, combinăm modulele găsite în timpul fiecăreia din cele două faze într-un singur
arbore de descompunere, T. Fie T1 şi T2 arborii găsiţi în timpul celor două faze. Fie toate nodurile
lui T1 şi T2 în ordine nedescrescătoare a numărului de descendenţi direcţi vârfuri pendante. Când
un modul M este întâlnit, adăugăm M dacă M nu este deja parte a arborelui combinat. Pentru a
cunoaşte dacă un modul M cu k descendenţi vârfuri pendante este deja în arbore este suficient a
verifica dacă cel mai mic număr de vârfuri din M este deja într-un modul de lungime k. Dacă
memorăm cel mai mic număr de vârfuri din fiecare modul şi modificăm variabila i pentru un vârf
v când v este vârful cu cel mai mic număr dintr-un modul de lungime i, acest pas ia timp constant
pe modul. Adăugarea unui modul M la T se face astfel. Orice descendent direct c al lui M se află
în T şi pentru orice rădăcină r din arborele curent care conţine un descendent direct al lui M, r
devine un descendent direct al lui M în arbore. Orice metodă naturală de a marca un arbore,
pentru ca un drum să nu fie traversat de două ori, ne permite să combinăm doi arbori în O(n)
timp.

89
La sfârşitul acestui paragraf, prezentăm următorul rezultat (Olariu).
Pentru un graf G următoarele două afirmaţii sunt echivalente:
(i) G nu are nici un subgraf indus izomorf cu grafurile cu secvenţa de grade (2,2,2,3,3),
(1,1,2,3,3), (2,2,3,3,4,4);
(ii) pentru fiecare subgraf indus H a lui G, cel puţin una din următoarele afirmaţii sunt
adevărate:
(a) H conţine o mulţime omogenă;
(b) w(H) �2 .

8.2. Descompunerea slabă a grafurilor


8.2.1. Introducere
Fie G = (V,E) un graf neorientat, fără bucle şi muchii multiple. În diverse probleme de
teoria grafurilor, cu precădere în construcţia unor algoritmi de recunoaştere apare frecvent un
anumit tip de partiţie a mulţimii vârfurilor în trei clase A, B, C astfel încât A să inducă graf conex,
iar C să fie total adiacent cu B şi total neadiacent cu A. Aşa se întâmplă, de exemplu, cu
construcţia cografurilor pornind de la K1,2 şi substituind vârfurile cu cografuri. Introducerea
noţiunii de descompunere slabă (C. Croitoru) şi studiul proprietăţilor ei ne permite să obţinem
alte rezultate de acest tip, cum ar fi: caracterizarea cografurilor cu coarbori ( rezultat cunoscut,
obţinut de Lerchs, dar pentru care obţinem demonstraţie mai uşoară). De asemenea, caracterizăm
grafurile K1,3 –free şi dăm un algoritm de recunoaştere şi un altul pentru determinarea unui cuplaj
de cardinal maxim în această clasă de grafuri. De asemenea, alte proprietăţi se obţin pentru
grafuri triangulate, garfuri {P4, C4}- free, grafuri paw – free, grafuri confidenţial conexe.
O parte din acest capitol a fost prezentat la ROSYCS 2000 Iaşi.

8.2.2. Descompunerea slabă a unui graf


Înainte de a da definiţia centrală din acest capitol, reamintim că pentru un graf G=(V,E) şi
A �V , am notat:
N G ( A) = {v | v �V - A, $w �A si v ~ w}
N G [ A] = A �N G ( A)
şi
N G ( A) = V - N G [ A] .
(Dacă nu sunt posibile confuzii, indicele G poate fi omis).
Fie G1 = (V1 , E1 ) şi G2 = (V2 , E2 ) , două grafuri oarecare. Notăm cu G1+G2, K2 - adiacenţa
(K2 – join) a grafurilor G1 şi G2, adică, graful obţinut din K2 prin substituţia vârfurilor sale cu G1
şi G2 şi orice vârf din G1 este adiacent cu orice vârf din G2. Graful G1+G2 va fi numit suma
grafurilor G1 cu G2.
Definiţia 1. Fie G=(V,E) un graf. O mulţime de vârfuri , A, se numeşte mulţime slabă
dacă N G ( A) �V - A şi subgraful indus de A este conex. Dacă A este mulţime slabă, maximală în
raport cu incluziunea, subgraful indus de A se numeşte componentă slabă. Pentru simplificare,
componenta slabă G(A), se va nota cu A.
Denumirea de componentă slabă este justificată de următorul rezultat.
Propoziţia 2.
Orice graf conex şi incomplete G=(V,E) admite o componentă slabă A astfel încât
G (V - A) = G ( N ( A))  G( N ( A)) .
Demonstraţie. Deoarece graful G este incomplet, a (G ) �2 , există vârfurile x �y ,
neadiacente. Fie

90
A0 = {x}; B0 = N ( x); C0 = N ( x) .
Avem y �C0 . Dacă N ( x) ~ N ( x) atunci A = A0 . Dacă nu, fie x1 �N ( x ), y1 �N ( x) astfel
încât x1�y1 .
Pentru A1 = A0 �{x1},[ A1 ] este conex, deoarece [ A0 ] este conex şi x1 �N ( x) .
N ( A1 ) = ( N ( A0 ) - {x1}) �( N ( x1 ) �C0 ). y1 �N ( A1 )
(deoarece x1�y1 şi y1 �N ( x) şi y1�A0 ).
Deci
N ( A1 ) ��.
Dacă
N ( A1 ) ~ N ( A1 )
atunci [V - A1 ] = [ N ( A1 )]G  [ N ( A1 )]G . Presupunem că s-a construit Ai , Bi=N(Ai) , Ci = N ( Ai ) .
Dacă Bi ~ Ci atunci A = Ai. Dacă nu, fie xi 1 �Bi şi yi 1 �Ci cu xi 1�yi 1 .
Notăm
Ai 1 = Ai �{xi 1}; Bi 1 = N ( Ai 1 ); Ci 1 = N ( Ai 1 ) .
[Ai+1] este conex, deoarece [Ai] este conex şi xi 1 �N ( Ai ) . yi 1 �N ( Ai 1 ) (deoarece
xi 1�yi 1 şi yi 1 �N ( Ai ) şi yi 1�Ai ).
Deci
N ( Ai 1 ) ��.
Dacă Bi 1 ~ Ci 1 atunci A=Ai+1 şi
[V - Ai 1 ]G = [ N ( Ai 1 )]G  [ N ( Ai 1 )]G .
Deoarece
A0 �A1 �... �Ai �... �V
şi V < � rezultă $p �N astfel încât N ( Ap ) ~ N ( Ap ) şi deci A = Ap este componenta slabă cu
proprietatea din enunţ.
Propoziţia 3.
Fie G = (V,E) un graf conex şi incomplet şi A �V . Atunci A este componentă slabă a lui
G dacă şi numai dacă G(A) este conex şi N ( A) ~ N ( A) .
Demonstraţie. Presupunem că există n �N ( A) şi n �N ( A) astfel încât nn �E (G ) .
Fie A� = A �{n} şi N � = ( N ( A) - {n}) �( N (n) �N ( A)) .
[ A�
]G este conex, N ( A� ) = N �şi V (G ) - ( A� �N ( A� ) �V (G) - A�
)) �{n } . Deci N ( A� ,
contrazicând maximalitatea lui A.
Invers.
Fie G(A) conex şi N ( A) ~ N ( A) . Arătăm că G(A) este componentă slabă. Fie A� �A ,
componentă slabă.
ƹ-�A� A N ( A) (deoarece A�N ( A) şi G ( A� ) conex).
Fie n �A� - A . Atunci N ( A) �N (n) . Deci N ( A� ) ��, contrazicând definiţia
componentei slabe.
Definiţia 4. O partiţie de forma ( A, N ( A),V - N [ A]) , unde A este mulţime slabă o numim
descompunere slabă în raport cu A a grafului G. Numim A componentă slabă, N(A) mulţime
separatoare minimală, iar V-N[A] mulţime îndepărtată (remote set).

91
Propoziţia 5.
Dacă G=(V,E) este un graf conex şi incomplet atunci mulţimea vârfurilor V admite o
descompunere slabă (A,B,C) astfel încât G(A) este componentă slabă şi G(V-A)=G(B)+G(C).
{Demonstraţia este cea dată în propoziţia 2}.
Observaţia 6. Fie G=(V,E) un graf conex şi incomplet. Dacă A este mulţime slabă atunci
A �V .
{ A mulţime slabă şi G conex rezultă A �V , altfel N G ( A) = �, V - A = �, adică N G ( A) = V - A
}.
Fie G un graf conex şi WG={A: A mulţime slabă în G}.
Observaţia 7. Fie G=(V,E) un graf conex. WG are cel puţin un element dacă şi numai
dacă G nu-i complet.
{Dacă G nu este complet atunci $ v �V (G ) astfel încât N G ( A) �V - A . Deci {v} este mulţime
slabă, adică {v} �WG . Arătăm implicaţia inversă. Presupunem că există A mulţime slabă în G.
Atunci N G ( A) �V - A . Deci N G ( A) ��. Deci $a �A$b �N G ( A) astfel încât ab �E (G ) . Deci
G nu este complet.}
Fie WG = { A | A �WG , A maximală în raport cu incluziunea }.
0

Mai numim componentele slabe ale unui graf G şi frunze. Mulţimea frunzelor o notăm cu
0
LG. Deci LG = WG .
0
Remarca 8. Fie G=(V,E) un graf conex şi incomplet. Dacă A �WG atunci A este cutset
(mulţime separatoare) în G .
{În G - A, R = N ( A) şi N sunt mulţimi nevide de vârfuri total neadiacente}
0
Remarca 9. Fie G=(V,E) un graf conex şi incomplet. Dacă A �WG atunci N G ( A) este
cutset (mulţime separatoare) în G.
{În G - A, R = N ( A) şi A sunt mulţimi nevide de vârfuri total neadiacente}
Demonstraţia dată în propoziţia 3. oferă un algoritm polinomial pentru construirea unei
descompuneri slabe pentru un graf conex şi incomplet.

Algoritm de descompunere slabă a unui graf


Input: Un graf conex şi cu cel puţin două vârfuri neadiacente, G=(V,E).
Output: O partiţie V=(A,N,R) astfel încât G(A) conex, N=N(A), A�R = N ( A) .
begin
A := o mulţime arbitrară de vârfuri astfel încât
A ȹN ( A) V
N:=N(A)
R := V - A �N ( A)
while ( $n �N , $r �R astfel încât nr �E ) do
A := A �{n}
N := ( N - {n}) �( N ( n) �R)
R := R - ( N (n) �R)
end.

Se observă că [A]G este conex, N=NG(A), R �� este un invariant al algoritmului.


0
Consecinţa 10. Dacă G este conex şi cu cel puţin două vârfuri neadiacente şi A �WG
atunci
a (G ) = max{a ([ A]G )  a ( NG ( A)), a ( N G ( A) �A)}.

92
{Într-adevăr, orice mulţime stabilă de cardinal maxim ori intersectează N G ( A) şi atunci are
cardinalul a ([ A]G )  a ( NG ( A)) ori nu intersectează N G ( A) şi atunci are cardinalul a ( N G [ A]) .}
Observaţia 11. Dacă G este un graf conex cu a (G ) = 2 atunci A este clică şi R este clică,
0
pentru orice descompunere slabă (A,N,R) a lui G cu A �WG .
{ 2 = a (G ) �a ([ A])  a ([ R ]) �1  1 = 2 .}.
În continuare reamintim următorul rezultat.
Lema 12.
Dacă G este un graf conex, C este cutset, K 1,K2,…,Kp (p≥ 2) sunt componentele conexe
ale lui G-C atunci:
(i) N ( K i ) �C , "i = 1,..., p;
(ii) C este cutset minimal dacă şi numai dacă N ( K i ) = C , "i = 1,..., p
În încheierea acestei secţiuni caracterizăm grafurile G cu proprietatea A ~ N , unde
(A,N,R) este descompunere slabă.
Fie G=(V,E) un graf, X �V şi k �N* .
Numim vecinătatea de ordin k a lui X, mulţimea
N G ( X ) =  a | a �X , $ un drum indus în G, P, de lungime k de la x �X la a astfel încât
k

V ( P) �X = {x} }.
Evident,
( A) = N G ( A) .
N G�
Lema 13.
Fie G conex şi (A,N,R) o descompunere slabă cu A �WG . Atunci N G ( R) = � dacă şi
0 3

numai dacă A ~ N .
Demonstraţie.
Fie N G ( R ) = �. Presupunem, totuşi, că $a �A şi n �N cu a�n . Deoarece [ A �{n}]
3

este conex atunci $ un drum P de lungime ≥ 2 de la a la n. Întrucât N ~ R şi R�A atunci P� ,


obţinut din P la care adăugăm muchia nr ( "r � R ) , este un drum indus în G de lungime ≥ 3.
3
Obţinem că N G ( R ) ��, o contradicţie.
Dacă A ~ N , având şi N ~ R rezultă că pentru "r �R , nu există drum indus de lungime
≥ 3 cu o extremitate în r şi restul vârfurilor în V-R. Deci N G ( R ) = �.
3

8.3. Teorema celor patru culori


Teorema celor patru culori (cunoscută şi sub numele de teorema de colorare a hărţii cu
ajutorul a patru culori) afirmă următoarele: fiind dat un plan separat în regiuni, regiunile pot fi
colorate folosind nu mai mult de patru culori, astfel încât două regiuni adiacente nu sunt colorate
cu aceeaşi culoare. Două regiuni se numesc adiacente doar dacă „împart” un segment de
„graniţă”.
Teorema celor patru culori a fost prima teoremă majoră ce a necesitat computerul pentru a
fi demonstrată, iar această demonstraţie nu este acceptată de toţi matematicienii deoarece ar fi
omeneşte imposibil de demonstrat un astfel de lucru. În ultimă instanţă, pentru a da crezare
demonstraţiei, trebuie să se încreadă în corectitudinea compilării şi a execuţiei hardware.
Istoric
Se ridică problema folosirii a cinci sau mai multe culori.
În anul 1890, Heawood a demonstrat că toate grafurile planare sunt cinci-colorabile.

93
Între 1960 şi 1970 matematicianul german Heinrich Heesch a dezvoltat metode de
utilizare a calculatorului în scopul găsirii acelei demonstraţii atât râvnite.
Nu mai devreme de anul 1976 s-a demonstrat ipoteza celor patru culori a lui, de către
Kenneth Appel şi Wolfgang Haken la Universitatea din Illinois. Au fost asistaţi, în ceea ce
priveşte algoritmica, de John Koch.
Dacă s-ar fi întâmplat ca ipoteza privind cele patru culori să fie falsă, ar fi existat cel puţin
o hartă cu cele mai puţine regiuni posibile care să necesite cinci culori pentru colorare.
Demonstraţia a arătat că un astfel de contraexemplu minimal nu poate exista.
Odată cu demonstraţia teoremei, au fost elaboraţi algoritmi eficienţi de 4-colorare a
hărţilor, necesitând doar O ( n 2 ) timp de rulare, unde n reprezintă numărul de noduri. În anul
1996, Neil Robertson, Daniel P. Sanders, Paul Seymour şi Robin Thomas au creat un algoritm cu
timpul pătratic.
În anul 2004 Benjamin Werner şi Georges Gonthier au formalizat o demonstraţie a
teoremei.
Determinarea suficienţei folosirii a trei culori în colorarea optimă a unei hărţi, este de
complexitate NP, ceea ce indică faptul că nu vom avea o soluţie „rapidă”. Determinarea
posibilităţii de 4-colorare a unui graf general (posibil ne-planar) are de asemenea complexitatea
NP.
Expunere formală în teoria grafurilor
Pentru a expune formal teorema, este mai simplu să o reformulăm în teoria grafurilor.
Teorema afirmă că vârfurile fiecărui graf planar pot fi colorate cu ajutorul a cel mult patru culori,
astfel ca două vârfuri adiacente oarecare să nu aibă aceeaşi culoare. Sau, pe scurt: „orice graf
planar este patru-colorabil”. În cazul acesta, fiecare regiune a hărţii este înlocuită cu un vârf, iar
două vârfuri sunt conectate prin intermediul unei muchii dacă şi numai dacă regiunile
corespunzătoare „împart” un segment de graniţă.
Generalizări
S-ar putea pune problema colorării suprafeţelor ce nu sunt neapărat plane. Problema
corespunzătoare sferei este echivalentă cu cea din plan. Pentru suprafeţele închise (orientate sau
neorientate), de clasă pozitivă, numărul maxim p de culori necesare depinde de caracteristica
Euler c a suprafeţelor conform formulei:
7  49 - 24 c 
p= ,
 2 
unde parantezele indică partea întreagă a funcţiei. Singura excepţie în ceea ce priveşte această
formulă o reprezintă „sticla lui Klein”, a cărei caracteristică a lui Euler are valoarea 0 şi necesită
6 culori. Aceasta a fost denumită iniţial ipoteza Heawood, fiind demonstrată ca şi Teorema
colorării hărţii de către Gerhard Ringel şi J. T. W. Youngs în anul 1968.
Alternativ, pentru o suprafaţă orientată, formula poate depinde de parametrul g (parametru
ce caracterizează clasa (genul) suprafeţei):
 7  1  48 g 
p= .
 2 

8.3.1. Colorarea grafurilor


În teoria grafurilor, colorarea grafurilor constă în atribuirea de „culori” vârfurilor unui
graf astfel ca oricare două vârfuri adiacente să nu aibă aceeaşi culoare. În mod analog, colorarea
muchiilor constă în atribuirea unei culori fiecărei muchii în parte, astfel încât oricare două
muchii incidente să nu aibă aceeaşi culoare, iar colorarea feţelor unui graf planar atribuie o
culoare fiecărei feţe in parte, ţinându-se cont de faptul că oricare două astfel de feţe, ce au o
„frontieră” comună, nu pot avea aceeaşi culoare.
94
O 3-colorare este suficientă acestui graf, însă, dacă s-ar folosi mai puţine culori ar rezulta noduri
(vârfuri) adiacente de aceeaşi culoare.
Găsirea numărului minim de culori necesare colorării unui graf oarecare are dificultate
NP

8.3.2. Colorarea vârfurilor


O colorare ce foloseşte cel mult k culori se numeşte k-colorare şi este echivalentă cu
problema partiţionării mulţimii vârfurilor în k sau mai puţine mulţimi independente. Problema
colorării grafurilor şi-a găsit aplicaţii, cum ar fi în planificarea calendaristică, înregistrarea
alocărilor în compilatoare, distribuţia frecvenţelor radiourilor mobile, respectiv compatibilitatea
modelelor.
8.3.3. Numărul cromatic
Cel mai mic număr de culori necesare colorării unui graf se numeşte numărul cromatic
c corespunzător. De exemplu, numărul cromatic al unui graf complet K n cu n vârfuri (un graf
cu o muchie între oricare două vârfuri), este c ( K n ) = n .
Un graf căruia îi poate fi atribuită o k-colorare (corespunzătoare) este k-colorabil, şi este
k-cromatic dacă numărul său cromatic este chiar k.
Problema găsirii unei colorări minime a unui graf are o dificultate-NP. Problema deciziei
corespunzătoare (există o colorare care foloseşte cel mult k culori?) are o complexitate de ordinul
NP, reprezentând, la origine, una din cele 21 de probleme ale lui Karp NP-complete. Rămâne
NP-completă chiar şi în cazul grafurilor planare de grad cel mult 4, aşa cum a fost demonstrat de
către Garey şi Johnson în 1974, chiar dacă în cazul grafurilor planare este trivială (n.
demonstraţia) pentru k>3 (acest lucru datorându-se teoremei celor patru culori). Există, totuşi,
unii algoritmi de aproximare eficienţi, care folosesc programarea semidefinită.
Proprietăţi ale c ( G ) :
1. χ(G) = 1 dacă şi numai dacă G este total neconex.
2. χ(G) ≥ 3 dacă şi numai dacă G are un ciclu impar (echivalent, dacă G nu este bipartit).
3. χ(G) ≥ ω(G).
4. χ(G) ≤ Δ(G)+1.
5. χ(G) ≤ Δ(G) pentru G conex, doar dacă nu cumva G este un graf complet sau un ciclu
impar (Teorema lui Brook).
6. χ(G) ≤ 4, pentru orice graf planar G. Acest faimos rezultat este numit Teorema celor patru
culori.
Aici, D( G ) reprezintă gradul maxim, iar w ( G ) , numărul clică.

8.3.4. Aspecte algoritmice


Colorarea optimală
Colorarea vârfurilor, în general, este o problemă NP-completă. În loc să cerem cel mai
mic număr culori necesare colorării unui graf, putem să ridicăm probleme mult mai simple, cum
ar fi „Putem colora graful cu cel mult k culori?”.
Cazul corespunzător lui k = 2 este echivalent determinării bipartiţiei, respectiv a non-
bipartiţiei grafului. Aceasta se poate realiza într-un timp de ordin polinomial. Pentru k  3

95
problema este NP-Completă. Există un rezultat remarcabil, al lui László Lovász, care enunţă că
este permis să se afirme că numărul cromatic al unui graf perfect implică timpul polinomial.

Algoritmi predefiniţi
Algoritmii de colorare pot fi divizaţi în două categorii:
 Algoritmi optimali de colorare (de exemplu, Algoritmul lui Zykov, metoda ramificării şi
mărginirii, etc. )
 Algoritmi care nu asigură un rezultat cu cele mai puţine culori posibile. În această
categorie se pot încadra algoritmii secvenţiali, algoritmii euristici, algoritmi globali
aleatori, algoritmi metaeuristici, respectiv algoritmii genetici.

8.3.5. Algoritmul Welsh – Powell


Algoritmul Welsh-Powell de colorare a grafurilor foloseşte o îmbunătăţire euristică a unui
algoritm greedy. Se demonstrează uşor că o astfel de abordare foloseşte cel mult D( G )  1 culori,
unde D ( G ) este gradul maxim al grafului. Algoritmul este următorul:
1. Sortarea nodurilor în ordine descrescătoare a gradului, iniţial considerându-se
toate nodurile necolorate.
2. Traversarea (Parcurgerea) nodurilor în ordine, atribuindu-i unui nod culoarea 1
dacă este necolorat şi nu are vecini coloraţi cu aceeaşi culoare.
3. Se repetă acest proces şi în cazul culorilor 2, 3, etc. până când toate vârfurile
vor fi fost colorate.
Acest algoritm nu găseşte neapărat o colorare c ( G ) .

8.3.6. Polinomul cromatic


Polinomul cromatic contorizează numărul posibilităţilor de colorare a unui graf ,
utilizând un număr prestabilit de culori.
Polinomul cromatic este o funcţie P ( G, t ) ce contorizează numărul t-colorărilor lui G.
Aşa cum indică şi numele, pentru un graf G dat funcţia este într-adevăr polinomială în t.
Polinomul cromatic conţine cel puţin la fel de multe informaţii legate de colorabilitatea
lui G ca şi numărul cromatic.
Într-adevăr, c este cel mai mic întreg pozitiv care nu este rădăcină a polinomului
cromatic,
c ( G ) = min k : P( G, k ) > 0
A fost folosit pentru prima dată de către Birkhoff şi Lewis în demersul lor împotriva
teoremei celor patru culori.
A rămas o problemă nerezolvată, în ceea ce priveşte eficienţa caracterizării grafurilor ce
au acelaşi polinomul cromatic.

Exemple

Graful Petersen are numărul cromatic 3


Polinomul cromatic pentru grafurile

96
K3 t(t-1)(t-2)
Graful complet Kn t(t-1)(t-2)...(t-n+1)
Arbore cu n noduri t(t-1)n-1
Ciclul Cn (t-1)n + (-1)n(t-1)
Graful Petersen t(t-1)(t-2)(t7-12t6 + 67t5-230t4 + 529t3-814t2 + 775t-352)
Proprietăţi
 P(G,0) = 0
 P(G,1) = 0 dacă G conţine o muchie
 P(G,t) = 0, dacă t < χ(G).
 P(G, − 1) este numărul orientărilor aciclice ale lui G
 Dacă G are n noduri, m muchii, şi k componente G1,G2,…,Gk, atunci
o P(G,t) are gradul n.
o Coeficientul lui tn în P(G,t) este 1.
o Coeficientul lui tn − 1 în P(G,t) este − m.
o Coeficienţii corespunzători : t0,t1, … tk − 1 sunt toţi zero.
o Coeficientul lui tk este diferit de zero.
o P(G,t) = P(G1,t)P(G2,t)⋯P(Gk,t)
 Coeficienţii fiecărui polinomul cromatic în parte alternează în ceea ce priveşte semnele.
 Un graf G cu n vârfuri este arbore dacă şi numai dacă
P(G,t) = t(t − 1)n − 1.
 Derivata evaluată în 1, P'(G,1), reprezintă invariantul cromatic θ(G).
Calcularea polinomului cromatic
De fiecare dată când G conţine o buclă, acesta nu poate fi colorat, astfel că P( G, t ) = 0 .
Dacă e nu este o buclă, atunci polinomul cromatic satisface relaţia de recurenţă
P( G , t ) = P ( G - e, t ) - P ( G / e, t ) unde G – e reprezintă graful G din care a fost înlăturată
muchia e, iar G / e reprezintă graful pentru care nodurile finale ale muchiilor sale sunt
contractate într-un singur vârf.
Cele două expresii dau naştere unei proceduri recursive, numită ( n.procedura)
algoritmul suprimare (ştergere) – contractare.

Discutii finale:
Sa se aplice algoritmul de descompunere slabă pe un graf dat.
Tema propusa:
Sa se proiecteze un program utilizand algoritmul de descompunere slaba.

BIBLIOGRAFIE

97
[1] Aho, Hopcroft and Ullman, The Design and Analysis of Computer’s Algorithms,
Addison Wesley, 1975.

[2] L. Babel, S. Olariu, On the Structure of Graphs with Few P4s, Work supported by NSF
grants CCR-9407180 and CCR-95-22093, as well as by ONR grandt N00014-1-95-0779,
Discrete Applied Mathematics, 1998.

[3] T. Bălănescu, Corectitudinea algoritmilor, Editura Tehnică, Bucureşti, 1995.

[4] M. Behzad, G. Chartand, L. L. Foster, Graphs and Digraphs, Prindle, Weber &
Schmidt International Series, 1973.

[5] C. Berge, Graphs (Nort-Holland, Amsterdam, 1985).

[6] V. Chvatal, Perfectly orderable graphs in "Topics on Perfect Graphs" (C. Berge and V.
Chvatal), Ann. Discrete Math.21(1984), 63-65.

[7] V. Chvatal, Star – cutest and Perfect Graphs, Journal of Comb. Th., B, 39, 189-199
(1985).

[8] E. Ciurea, Algoritmi. Introducere în algoritmica grafurilor, Editura Tehnică, Bucureşti,


2001.

[9] E. Ciurea, L. Ciupală, Algoritmi. Introducere în algoritmica fluxurilor în reţele, Editura


MATRIXROM, Bucureşti, 2006.

[10] T. H. Cormen, C. E. Leiserson, R. L. Rivest, Introducere în algoritmi (traducere),


Computer Libris Agora, 2000.

[11] D. G. Corneil, H. Lerchs, L. Stewart Burlingham, Complement Reductible Graphs,


Discrete Appl. Math. 3(1981), pp.163-174.

[12] D. G. Corneil, D. G. Kirkpatrick, Families of recursively defined perfect graphs, Congr.


Numer., 39(l983), 237-246.

[13] D. G. Corneil, Y. Perl, L. K. Stewart, A linear recognition algorithmic for cographs,


SIAM.J. Comput., 14(l985), 926-934.

[14] C. Croitoru, Tehnici de bază în optimizarea combinatorie, Editura Universităţii "Al. I.


Cuza", Iaşi, 1992.

[15] C. Croitoru, M. Talmaciu, A new graph search algorithm and some applications,
presented at ROSYCS 2000, Universitatea "Al.I.Cuza", Iaşi, 2000.

[16] E. Dahlhaus, Efficient parallel recognition algorithms of cographs and distance


hereditary graphs, Discrete Applied Mathematics 57(1995), 29-44.

[17] G. A. Dune, Generalisation du theoreme de Menger, C. R. Acad. Sci. Paris 250 (26)
(1960), 4252-4253.

[18] H. Georgescu, Tehnici de programare, Editura Universităţii din Bucreşti, 2005.

98
[19] M. C. Golumbic, Algorithmic Graphs Theory and Perfect Graphs, Academic Press 1980.

[20] F. Harary, On the number of bicolored graphs, Pacific J. Math. 8(1958), 743-755.

[21] X. He, Parallel Algorithm for Cograph Recognition With Applications, in: O. Nurmi and
E. Ukkonen eds. AIgorithm Theory-SWAT '92, Lecture Notes in Computer Science 621 (Springer
Berlin 1992), 94-105.

[22] B. Jamison and S. Olariu, On the clique-kernel structure of graphs, Department of


Computer Science, University of' Toronto, Toronto, Ontario, Octobre,1972.

[23] B. Jamison, S. Olariu, A tree representation for P4 sparse graphs, Discrete Appl. Math.,
35(1992), 115-129, North-Holland.

[24] B. Jamison, S. Olariu, P-components and the homogeneous decomposition of graphs,


SIAM J. DISC. MATH. Vol. 8, No.3, 448-463,1995.

[25] D. Knuth, Arta programării calculatoarelor, Editura Teora, Bucureşti, 2002.

[26] H. Lerchs, On clique-kernel structure of graphs, Department of Computer Science,


University of Toronto, Toronto, Ontario, October 1972.

[27] R. Lin, S. Olariu, An Nc Recognition Algorithm for Cographs, Journal of Parallel and
Distributed Computing, 13, 76-90 (1991).

[28] L. Livovschi, H. Georgescu, Sinteza şi analiza algoritmilor, Editura Ştiinţifică,


Bucureşti, 1986.

[29] R. H. Mohring, F. J. Radamacher, Substitution decomposition and connections with


combinatorial optimization, Ann. Discrete Math. 19(1984), 257-266.

[30] J. A. Nash-Williams, Edge-disjoint Hamiltonian circuits in graphs with vertices of large


valency, L. Musky, ed ., Studies in pure mathematics (papers presented to Richard Ratio),
(Academic Press, New York. 1971).

[31] I. Odagescu, C. Copos, D. Luca, F. Furtuna, I. Smeureanu, Metode şi tehnici de


programare, Editura Intact, Bucureşti, 1994.

[32] E. Olaru, On strongly stable graphs and some consequences for partitionable graphs,
Analele Ştiintifice ale Universităţii "Al.I.Cuza" din Iaşi, Tomul VII, 1998, 33-41.

[33] E. Olaru, On strongly perfect graphs and the structure of critically-imperfect graphs,
Ştiintifice ale Universităţii "Al.I.Cuza" din Iaşi, Tomul II, Informatică, 1993.

[34] E. Olaru, Some results on a-partitionable graphs and perfect graphs, Analele Univ. D. de
Jos Galaţi, Fascicola II, Supliment la tomul XVI/(XXI) 1998, Vol. I, Matematica, 171-185.

[35] E. Olaru, Introducere în teoria grafurilor, Univ. "Al.I.Cuza" Iaşi,1975.

[36] E. Olaru, St. Antohe, Decomposition with respect to the graph-join operation, Analele
Universităţii din Galaţi, fascicula II, I(VI)(1983), 15- 26.

99
[37] E. Olaru, S. Antohe, Some results concerning graphs decomposition, Analele
Universităţii din Galaţi, Matematică, Fascicula II, Anul III(VIII)1985.

[38] E. Olaru, Gh. Suciu, Dauerstabile und Kritisch imperfekte Graphen, An.Univ.
Timişoara, vol. XVII, fasc.l, 1970, 59-64.

[39] E. Olaru, a - Zerlegbare Graphen, Bul. Univ. Galati, fasc.II, 1980, 39-45.

[40] St. Olariu, On the closure of triangle-free graphs under substitution, Information
Processing Letters 34(1990), 97-101.

[41] O. Pătraşcoiu, Gh. Marian, N. Mitroi, Elemente de grafuri şi combinatorică. Metode,


algoritmi şi programe, Editura All, Bucureşti, 1994.

[42] G. Sabidussi, Graph derivatives, Math, Z76(l 961), 385-401.

[43] G. Sabidussi, The composition of graph, Duke Math. J. 26 (1959), 693-696.

[44] G. Sabidussi, The lexicographic product of graphs, Duke Math. J. 28 (1961), 573-578.

[45] R. Shamir, Advanced Topics in Graph Algorithms, Tehnical Reports, Tel – Aviv.
University, Sprin 1994.

[46] D. Summer, Graphs indecomposable with respect to the X-join, Discrete Math. 6(1973),
281-298.

[47] I. Tomescu, Probleme de combinatorică şi teoria grafurilor, Editura Didactică şi


Enciclopedică, Bucureşti, 1981.

[48] W. Wessel, Some color-critical equivalents of the strong perfect graph conjecture, in
Beitrage zur Graphentbeorie und deren Anwendungen, Obezhof (DDR), 10-16 April 1977, 300-
309.

[49] R. Yuster, Independent Transversals in r-partit graphs, Department of Mathematics


Raymond and Beverly Sackler Faculty of Exact Sciences Tel Aviv University, Tel Aviv, Israel,
(electronic), 2001.

[50] R. Yuster, Independent Transversals and Independent Coverings in Sparse Partite


Graphs, Department of Mathematics Raymond and Beverly Sackler Faculty of Exact Sciences
Tel Aviv University, Tel Aviv, Israel, (electronic), 2001.

FIŞA DISCIPLINEI

Denumirea ALGORITMICA GRAFURILOR


disciplinei

100
Codul UB04I205I 3
Semestrul Numărul de credite 5
disciplinei

Facultatea de Stiinte Numărul orelor pe semestru/activităţi


Domeniul INFORMATICĂ Total SI S L P
Specializarea INFORMATICĂ 42 55 14

Categoria formativă a disciplinei


DF-fundamentală, DG-generală, DS-de specialitate, DE-economică/managerială, DU-umanistă DF
Categoria de opţionalitate a disciplinei: DI-impusă, DO-opţională, DL-liber aleasă (facultativă) DI
Competenţele specifice acumulate
Competenţe Utilizarea instrumentelor informatice in context interdisciplinar
profesionale Utilizarea bazelor teoretice ale informaticii si a modelelor formale
Competenţe
transversale
- Găsirea soluţiilor unor probleme sau a valorilor aproximative ale
lor, determinarea metodelor de rezolvare şi a ordinului lor,
Obiective determinarea valorilor unor variabile supuse unor condiţii care să
optimizeze o funcţie.

1 Noţiuni de bază în teoria grafurilor. Arbori şi arborescenţe. 6 ore


2 Algoritmi elementari în grafuri. Reprezentări ale grafurilor,
căutarea în lăţime şi lungime, sortarea topologică. 6 ore
3 Drumuri minime în grafuri. Algoritmul lui Dijkstra, algoritmul
Conţinut
lui Bellman. 4 ore
(descriptori)
4 Fluxuri în reţele. Reţele de transport, algoritmul lui Ford şi
Fulkerson, cuplaje în grafuri bipartite. 6 ore
5 Probleme de stabilitate, de colorare, de conexiune. 6 ore

Teste si teme de
control

Forma de evaluare (E-examen, C-colocviu/test final, LP-lucrări de control) E


Stabilirea notei finale 40%
-răspunsurile la examen / colocviu / lucrări practice
(procentaje)
-activităŃi aplicative atestate / laborator / lucrări practice/proiect 60%
etc.
-teste pe parcursul semestrului
-teme de control
Bibliografia 1. Cornelius Croitoru, Tehnici de bază în Optimizarea
Combinatorie, Editura Universităţii “Al. I. Cuza” Iasi, 1992.
2. Eleonor Ciurea, Algoritmi. Introducere in algoritmica
grafurilor, Editura Tehnică, Bucureşti, 2001.
3. M.C.Golumbic, Algoritmic Graphs Theory and Perfect
Graphs, Academic Press 1980.
4. Udi Manber, Introduction to algorithms. A Creative Approach.
Addison Wesley, 1989.
5. Kurt Melhorn, Data Structures and Algorithms 2: Graph

101
Algorithms and NP-Completeness, Springer-Verlag Berlin
Heidelberg New York Tokyo 1984.
6. Ron Shamir, Advanced Topics in Graph Algorithm, Tehnical
Reports, TelAviv University, 1994.

Lista materialelor stiinte.ub.ro


didactice necesare

Coordonator Titular de Grad didactic, titlu, prenume, nume Semnătura


a) Disciplină Prof.univ.dr. Mihai Talmaciu
Algoritmica grafurilor
Legenda: SI – studiu individual, S-seminar, L – activităţi de laborator, P-proiect sau lucrări practice

102