Вы находитесь на странице: 1из 43

5.

Arbori pariali
Adeseori apar n practic probleme a cror rezolvare implic noiuni i rezultate din teoria grafurilor. S considerm, de exemplu, proiectarea unei reele de telecomunicaii care s conecteze un numr dat de centrale, de amplasare cunoscut, astfel nct ntre oricare dou centrale s existe legtur. Unei astfel de probleme i se poate asocia un graf neorientat n care mulimea vrfurilor este format din centralele ce trebuie interconectate, iar mulimea muchiilor din perechile de centrale ntre care se poate realiza o legtur direct. Pentru ca ntre oricare dou centrale s existe legtur, ar trebui ca graful s fie conex, dar, ca n orice problem practic, intervine factorul de cost i atunci ar fi de dorit s se selecteze un numr ct mai mic posibil de legturi directe ntre centrale, cu alte cuvinte intereseaz un graf parial conex minimal al grafului, deci un arbore. Definiie Fie G = (X, U) un graf neorientat. Numim arbore parial al grafului G un graf parial care este arbore. Teorem Condiia necesar i suficient ca un graf s conin un arbore parial este ca graful s fie conex. Demonstraie: Necesitatea Presupunem c G = (X,U) admite un arbore parial H = (X, U'), U' U. H, fiind arbore, este conex i adugnd la H muchiile din U-U' el rmne conex. Deci G conex. Suficiena Presupunem c G este conex. Dac G este conex minimal, el este arborele parial cutat. Altfel, exist o muchie [x,y] U astfel nct graful G 1 = (X, U-{[x,y]}) este conex. Dac G 1 este conex minimal, arborele parial cutat este G 1, altfel continum procedeul de eliminare a muchiilor pn cnd obinem un graf conex minimal, care va fi un arbore parial a lui G. Q.E.D.
178

Observaie Procedeul descris are un numr finit de pai, deoarece la fiecare pas este eliminat o muchie, iar G are un numr finit de muchii. Mai mult, putem demonstra urmtoarea proprietate : Propoziie Fie G = (X, U) conex, |X| = n, |U| = m. Numrul de muchii ce trebuie nlturate pentru ca graful s devin un arbore este m-n+1 (numrul ciclomatic al grafului). Demonstraie: Presupunem c prin eliminarea unui numr oarecare de muchii din G am obinut un graf G' fr cicluri (o pdure). Fiecare din componentele conexe ale lui G' este un arbore. Notm : - p numrul componentelor conexe, - ni numrul de vrfuri din componenta conex i, i{1,2,...,p} - mi numrul de muchii din componenta conex i, i{1,2,...,p}. Evident c mi = ni-1, i{1,2,...,p}. Numrul de muchi din G' este

(n 1) = n p
i i =1

Deci au fost eliminate m-n+p muchii. Cnd G' este arbore, deci conex (p=1), numrul muchiilor eliminate este m-n+1. Q.E.D. O prim problem, cu aplicaii, de exemplu, n chimie ar fi generarea tuturor arborilor pariali ai unui graf dat. Pentru acesta vom folosi metoda backtracking. Reprezentarea informaiilor -Vom reprezenta graful G= (X, U) cu ajutorul matricii muchiilor, o matrice G cu dou linii i m coloane (m = |U|), n care pentru fiecare muchie reinem cele dou extremiti. -Reprezentm un arbore parial ca un vector A cu n-1 componente, n care reinem indicii din G ai muchiilor arborelui. Pentru a nu genera de mai multe ori
179

acelai arbore, vom conveni ca muchiile s fie memorate n ordinea cresctoare a indicilor lor din G. - Pentru a verifica dac o muchie nou selectat nu formeaz cicluri cu muchiile deja selectate, vom ine evidena componentelor conexe ntr-un vector c de dimensiune n (n = |X|), c[i] desemnnd componenta conex din care face parte nodul i, pentru i {1,2,..., n}. Condiii interne 1. A[i] {1, 2, ..., m}, i {1, 2, ..., n-1} 2. A[i] A[i+1], i {1, 2, ..., n-2} 3. c[G[1, A[i]]] c[G[2, A[i]]], i {1, 2, ..., n-1} (nu se formeaz cicluri, extremitile muchiei fiind n componente conexe diferite). procedure ConstrArbore (i: byte); * //genereaz toi arborii pariali care au primele i-1 muchii //A[1],...,A[i-1] var j: byte; begin if i = n then //am selectat n-1 muchii care nu formeaz cicluri AfiareArbore else //selectez o muchie cu indice mai mare dect A[i-1] for j := A[i-1]+1 to m do if c[G[1, j]] c[G[2, j]] then begin A[i] := j; //selectez muchia j Unific(c[G[1, j]], c[G[2, j]]); //unific componentele conexe ale extremitilor muchiei j ConstrArbore(i+1); Separ(c[G[1, j]], c[G[2, j]]); //restaurez situaia dinaintea selectrii muchiei j end; end;
*

Programul Generare-Arbori-Pariali de la sfritul capitolului curent genereaz toi arborii pariali ai unui graf conex dat. 180

Iniial, c[i] := i, i {1, 2, ..., n}i apelm ConstrArbore(1). 5.1. Arbori pariali de cost minim Uneori nu intereseaz generarea tuturor arborilor pariali ai unui graf conex ci numai a celor care satisfac anumite condiii de optim. De exemplu, pentru proiectarea reelei de telecomunicaii ar fi interesant obinerea unui arbore parial care s minimizeze cheltuielile. Vom considera n continuare o funcie c: U R+, care asociaz fiecrei muchii din graf un cost (n exemplul nostru, s spunem, distana ntre dou centrale). Definind costul unui arbore parial ca fiind suma costurilor muchiilor arborelui, se pune problema obinerii unui arbore parial de cost minim. De exemplu, fie urmtorul graf:

Fig. 1. Acest graf admite mai muli arbori pariali de cost minim, de exemplu:

Fig. 2.
5.1.1.

Algoritmul lui Kruskal de determinare a


181

unui arbore parial de cost minim* Fie G = (X, U) un graf conex i c: U R+ o funcie cost. Pentru a determina un arbore parial de cost minim, se pleac de la o pdure format din n arbori (n = X), fiecare arbore fiind format dintr-un singur vrf. La fiecare pas se selecteaz o muchie de cost minim care nu a mai fost selectat i care nu formeaz cicluri cu cele deja selectate. S considerm de exemplu, graful din figura 1. Iniial:

Fig. 3. Selectnd o muchie de cost 1, obinem:

Programul Kruskal de la sfritul capitolului curent genereaz un arbore parial de cost minim al unui graf conex, utiliznd algoritmul lui Kruskal. 182

Fig. 4. Deci am unificat arborii corespunztori extremitilor muchiei selectate. Selectm din nou o muchie de costul minim 1:

Fig. 5. La acest pas nu mai putem selecta o muchie de cost 1, deoarece s-ar obine un ciclu. Selectm muchia de cost 2:

Fig. 6. Selectnd, n final, muchia de cost 3, obinem un graf fr cicluri maximal, deci un arbore:

Fig. 7.

183

La fiecare pas al algoritmului sunt unificai doi arbori, cei corespunztori extremitilor muchiei selectate. Deci, dup n-1 pai, pdurea iniial va fi transformat ntr-un singur arbore. Pentru implementarea algoritmului este necesar rezolvarea urmtoarelor dou probleme: cum extragem muchia de cost minim i cum testm dac muchia selectat formeaz sau nu cicluri cu cele deja selectate. Pentru a extrage minimul, o prim idee ar fi s sortm muchiile cresctor dup cost i s parcurgem secvenial muchiile ordonate. n cazul n care arborele parial de cost minim este gsit suficient de repede, un numr mare de muchii rmn netestate i n acest caz sar pierde timp inutil cu sortarea acestor muchii. O alt idee, mai eficient, ar fi s organizm muchiile grafului ca un min-heap, structur ce permite extragerea eficient a minimului. Pentru a testa dac o muchie formeaz cicluri cu muchiile deja selectate este suficient s testm dac extremitile muchiei se gsesc n aceeai component conex. Pentru aceasta va trebui s inem permanent evidena componentelor conexe (arborilor) care se formeaz. Reprezentarea informaiilor 1.Vom memora graful ntr-un vector G cu m (m = U) componente, fiecare component fiind o nregistrare cele dou extremiti i costul muchiei: Muchie = record e1, e2: Vf; cost: real; end; 2. Arborele parial de cost mimim se va memora ntr-un vector A cu n-1 componente ce reine indicii din G ai muchiilor selectate 3. Evidena componentelor conexe o vom ine cu ajutorul unui vector c cu n componente, c[i] = componenta conex creia i aparine vrful i. Componentele conexe vor fi identificate printr-un reprezentant (vrful cu indicele cel mai mic din componenta conex respectiv).
184

Teorem Algoritmul lui Kruskal genereaz un arbore parial de cost minim. Demonstraie: 1. Algoritmul selecteaz numrul maxim de muchii care nu formeaz cicluri, deci, conform teoremei de caracterizare a arborilor, se obine un arbore parial. 2. S demonstrm c arborele parial obinut n urma aplicrii algoritmului lui Kruskal este un arbore parial de cost minim : Fie A = (a 1, a2, ..., an-1) muchiile arborelui rezultat n urma aplicrii algoritmului, n ordinea selectrii lor. Presupunem prin reducere la absurd c arborele obinut nu este de cost minim, deci exist A' = (a 1', a2', ...., an-1') un alt arbore parial, astfel nct c(A') < c(A). Fie k = min{i | 1 i n, ai ai'}, primul indice de la care A i A difer. Deci A = (a1, a2, ..., ai-1, ai, ..., an-1) A' = (a1, a2, ..., ai-1, ai', .., an-1'), cu ai ai' . Evident c(ai) c(aj'), j {i, ..., n-1}, altfel algoritmul ar fi selectat muchia a j' n loc de ai, deoarece ar fi avut cost mai mic i nu formeaz cicluri cu a 1,...,ai-1. Adaug la A' muchia a i. Se formeaz un ciclu n care intervin doar muchii din {a i'...an-1'}. Elimin una din muchiile ciclului diferit de a i. Se obine un arbore A" = (a1,..., ai-1, ai, ai+1"..., an-1") care are i muchii comune cu A. n plus c(A")-c(A') = c(ai)-c(aj') 0 c(A") c(A'). Repetm procedeul de nlocuire a muchiilor din A' cu muchiile din A. Obinem c(A') c(A") ... c(A). Dar am presupus c c(A') < c(A) contradicie. Deci A este un arbore parial de cost minim. Q.E.D. Complexitatea algoritmului Organizarea muchiilor ca un min-heap este de O(m), m = U. Algoritmul cerceteaz n cel mai defavorabil caz toate cele m muchii pentru a selecta n-1, la fiecare pas fiind necesar extragerea muchiei de cost minim, operaie de O(log m) = O(log n). Selectarea unei
185

muchii implic i o operaie de unificare a doi arbori, al crei timp de execuie depinde de metoda de unificare aleas. 5.1.2. Algoritmul lui Prim de determinare a unui arbore parial de cost minim* Ca i algoritmul lui Kruskal, algoritmul lui Prim utilizeaz o strategie Greedy. Iniial se pleac de la un arbore format dintr-un singur vrf. La fiecare pas se selecteaz o muchie de cost minim astfel nct mulimea A a muchiilor selectate i mulimea Y a vrfurilor unite de acestea s formeze un arbore. De exemplu, s considerm graful din figura 1 i vrful de start 5. Iniial

Fig. 8. Selectm o muchie de cost minim care s fie incident cu vrful 5:

Fig. 9.

Programul Prim de la sfritul capitolului curent genereaz un arbore parial de cost minim al unui graf conex, utiliznd algoritmul lui Prim. 186

Selectm o muchie de cost minim, care s fie incident cu unul din vrfurile din subgraful obinut la pasul anterior:

Fig. 10. Selectez o muchie de cost 1, incident cu unul din vrfurile din subgraful anterior:

Fig. 11. Selectnd cea de a patra muchie, obinem un arbore parial de cost minim:

Fig. 12. La fiecare pas se selecteaz un nou vrf, adiacent cu unul din vrfurile subgrafului, astfel nct muchia corespunztoare s fie de cost minim. Nodul nou adugat va fi terminal i deci nu se vor obine cicluri, iar subgraful construit este la fiecare pas conex, deci arbore. Reprezentarea informaiilor 1. Reprezentm graful prin matricea costurilor, Cnxn

187

2. Z va fi mulimea vrfurilor neselectate n subgraf (Z = X-Y). 3. Vom utiliza un vector key, de dimensiune n, n care pentru fiecare vrf x Z vom reine costul minim al muchiilor ce unesc vrful x cu un vrf v din subgraf: key[x] = min{c([x, v]) v Y}, x X\Y. Evident, dac astfel de muchii nu exist key[x] = + . 4. Reinem arborele parial de cost minim, memornd vrfurile grafului n ordinea n care au fost atinse ntr-un vector p de dimensiune n. p[x] = vrful din care a fost atins x. procedure Prim; //determin un APM al unui graf; matricea costurilor c, numrul de // vrfuri n i vrful de start r sunt variabile globale var x, j, i: Vf; key: array[ Vf ] of real; Z: set of Vf; p: array[ Vf ] of Vf; begin //iniializare for x := 1 to n do key[x] +; key[r] := 0; p[r] := 0; := [1, 2, ..., n] - [r]; while Z [] do //exist vrfuri neselectate begin i := xtrageMin(Z); //extrage din Z vrful de cheie minim for j := 1 to n do //actualizez cheile vrfurilor din Z if C[i, j] then // i i j sunt adiacente if (j Z) and (key[j] > C[i, j]) then begin p[j] := i; key[j] := C[i, j]; end
188

0, dac i= j R L O | M P c(M i,jP ), dac L i,jO U | M P M P N Q N Q C i, j = S | M P , dac L i,jO U M N P Q | T

end;

end

Complexitatea algoritmului Algoritmul execut n-1 pai, la fiecare pas selectnd un vrf din graf de cheie minim i reactualiznd cheile vrfurilor neselectate, operaie de O(n). Deci algoritmul este de ordinul O(n2). 5.2. Arbori pariali BFS Breadth-First-Search este tehnica de explorare a grafurilor n lime. Dat fiind un graf conex G = (X, U) i un nod surs s X, metoda BFS impune vizitarea mai nti a nodului s, apoi a tuturor nodurilor nevizitate adiacente cu s, apoi a tuturor nodurilor nevizitate adiacente nodurilor adiacente cu s, .a.m.d. De exemplu, pentru graful din figura de mai jos, parcurgerea BFS, cu nodul surs s = 6, este: 6, 4, 5, 8, 9, 2, 3, 7, 10, 11, 1, 12.

Fig. 13. Reinnd toate muchiile utilizate n timpul parcurgerii obinem arborele parial BFS, cu rdcina s = 6 (figura 14): [6,4], [6,5], [6,8], [6,9], [4,2], [4,3], [5,7], [8,10], [9,11], [2,1], [11,12].

189

Observaie Pentru orice vrf v din arbore, lanul unic care unete rdcina s de v reprezint lanul cu numr minim de muchii de la s la v n graf. Reprezentarea informaiilor 1. Reprezentm graful prin listele de adiacen. G: array[Vf] of Lista; Deci pentru fiecare vrf din graf reinem lista vrfurilor adiacente cu vrful respectiv. 2. Arborele parial BFS l reprezentm cu ajutorul unui vector T n care pentru fiecare vrf reinem vrful din care a fost atins n timpul parcurgerii BFS. T: array[Vf] of Vf; 3. Utilizm un vector boolean V, n care pentru fiecare vrf din graf reinem dac a fost sau nu atins n timpul parcurgerii BFS. V: array[Vf] of boolean; 4. Pentru parcurgerea grafului n lime vom utiliza o coad pe care o iniializm cu vrful surs. La fiecare pas extragem un element din coad, vizitm toate vrfurile nevizitate adiacente cu vrful extras i le inserm n coad, reinnd pentru fiecare vrf vizitat vrful din care a fost atins, pentru reconstituirea arborelui parial BFS. Observaie G, T, n (numrul de vrfuri din graf) i s (vrful surs) sunt variabile globale. procedure BFS;*
*

Fig. 14.

Programul Arbori-Pariali-BF-DF de la stritul capitolului curent genereaz arborii pariali BFS i DFS ai unui graf conex dat. 190

//parcurge n lime graful G, ncepnd cu vrful s construind //arborele parial BFS var C: Coada; q: Lista; i: Vf; V: array[ Vf ] of boolean; begin for i := 1 to n do V[i] := false; C s; //iniializez coada cu vrful surs while C [] do begin x C; //extrage un vrf x din coad q := G[x]; while q nil do //parcurg lista de adiacen a nodului x begin i := q^.inf; if not V[i] then // nodul i este nevizitat begin V[i] := true; T[i] := x;//rein vrful din care a fost atins i C i; //introduc vrful i n coad end; q := q^.urm; end; end; end; Observaii 1. Dac graful G nu este conex parcurgnd BFS fiecare component conex obinem o pdure, format din arborii pariali corespunztori fiecrei componente conexe. 2. Complexitatea algoritmului, n cazul n care graful este reprezentat prin listele de adiacen, este de O(n+m), unde n este numrul de vrfuri, iar m numrul de muchii din graf. 5.3. Arbori pariali DFS O alt tehnic de parcurgere (explorare) a

191

grafurilor este metoda Depth-First-Search (parcurgerea n adncime). Dat fiind G un graf conex i un nodul surs s vizitm mai nti nodul s, apoi primul nod nevizitat adiacent cu s, mergnd n adncime ct este posibil. Cnd un nod x nu mai are vecini nevizitai ne ntoarcem s cercetm dac nodul din care a fost atins x mai are sau nu vecini nevizitai i continum parcurgerea. De exemplu, pentru graful din figura 13, parcurgerea dup metoda DFS, cu nodul iniial s = 6, determin urmtoarea ordine de vizitarea a nodurilor: 6, 4,2,1,3,7,5,8,9,10,11,12. Marcnd muchiile utilizate prin vizitarea nodurilor obinem un arbore parial numit arbore parial DFS, cu rdcina s = 6 (figura 15): [6,4], [4,2], [2,1], [2,3], [3,7], [7,5], [5,8], [8,9], [9,10], [10,11], [11,12].

Fig. 15. Observaie Reprezentarea informaiilor se face n acelai mod ca la parcurgerea BFS, n plus vectorul V fiind de asemeni variabil global. Algoritmul poate fi descris folosind o procedur recursiv DFS, pe care iniial o apelm cu parametrul s, astfel: procedura DFS(x: Vf); //parcurge vrfurile nevizitate ale grafului ncepnd cu x begin V[x] := true; q := G[x]; while q nil do //parcurg lista de adiacen a vrfului x
192

begin i := q^.inf; if not V[i] then //i este nevizitat begin T[i] := x; //rein vrful din care a fost atins DFS(i); //parcurge vrfurile nevizitate

ncepnd cu i end; q := q^.urm end; end; Observaii 1. Dac graful G nu este conex parcurgnd DFS fiecare component conex obinem o pdure, format din arborii pariali corespunztori fiecrei componente conexe. 2. Complexitatea algoritmului, n cazul n care graful este reprezentat prin listele de adiacen este de O(n+m), unde n este numrul de vrfuri, iar m numrul de muchii din graf. 5.4. Aplicaie. Descompunerea unui graf n componente biconexe Definiie Fie G = (X, U) un graf neorientat conex. Vrful v X se numete punct de articulaie dac subgraful obinut prin eliminarea vrfului v i a muchiilor incidente cu acesta nu mai este conex. De exemplu, pentru graful din figura 16 punctele de articulaie sunt 1,3,5,7.

193

Definiie Un graf se numete biconex dac nu are puncte de articulaie. n multe aplicaii practice, ce se pot modela cu ajutorul grafurilor, nu sunt de dorit punctele de articulaie. Revenind la exemplul cu reeaua de telecomunicaii, dac o central dintr-un punct de articulaie se defecteaz rezultatul este nu numai ntreruperea comunicrii cu centrala respectiv ci i cu alte centrale. Definiie O component biconex a unui graf este un subgraf biconex maximal cu aceast proprietate. De exemplu, pentru graful din figura 16 componentele biconexe sunt:

Fig. 16.

Fig. 17. Pentru a descompune graful n componente biconexe vom utiliza proprietile parcurgerii DFS. Parcurgnd graful DFS putem clasifica muchiile grafului n: -muchii care aparin arborelui parial DFS (tree edges); -muchii [u, v] care nu aparin arborelui i care unesc vrful u cu un strmo al su v n arborele parial DFS numite muchii de ntoarcere (back edges). Acestea
194

sunt marcate n exemplu punctat. De exemplu graful de mai sus poate fi redesenat, clasificnd muchiile innd cont de arborele parial DFS cu rdcina 3:

Fig. 18. Observm c rdcina arborelui parial DFS este punct de articulaie dac i numai dac are cel puin doi descendeni, ntre vrfuri din subarbori diferii ai rdcinii neexistnd muchii. Mai mult, un vrf x oarecare nu este punct de articulaie dac i numai dac din orice fiu y al lui x poate fi atins un strmo al lui x pe un lan format din descendeni ai lui x i o muchie de ntoarcere (un drum de siguran ntre x i y). Pentru fiecare vrf x al grafului putem defini : dfn(x) = numrul de ordine al vrfului x n parcurgerea DFS a grafului (depth-first-number ). De exemplu: x 0 1 2 dfn(x 2 1 3 ) 3 0 4 4 5 5 6 6 7 7 8 8 9 9

Dac x este un strmo al lui y n arborele parial DFS atunci dfn(x) < dfn(y). De asemeni pentru fiecare vrf x din graf putem defini : low(x) = numrul de ordine al primului vrf din parcurgerea DFS ce poate fi atins din x pe un alt lan
195

dect lanul unic din arborele parial DFS. low(x) = min{dfn(x), min{ low(y) | y fiu al lui x }, min{dfn(y) | [x,y] muchie de ntoarcere}. De exemplu: x 0 1 2 3 4 5 6 7 8 9 dfn(x 2 1 3 0 4 5 6 7 8 9 ) low(x 2 1 1 0 1 5 5 5 8 9 ) Deci putem caracteriza mai exact punctele de articulaie dintr-un graf astfel: x este punct de articulaie dac i numai dac este rdcina unui arbore parial DFS cu cel puin doi descendeni sau, dac nu este rdcin, are un fiu y astfel nct low(y) dfn(x). Pentru exemplul din figura 16: - nodul 3 este punct de articulaie deoarece este rdcina arborelui parial DFS i are doi descendeni, - nodul 7 este punct de articulaie deoarece low(8)=8 dfn(7)=7, - nodul 5 este punct de articulaie deoarece low(6)=5dfn(5)=5, - nodul 1 este punct de articulaie deoarece low(0)=2dfn(1)=1. Modificm procedura DFS, pentru a calcula pentru fiecare vrf din graf valorile dfn i low. Intrare:-graful G, reprezentat prin listele de adiacen; Ieire:-vectorii dfn i low. Utilizm o variabil global num, pentru a calcula numrul de ordine al vrfului curent n parcurgerea n adncime. //Iniializare num := 0; for x := 1 to n do dfn[x] := -1; DfnLow(s, -1) // s este rdcina arborelui parial DFS procedure DfnLow(u, tu: Vf); //parcurge DFS graful G ncepnd
196

cu

vrful

u,

calculnd // valorile dfn[u] i low(u); tu este tatl lui u var q: Lista; x: Vf; begin dfn[u] := num; low[u] := dfn[u]; inc(num); q := G[u]; while q nil do //parcurg lista de adiacen a lui u begin x := q^.inf; if dfn[x] = -1 then //x este nevizitat begin DfnLow(x, u) low[u] := min(low[u], low[x]); //funcia min returneaz minimul argumentelor ei end else if x tu then low[u] := min(low[u], dfn[x]); q := q^.urm; end end; Vom folosi aceast idee de calcul recursiv al valorilor dfn i low pentru descompunerea n componente biconexe a unui graf neorientat conex. Reprezentarea informaiilor se face n acelai mod ca la parcurgerea DFS, dar vectorul V nu mai este necesar, pentru vrfurile nevizitate dfn fiind -1. n plus, vom folosi o stiv S n care vom reine muchiile din graf (att cele care aparin arborelui ct i cele de ntoarcere) n ordinea n care sunt ntlnite n timpul parcurgerii. Atunci cnd identificm o component biconex, mai exact identificm un nod u care are un fiu x astfel nct low(x) dfn(u), eliminm din stiv i afim toate muchiile din componenta biconex respectiv. //Iniializare num := 0; S (s, -1); //stiva este iniializat cu o muchie fictiv [s,-1], s fiind
197

sursa for x := 1 to n do dfn[x] := -1; Biconex(s, -1) // s este rdcina arborelui parial DFS procedure biconex(u, tu: Vf);* //afieaz componentele biconexe , parcurgnd graful ncepnd // cu vrful u; tu este tatl lui u var q: Lista; x: Vf; begin dfn[u] := num; low[u] := dfn[u]; inc(num); q := G[u]; while q nil do // parcurg lista de adiacen a lui u begin x := q^.inf; if (tu x) and (dfn[x] < dfn[u]) then S [u, x]; //dac tu=x sau dfn(x)>dfn(u) muchia (u,x) a fost deja //reinut n stiv if dfn[x] = -1 then //x este nevizitat begin Biconex(x, u) low[u] := min(low[u], low[x]); if low[x] dfn[u] then //am identificat o nou component biconex Afisare(x, u) // extrage din S i afieaz muchiile //componentei biconexe curente else if x tu then low[u] := min(low[u], dfn[x]); end; q := q^.urm;
*

Programul Componente-Biconexe de la sfritul capitolului curent realizeaz descompunerea n componente biconexe a unui graf neorientat conex. 198

end; end; Observaie Oricare dou componente biconexe au cel mult un vrf comun, deci nici o muchie nu poate fi n dou componente biconexe. S urmrim execuia algoritmului pe urmtorul exemplu:

Fig. 19. Arborele parial DFS este:

Fig. 20. Iniial: x 0 1 2 3 4 5 6 7 dfn( -1 -1 -1 -1 -1 -1 -1 -1 x) low( x) num 0 S: 3 -1 Apelez biconex(3, -1) x 0 1 2 3 4 5 6


199

dfn( x) low( x)

-1 -1 -1 0 0

-1 -1 -1 -1

S: 3 1 3 -1 Apelez biconex(1, 3) x 0 1 2 3 dfn( -1 1 -1 0 x) low( 1 0 x)

num 1 x 1

4 5 6 7 -1 -1 -1 -1

num 2 x 0 S: 1 0 3 1 3 -1 Apelez biconex(0, 1): x 0 1 2 3 dfn( 2 1 -1 0 x) low( 2 1 0 x)

4 5 6 7 -1 -1 -1 -1

num 3 x 1 Revin n biconex(1, 3) low[1] min(low[1], low[0]) = low[1] low[0] > dfn[3]. Am obinut o biconex, afiez [1,0]. S:
200

component

3 1 3 -1 x 2 S: 1 2 3 1 3 -1 Apelez biconex(2, 1) x 0 1 2 3 dfn( 2 1 3 0 x) low( 2 1 3 0 x)

4 5 6 7 -1 -1 -1 -1

num 4 x 1 x 4 S: 2 4 1 2 3 1 3 -1 Apelez biconex(4, 2) 0 1 2 3 x dfn( 2 1 3 0 x) low( 2 1 3 0 x) num 5 x 2 x 3 S: 4 3 2 4


201

4 4 4

-1 -1 -1

1 2 3 1 3 -1 low[4] min(low[4], dfn[3]) = 0 x 0 1 2 3 4 5 6 7 dfn( 2 1 3 0 4 -1 -1 -1 x) low( 2 1 3 0 0 x) Revin n biconex(2, 1) low[2] min(low[2], low[4]) = 0 x 0 1 2 3 4 5 6 7 dfn( 2 1 3 0 4 -1 -1 -1 x) low( 2 1 0 0 0 x) Revin n biconex(1, 3) low[1] min(low[1], low[2]) = 0 x 0 1 2 3 4 5 6 7 dfn( 2 1 3 0 4 -1 -1 -1 x) low( 2 0 0 0 0 x) low[1] min(low[1], dfn[2]) Revin n biconex(3, -1) low[3] min(low[3], low[1]) low[1] = dfn[3]. Am obinut o nou component biconex: [4,3], [2,4], [1,2], [3,1]. S: 3 -1 x 4 low[3] min(low[3], dfn[4]) = 0 x 5 S: 3 5 3 -1 Apelez biconex(5, 3)
202

x dfn( x) low( x)

0 2 1

1 1 0

2 3 0

3 0 0

4 4 0

5 5 5

6 7 -1 -1

num 6 x 3 x 6 S: 5 6 3 5 3 -1 Apelez biconex(6, 5) x 0 1 2 3 dfn( 2 1 3 0 x) low( 1 0 0 0 x)

4 4 0

5 5 5

6 6 6

7 -1

num 7 x 5 x 7 S: 6 7 5 6 3 5 3 -1 Apelez biconex(7, 6) x 0 1 2 3 dfn( 2 1 3 0 x) low( 1 0 0 0 x) num 8 x 5


203

4 4 0

5 5 5

6 6 6

7 7 7

S: 7 5 6 7 5 6 3 5 3 -1 low[7] min(low[7], dfn[5]) = 5 x 0 1 2 3 4 5 6 7 dfn( 2 1 3 0 4 5 6 7 x) low( 1 0 0 0 0 5 6 5 x) x 6 Revin n biconex(6, 5) low[6] min(low[6], low[7]) = 5 x 0 1 2 3 4 5 6 7 dfn( 2 1 3 0 4 5 6 7 x) low( 1 0 0 0 0 5 5 5 x) low[6] min(low[6], dfn[7]) = 5 Revin n biconex (5, 3) low[5] min(low[5], low[6]) = 5 low[6] > dfn[5], deci afiez o nou component biconex: [7,5],[6,7],[5,6]. S: 3 5 3 -1 Revin n biconex(3, -1) low[3] = min(low[3], low[5]) = 0 low[5] = dfn[3], deci afiez o nou component biconex: [5,3]. S: 3 -1 Stop Teorem
204

Procedura biconex(s,-1) genereaz componentele biconexe ale grafului conex G. Demonstraie: Vom lua n considerare cazul n care n, numrul de vrfuri din G, este cel puin 2 (dac n = 1, G are o singur component biconex, format dintr-un singur vrf). Vom face demonstraia prin inducie dup B, numrul de componente biconexe. P(1) B = 1 graful este biconex: rdcina s a arborelui parial DFS va avea un singur fiu, s-l notm x. Apelul biconex(x, s) a explorat toate muchiile grafului i le-a introdus n stiva S. Cum x este singurul vrf pentru care low[x] dfn[s], muchiile grafului vor fi afiate ntr-o singur component biconex. P(B) S presupunem acum c algoritmul funcioneaz corect pentru toate grafurile conexe cu cel mult B componente biconexe. P(B+1) Vom demonstra c algoritmul funcioneaz pentru toate grafurile conexe cu cel mult B +1 componente biconexe. Fie G un graf cu B+1 componente biconexe i fie x primul vrf pentru care este ndeplinit condiia low[x] dfn[u]. Pn n acest moment nu a fost identificat nici o component biconex. n urma apelului biconex(x, u), toate muchiile incidente cu descendeni ai lui x se afl n stiva S, deasupra muchiei [u, x]. Cum x este primul vrf care ndeplinete condiia low[x] dfn[u], deducem c descendenii lui x nu sunt puncte de articulaie. Cum u este punct de articulaie, rezult c muchiile situate n S, deasupra muchiei [u, x], inclusiv, formeaz o component biconex. Muchiile acestei componente biconexe sunt extrase din stiv i afiate, deci, de la acest pas algoritmul revine la aflarea componentelor biconexe ale unui graf G, obinut din G prin eliminarea unei componente biconexe. Cum G are B componente biconexe, conform ipotezei inductive, algoritmul determin corect componentele biconexe ale lui G. Q.E.D.
205

Observaie Algoritmul de descompunere n componente biconexe a unui graf reprezentat prin listele de adiacen este liniar (O(n+m)), unde n este numrul de vrfuri, iar m numrul de muchii din graf. 5.5. Problem rezolvat Problema brfei -Baraj, Arad 1992Se consider n persoane P1, P2, ..., Pn care doresc fiecare s transmit propria brfa celorlalte persoane. Numim instruciune o pereche (i, j) avnd urmtorul efect: persoana Pi transmite persoanei Pj propria sa brf, dar i eventualele brfe primite anterior prin instruciuni de la alte persoane. Din pcate anumite perechi de persoane, citite de la intrare, se dumnesc i deci nu comunic ntre ele. S se determine, dac este posibil, o secven de instruciuni prin care fiecare persoan s cunoasc brfele tuturor celorlalte persoane. Soluie : Asociem problemei un graf neorientat astfel: -mulimea vrfurilor este format din cele n persoane. -ntre dou vrfuri exist muchie dac cele dou persoane corespunzatoare comunic. Problema are soluie dac i numai dac graful asociat problemei este conex. Dac graful este conex, atunci admite un arbore parial. Determinm un arbore parial DFS al grafului dat, cu rdcina 1. Parcurgnd acest arbore n postordine, toate brfele vor fi recepionate de vrful 1, fiecare muchie utilizat reprezentnd o instruciune. Parcurgnd arborele n preordine, nodul 1 va transmite brfele tuturor celorlalte persoane. Sunt astfel necesare 2n-2 instruciuni. Observaie Numrul de 2n-2 instruciuni este optim. Reprezentarea informaiilor -Reprezentm graful prin matricea de adiacen:
206

1 , dac i i j comunic; g i, j = 0, altfel. -Reprezentm arborele parial DFS ca pe un arbore binar, utiliznd reprezentarea fiu-frate. -Utilizam un vector v de dimensiune n, pentru a marca dac un vrf a fost sau nu atins n timpul parcurgerii DFS: , dac i a fost vizitat; vi = 1 0, altfel.

program barfa;
const NMaxPers = 7; type Pers = 0..NMaxPers; Arbore = ^NodArbore; NodArbore = record p: Pers; tata, fiu, frate: Arbore end; var g: array[Pers, Pers] of 0..1; n, i: Pers; conex: boolean; v: array[Pers] of 0..1; A: Arbore; fout: text; procedure citire; var f: text; i, j: Pers; begin assign(f, 'barfa.in'); reset(f); readln(f, n); for i := 1 to n do for j := 1 to n do g[i,j] := 1; while not eof(f) do begin readln(f, i, j); g[i,j] := 0; g[j,i] := 0 end; close(f); end; procedure DFS(x: Arbore); {vizitam varful x; tx este tatal lui x} var i: Pers; q, u: Arbore; begin u := nil;{ultimul fiu al lui x} for i := 1 to n do if (g[x^.p, i] = 1) and (v[i] = 0) then begin v[i] := 1; new(q); q^.p := i; q^.tata := x; q^.fiu := nil; q^.frate := nil;

207

if u = nil then {i este primul fiu al lui x} x^.fiu := q else u^.frate := q; {i este primul frate al lui u} u := q; DFS(q); end; end; procedure postordine(x: Arbore); {afiseaza instructiunile corespunzatoare parcurgerii postordine a arborelui DFS} var q: Arbore; begin q := x^.fiu; while q <> nil do begin postordine(q); q := q^.frate; end; if x<> A then writeln(fout,'(',x^.p,',',x^.tata^.p,')'); end; procedure preordine(x: Arbore); var q: Arbore; begin if x <> A then writeln(fout,'(',x^.tata^.p,',',x^.p,')'); q := x^.fiu; while q <> nil do begin preordine(q); q := q^.frate; end; end; begin {program principal} citire; assign(fout,'barfa.out'); rewrite(fout); v[1] := 1; A^.p := 1; DFS(A); conex := true; for i := 1 to n do if v[i] = 0 then begin conex := false; break end; if not conex then {graful este neconex} writeln(fout,'Nu exista solutii!') else begin postordine(A); preordine(A); end; close(fout);

208

end.

5.6. Exerciii 1.Demonstrai prin inducie dup numrul de vrfuri din graf c algoritmul lui Prim produce un arbore parial de cost minim. 2. Fie G = (X, U) un graf neorientat conex. Muchia [x, y] se numete punte dac prin eliminarea ei din graf, graful parial obinut nu este conex. Scriei un algoritm care determin toate punile unui graf conex. Algoritmul s funcioneze n timp de O(n+m) (n =x, m =U). Observaie O muchie este punte dac i numai dac nu aparine unui ciclu. 3. Fie G = (X, U) un graf neorientat conex cu funcia pondere asociat c: U R+*; U X. a). Fie T un arbore parial de cost minim pentru G. Demonstrai c exist o muchie [u, v] T i [x, y] T astfel nct T-{[u,v]}{[x,y]} este al doilea cel mai bun arbore parial de cost minim (SAPM). b). Fie T un arbore parial pentru G. Pentru oricare dou vrfuri u, v X definim max(u, x) ca fiind o muchie de pondere maxim de pe lanul unic ntre u i v n T. Descriei un algoritm cu timpul de execuie de O(X2) astfel nct, dat T, s calculeze max(u, v), u, v X. 4. Fie G = (X, U) un graf neorientat conex. Numim distana dintre vrfurile x i y, notat d(x, y), numrul de muchii al celui mai scurt lan care unete x cu y. Notm: e(x) = excentricitatea vrfului x: e( x) = max d( x, y)
yX

d(G) = diametrul grafului G d(G) = max e( x) r(G) = raza grafului G r (G) = min e( x) c(G) = centrul grafului G
209
xX xX

c(G) = x X e( x) = r (G)

a). Demonstrai c c(G) este format dintr-un vrf sau din dou vrfuri adiacente. b). Artai c d(G) 2*r(G). c). Descriei un algoritm care s calculeze raza, diametrul i centrul unui graf conex dat. 5. Arbori pariali DS O alt tehnic de parcurgere n adncime a nodurilor unui graf este metoda Depth-Search. Aceast metod mbin cele dou metode de parcurgere prezentate, n sensul c urmeaz acelai algoritm ca metoda Breadth-First-Search numai c utilizeaz o stiv, ca i metoda Depth-First-Search. De exemplu, pentru graful din figura 13, parcurgerea dup metoda DS, cu nodul iniial s = 6, determin urmtoarea ordine de vizitare a nodurilor: 6, 4, 5, 8, 9, 10, 11, 12, 7, 3, 2, 1. Marcnd muchiile utilizate prin vizitarea nodurilor obinem un arbore parial numit arbore parial DS, cu rdcina s = 6 (figura 21): [6,4], [6,5], [6,8], [6,9], [9,10], [9,11], [11,12], [10,7, [7,3], [3,2], [2,1].

Fig. 21. Scriei un program care realizeaz parcurgerea n adncime dupa metoda DS a unui graf, cu obinerea arborelui parial DS. 6. Problema sponsorului (Olimpiada Naional de Informatic, Suceava 1996) RATUC Suceava, unul dintre sponsorii olimpiadei, i propune s mbunteasc transportul n comun n localitate. Directorul va pune la dispoziie o schem pe care sunt reprezentate staiile, numerotate pentru simplificare, de la 1 la n i cele k linii directe ntre staii,
210

astfel nct ntre oricare dou staii exist legtur, eventual cu schimbarea mijlocului de transport. Trebuie s determinai dac exist cel puin o linie direct prin blocarea creia legtura, direct sau indirect, ntre cel puin dou staii s fie ntrerupt. Dac astfel de linii exist, s se propun nfiinarea unui numr ct mai mic de linii directe ntre staiile existente astfel nct prin blocarea unei singure linii directe, oricare ar fi aceasta, circulaia ntre oricare dou staii s fie posibil; se alege soluia pentru care suma ocolurilor pe traseele varianta (msurate n numr de linii directe) s fie ct mai mic.

211

Anex
program Generare_Arbori_Partiali;
const NMaxVf = 20; NMaxMuchii = NMaxVf*(NMaxVf-1) div 2; type Vf = 1..NMaxVf; NrMuchie = 1..NMaxMuchii; Graf = array[1..2, NrMuchie] of NrMuchie; Arbore = array[0..NMaxVf-1] of NrMuchie; var n: Vf; m: NrMuchie; g:Graf; a: Arbore; c: array[Vf] of Vf; nr: word; fout: text; procedure Initializare; var i: NrMuchie; j: Vf; fin: text; begin assign(fin, 'ap.in'); assign(fout, 'ap.out'); rewrite(fout); reset(fin); readln(fin, n, m); for i := 1 to m do readln(fin, g[1,i], g[2,i]); for j := 1 to n do c[j] := j; close(fin) end; procedure ScrieArb; var i: Vf; begin inc(nr); writeln(fout, 'Arborele ',nr); for i := 1 to n-1 do write(fout,'[', g[1,a[i]],',',g[2,a[i]],'] '); writeln(fout) end; procedure ConstrArb(i: Vf); var j: NrMuchie; k, Nou, Vechi: Vf; aux: set of Vf; begin if i = n then ScrieArb else for j := a[i-1]+1 to m do if c[g[1, j]] <> c[g[2, j]] then {muchia j nu formeaza cicluri cu cele deja selectate} begin a[i] := j; {unific componentele conexe ale extremitatilor muchiei j} aux := [];{retin varfurile ce au fost trecute din componenta conexa Vechi in Nou} Nou := c[g[1, j]]; Vechi := c[g[2, j]]; for k := 1 to n do if c[k] = Vechi then begin

212

c[k] := Nou; aux := aux+[k]; end; ConstrArb(i+1); {restaurez situatia dinaintea apelului } for k := 1 to n do if k in aux then c[k] := Vechi; end end; begin {program principal } Initializare; ConstrArb(1); close(fout); end.

program Kruskal;
const NMaxVf = 20; NMaxMuchii = NMaxVf*(NMaxVf-1) div 2; type Vf = 1..NMaxVf; NrMuchie = 1..NMaxMuchii; Muchie = record e1, e2: Vf; cost: word end; Graf = array[NrMuchie] of Muchie; Arbore = array[1..NMaxVf-1] of Muchie; var n, i, min, max, NrMSel: Vf; k:Muchie; m: NrMuchie; g:Graf; a: Arbore; c: array[Vf] of Vf; procedure Initializare; var i: NrMuchie; j: Vf; fis: text; begin assign(fis, 'Kruskal.in'); reset(fis); readln(fis, n, m); for i := 1 to m do readln(fis, g[i].e1, g[i].e2, g[i].cost); for j := 1 to n do c[j] := j; close(fis) end; procedure CombHeap(i, m: NrMuchie); var parinte, fiu: NrMuchie; v: Muchie; begin v := g[i]; parinte := i; fiu := 2*i; while fiu <= m do begin if fiu < m then if g[fiu].cost > g[fiu+1].cost then fiu := fiu+1; if v.cost > g[fiu].cost then

213

begin g[parinte] := g[fiu]; parinte := fiu; fiu := fiu*2; end else fiu := m+1; end; g[parinte] := v; end; procedure ConstrHeap; var i: NrMuchie; begin for i := m div 2 downto 1 do CombHeap(i, m); end; function maxim(a, b: word): word; begin maxim := a; if b > a then maxim := b end; function minim(a, b: word): word; begin minim := a; if b < a then minim := b; end; procedure Afisare; var i: Vf; CostAPM: word; begin if NrMSel= n-1 then begin CostAPM := 0; writeln('Arborele partial de cost minim este :'); for i := 1 to n-1 do begin writeln('[',a[i].e1,',',a[i].e2,'] cost=',a[i].cost); CostAPM := CostAPM+a[i].cost end; writeln('Costul APM=',CostAPM); end else writeln('Graful nefiind conex nu admite arbori partiali. '); readln end; begin {program principal } Initializare; ConstrHeap; while (NrMSel < n) and (m > 0) do begin {extrag din heap muchia de cost minim}

214

k := g[1]; g[1] := g[m]; dec(m); CombHeap(1, m); if c[k.e1] <> c[k.e2] then {nu se formeaza ciclu} begin inc(NrMSel); a[NrMSel] := k; {contopesc conponentele conexe ale extremitatilor muchiei selectate } min := minim(c[k.e1], c[k.e2]); max := maxim(c[k.e1], c[k.e2]); for i := 1 to n do if c[i] = max then c[i] := min end end; Afisare end.

program Prim;
const NMaxVf = 20; NMaxMuchii = NMaxVf*(NMaxVf-1) div 2; Inf = MaxLongInt; type Vf = 1..NMaxVf; Graf = array[Vf,Vf] of real; var n, r, i, VfMin: Vf; G: Graf; p: array[Vf] of 0..NMaxVf; Z: set of Vf; key: array[Vf] of real; KeyMin: real; procedure Initializare; var i, j, k, nrv: Vf; c: real; fin: text; begin assign(fin, 'prim.in'); reset(fin); readln(fin, n, r); for i := 1 to n do for j := 1 to n do G[i,j] := Inf; for i := 1 to n do begin G[i,i] := 0; read(fin, nrv); {nrv-numarul de varfuri adiacente cu i} for j := 1 to nrv do begin {citesc varfurile adiacente cu i si costurile muchiilor corespunzatoare} read(fin, k, c); G[i,k] := c; end; readln(fin);

215

end; for i := 1 to n do begin key[i] := G[r, i]; p[i] := r end; Z := [1..n]-[r]; p[r] := 0; close(fin); end; procedure AfisareAPM; var i: Vf; cost: real; begin cost := 0; writeln('Muchiile APM sunt: '); for i := 1 to n do if i <> r then begin write('[',p[i],',',i,'] '); cost := cost+G[i,p[i]]; end; writeln; writeln('Costul APM ', cost:7:2); readln end; begin Initializare; while Z <> [] do begin KeyMin := Inf; {determin nodul din Z de cheie minima} for i := 1 to n do if (i in Z) and (KeyMin > key[i]) then begin KeyMin := key[i]; VfMin := i end; Z := Z-[VfMin]; for i := 1 to n do {actualizez cheile varfurilor din Z} if (i in Z) and (G[i, VfMin] < key[i]) then begin p[i] :=VfMin; key[i] := g[i, VfMin] end end; AfisareAPM end.

program Arbori_Partiali_BF_DF;
const NrMaxVf = 20; type Vf = 0..NrMaxVf; Lista = ^NodLista;

216

var

NodLista = record inf: Vf; urm: Lista; end; Graf = array[Vf] of Lista; Arbore = array[Vf] of Vf; n: Vf;{numarul de varfuri din graf} s: Vf;{varful sursa} G: Graf; AB, AD: Arbore;{arborii partiali obtinuti prin parcurgere BF, respectiv DF} V: array[Vf] of boolean;

procedure Initializare; var fin: text; i, j: Vf; p: Lista; begin assign(fin, 'graf.in'); reset(fin); readln(fin ,n); readln(fin, s); for i := 1 to n do begin G[i] := nil; V[i] := false; while not seekeoln(fin) do begin read(fin, j); new(p); p^.inf := j; p^.urm := G[i]; G[i] := p; end; readln(fin); end; V[s] := true; close(fin); end; procedure DFS(x: Vf); var q: Lista; begin q := G[x]; while q <> nil do {parcurg lista de adiacenta a lui x} begin if not V[q^.inf] then {varful q^.inf este nevizitat} begin V[q^.inf] := true; AD[q^.inf] := x; DFS(q^.inf); end; q := q^.urm; end; end; procedure BFS; type Coada = ^NodCoada; NodCoada = record inf: Vf; urm: Coada end;

217

var C, SfC: Coada; q: Lista; x: Vf; p: Coada; begin {initializez coada} new(C); C^.inf := s; C^.urm := nil; SfC := C; for x := 1 to n do V[x] := false; V[s] := true; while C <> nil do begin x := C^.inf;{primul varf din coada} q := G[x]; while q <> nil do {parcurg lista de adiacenta a lui x} begin if not V[q^.inf] then {varful q^.inf nevizitat} begin V[q^.inf] := true; AB[q^.inf] := x; {inserez q^.inf in coada} new(p); p^.inf := q^.inf; p^.urm := nil; SfC^.urm := p; SfC := p; end; q := q^.urm; end; p := C; C := C^.urm; dispose(p);{am extras din coada vaful x} end; end; procedure AfisareArbore(A: Arbore); var i: Vf; begin for i := 1 to n do if A[i] <> 0 then write('[',i,',',A[i],'] '); writeln; end; begin {program principal} Initializare; DFS(s); BFS; writeln('Arborele partial DFS este:'); AfisareArbore(AD); writeln('Arborele partial BFS este:'); AfisareArbore(AB); readln; end.

program Componente_Biconexe;
const type NMaxVf = 20; Vf = -1..NMaxVf; Stiva = ^NodStiva; NodStiva = record f, t: Vf; urm: Stiva

218

var

end; Lista = ^NodLista; NodLista = record v: Vf; leg: Lista; end; Graf = array[0..NMaxVf] of Lista; S: Stiva; G: Graf; low, dfn: array[0..NMaxVf] of Vf; nr, n, sursa:Vf; num: 0..NMaxVf;

procedure Initializare; var ns: Vf; i, j: Vf; p: Lista; fin: text; begin assign(fin, 'biconex.in'); reset(fin); readln(fin, n); readln(fin, sursa); for i := 0 to n do begin G[i] := nil; readln(fin, ns); for j := 1 to ns do begin new(p); read(fin, p^.v); p^.leg := G[i]; G[i] := p; end; readln(fin); dfn[i] := -1 end; close(fin); new(S); S^.f := sursa; S^.t := -1; S^.urm := nil; end; procedure Afisare_Comp_Biconexa(x, u: Vf); var p: Stiva; a, b: Vf; begin inc(nr);{nr-numarul de componente biconexe} writeln('muchiile componentei biconexe ',nr,' sunt:'); repeat p := S; a := p^.t; b := p^.f; S := S^.urm; write('(',a,',',b,') '); dispose(p); until (a = u) and (b = x); writeln end; function min (a, b: Vf): Vf; begin if a < b then min := a else min := b end; procedure Biconex(u, tu: Vf); {calculeaza dfn si low si afiseaza componentele biconexe}

219

var p: Lista; q: Stiva; x: Vf; begin dfn[u] := num; low[u] := dfn[u]; inc(num); p := G[u]; while p <> nil do {parcurg lista de adiacenta a lui u} begin x := p^.v; if (x <> tu) and (dfn[x] < dfn[u])then begin{insereaza in stiva S muchia(u,x)} new(q); q^.f := x; q^.t := u; q^.urm := S; S := q; end; if dfn[x] < 0 then {x nu a mai fost vizitat} begin Biconex (x, u); low[u] := min(low[u], low[x]); if low[x] >= dfn[u] then {u punct de articulatie; am identificat o componenta biconexa, ce contine muchiile din stiva S pana la (u,x)} Afisare_Comp_BiconexA(x,u); end else if x <> tu then low[u] := min(low[u], dfn[x]); p:=p^.leg end; end; begin {program principal} Initializare; Biconex(sursa, -1); readln end.

220

Вам также может понравиться