Sunteți pe pagina 1din 38

+

Algoritmi și tehnici de
programare

Cursul 10
+
Grafuri
◼ Drumuri în grafuri
◼ Matricea existenţei drumurilor
◼ Algoritmul Roy-Warshall
◼ Componente conexe ale unui graf
◼ Algoritmul Roy-Floyd
◼ Algoritmul lui Dijkstra
+
Grafuri
◼ FieG=(V, E) un graf orientat. Graful G se numeşte
graf antisimetric dacă pentru oricare două
vârfuri din graf x şi y dacă există arcul (x,y), atunci
nu există arcul (y,x).
+
Grafuri
◼ Fie G=(V, E) un graf. Graful G se numeşte graf complet
dacă oricare două vârfuri distincte ale sale sunt adiacente.

◼ Un graf complet este un graf neorientat simplu în care


fiecare pereche de noduri distincte este conectată printr-
o muchie unică.

◼ Un digraf complet este un graf orientat în care fiecare


pereche de noduri distincte este conectată printr-o pereche
de muchii unice (una în fiecare direcție).
+
Grafuri
◼ Exemplu de graf neorietat complet:

◼ G=(V, E) un graf unde: V={1,2,3,4,5} E={(1,2),


(1,3), (1,4), (1,5), (2,3), (2,4), (2,5), (3,4), (3,5),
(4,5)}

◼ Reprezentarea sa grafică este:

◼ Un graf neorientat complet cu n varfuri are


n(n − 1) muchii .
2
+
Grafuri
◼ Un graf turneu este un graf orientat obținut prin
atribuirea unei direcții fiecărei muchii dintr-un graf
neorientat complet.

◼ Un graf orientat este turneu, dacă oricare ar fi


două vârfuri x şi y, x≠y, între ele există un singur
arc: arcul (x,y) sau arcul (y,x).
+
Grafuri
◼ Fie
G=(V, E) un graf. Se numeşte lanț, în graful G
neorientat, o succesiune de arce, notată L =
(e0, e1, .., en ) cu proprietatea ca oricare două
muchii consecutive au o extremitate comună (nu
are importantă orientarea arcelor).

◼ Lungimea unui lanț este egală cu numărul de


muchii din care este alcătuit.

◼ Primul
nod și ultimul nod dintr-un lanț formează
extremitățile lanțului.
+
Grafuri
◼ Unlanţ este elementar dacă el nu conţine de mai
multe ori acelaşi vârf.

◼ Unlanţ este simplu dacă el nu conţine de mai


multe ori aceeaşi muchie.

◼ Se numeşte ciclu un lanţ simplu pentru care


extremitatea iniţială coincide cu extremitatea finală.

◼ Ciclulse numeşte elementar dacă nu conţine de


mai multe ori acelaşi vârf (exceptândn extremităţile
sale).
+
Lanț în grafuri
+
Drumuri în grafuri

◼ Fie G=(V,E) un graf orientat, u,vV. Secvenţa de


vîrfuri  : u0, u1, .., un este un drum dacă u=u0 ,
un=v , uiui+1E pentru toţi i.
+
Drumuri în grafuri

◼ Lungimea unui drum este egală cu numărul de


arce din care este alcătuit.

◼ Lungimea drumului, notată l().

◼ Convenţional se numeşte drum trivial un drum  cu


l() =0.
+
Drumuri în grafuri
◼ Drumulse numeşte elementar dacă nu conţine de
mai multe ori acelaşi vârf.

◼ Drumul se numeşte simplu (proces) dacă nu


conţine de mai multe ori același arc.

◼ Senumeşte circuit un drum simplu pentru care


extremitatea iniţială coincide cu extremitatea finală.

◼ Circuitul
se numeşte elementar dacă nu conţine
de mai multe ori acelaşi vârf (exceptând
extremităţile sale).

◼ Buclă – un circuit format dintr-un singur arc.


+
Drumuri în grafuri

◼ Fie : u0, u1, ..., un un drum în graful G=(V,E).


◼ este un drum închis dacă u0=un; în caz contrar  se
numeşte drum deschis.
+
Drumuri în grafuri
+
Grafuri
◼ Un lanţ/drum/ciclu/circuit elementar se numeşte
hamiltonian dacă el trece prin toate vârfurile
grafului.

Lanț hamiltonian:1,2,3,5,4
Ciclu hamiltonian:1,2,3,5,4,1

• Un graf neorientat se numește graf hamiltonian dacă conține


un ciclu hamiltonian.
+
Grafuri
◼ Un lanţ/drum/ciclu/circuit elementar se numeşte
eulerian dacă el trece prin fiecare muchie/arc a/al
grafului o singură dată.

1 2

Ciclu eulerian 1,2,3,1


+
Grafuri

◼ Fie : u0, u1, ..., un un drum în graful G=(V,E).

◼ ’ : v0, v1,..,vm este un subdrum al lui  dacă ’


este un drum şi pentru orice j, 0 ≤ j ≤ m , există i,
astfel încît ui = vj.

◼ Orice drum cu lungime cel puţin 1 conţine cel puţin


un drum elementar cu aceleaşi extremităţi.
+
Matricea existenţei
lanțurilor/drumurilor
◼ Fie G=(V,E) un graf, V = n . Dacă A este matricea de
adiacenţă asociată grafului, atunci:
◼ 𝐴𝑝 indică numărul de drumuri distincte de lungime p între oricare
2 vîrfuri. (pentru orice p≥1)

◼ Fie operaţiile binare de adunare şi înmulţire pentru matrice


binare. Notând A=(aij), B=(bij), atunci: a b ab a b ab
◼ A ⨁ B=(cij), A ⨂ B=(dij), pentru 0 ≤ i , j ≤ n 0 0 0 0 0 0

cij=max{aij, bij}
0 1 1 0 1 0
1 0 1 1 0 0

dij=max{min{aik, bkj}, 0 ≤ k ≤ n}
1 1 1 1 1 1

◼ 𝐴ҧ𝑝 indică existența unui drum de lungime p între oricare 2 vîrfuri.

◼ Matricea obţinută adunând puterile de la 1 la n-1 ale matricei


de adiacenţă constituie matricea existenţei drumurilor în graf.
◼ ҧ - matricea existenței drumurilor în graful G.
𝑀 = 𝐴1ҧ ⨁𝐴2ҧ ⨁ … ⨁𝐴𝑛−1
+
Matricea existenţei
lanțurilor/drumurilor
◼ Fiind dat un graf neorientat G=(X,U) prin matricea de adiacenţă, se cere
să se determine toate perechile de vârfuri între care există cel puţin un
lanţ.

◼ Pentru rezolvarea acestei probleme se poate folosi algoritmul Roy-


Warshall care, plecând de la matricea de adiacenţă a unui graf
construieşte matricea lanţurilor definită astfel:

◼ a[i][j]= 1, dacă există un lanţ de la i la j


0, în caz contrar
+
Matricea existenţei
lanțurilor/drumurilor

0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1
       
1 1 0 0 0 2 1 1 1 1 3  1 1 1 1 1 1 1 1
A= A =  , A = , A = , M =
1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1
       
1 0 1 0  1 1 1 1 1 1 1 1 1 1 1 1
   

Initial matricea lanturilor este chiar matricea de adiacenta, si apoi daca


exista lant de la i la k si lant de la k la j atunci exista lant de la i la j.
+
Algoritmul Roy-Warshall
int **Roy_Warshall (int **a, int n)
{
int i,j,k;
int **m;

m = (int**) malloc(n*sizeof(int*));
for(i=0;i<n;i++)
*(m+i)= (int*) malloc(n*sizeof(int));

for(i=0;i<n;i++)
for(j=0;j<n;j++)
m[i][j]=a[i][j];

for (k=0;k<n;k++)
for (i=0;i<n;i++)
for (j=0;j<n;j++)
if(m[i][j]==0) m[i][j]=m[i][k]*m[k][j];
return m;
}
+
Componente conexe ale unui graf

◼ Fie 𝐺 = (𝑉, 𝐸) graf netrivial. Vîrfurile 𝒖 și 𝒗 sînt


conectate dacă există un u-v drum/lanț în 𝐺.
◼ Un graf este conex dacă este format dintr-un
singur nod sau dacă între oricare două noduri ale
sale există cel puțin un lanț.
+
Componente conexe ale unui graf

◼ Dacă G este un graf, atunci o componentă conexă a lui G este


un subgraf conex al lui G, maximal în raport cu
proprietatea de conexitate.

◼ Un graf este conex dacă şi numai dacă numărul


componentelor sale conexe este 1.

◼ Mulţimile de vârfuri corespunzătoare oricăror două componente


conexe distincte sunt disjuncte.

◼ Orice vârf izolat este considerat componentă conexă.


+
Componente conexe ale unui graf
◼ Algoritm pentru determinarea unei componente
conexe

◼ I: 𝑢0 , 𝐺, 𝑛
◼ E: 𝐶 = (𝑉𝑐 , 𝐸𝑐 )

◼ Se iniţializează componenta vidă 𝑉𝑐 = ∅, 𝐸𝑐 = ∅


◼ Se adaugă la mulţimea de vîrfuri vîrful iniţial 𝑉𝑐 = 𝑉𝑐 ∪ 𝑢0
◼ Repetă
◼ Se caută toţi vecinii noi ai vîrfurilor din 𝑉𝑐 şi se adaugă la
mulţimea de vîrfuri
◼ Se caută toate muchiile noi dintre vîrfurile din 𝑉𝑐 şi se adaugă la
mulţimea de muchii 𝐸𝑐
◼ Pînă cînd nu se mai găsesc vîrfuri noi
+
Componente conexe ale unui graf
◼ Teme:

◼ Să se determine dacă un graf este conex.


◼ Să se determine numărul de componente conexe ale unui
graf.
◼ folosind algoritmul de parcurgere în lungime;

◼ folosind algoritmul de parcurgere în adâncime;

◼ folosind algoritmul Roy_Warshal.


+
Drumuri de cost minim
◼ Fie G=(V,E,w) un graf ponderat.

◼ Costul drumului Γ: u1,u2,..,un, notat L(Γ), este definit prin:

◼ Pentru orice u şi v vârfuri conectate în G, u ≠ v, w-distanţa între


u şi v, notată D(u,v), este definită prin,

◼ unde D(u,v ) desemnează mulţimea tuturor u-v drumurilor elementare


din G.

◼ Dacă Γ ∈ D este astfel încât D(u,v)=L(Γ), drumul Γ se numeşte


drum de cost minim.
+
Algoritmul Roy-Floyd

◼ Fie G=(V, E) un graf neorientat, unde V are n elemente (n


noduri) si E are m elemente (m muchii) memorat prin
matricea ponderilor.

◼ Se cere ca pentru două noduri x, y citite să se determine


lungimea minima a lanțului de la x la y.
+
Algoritmul Roy-Floyd
+
Algoritmul Roy-Floyd
float ** Roy_Floyd (float **c, int n)
{
int i,j,k;
float **d;
d = (float**) malloc(n*sizeof(float*));
for(i=0;i<n;i++)
*(d+i)= (float*) malloc(n*sizeof(float));
for (i=0;i<n;i++)
for (j=0;j<n;j++)
d[i][j]=c[i][j];

for (k=0;k<n;k++)
for (i=0;i<n;i++)
for (j=0;j<n;j++)
if(d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j];
return d;
}
+
Algoritmul Dijkstra

◼ Acest algoritm se aplică în cazul problemelor în care se dă un graf


G=(V, E), care are matricea costurilor C, şi se cere să se determine
lungimea drumurilor minime de la un nod dat xo la celelate noduri
din graf.
+
Algoritmul Dijkstra

◼ Algortimul reține în mulțimea S nodurile care au fost deja selectate şi


într-o coadă S de priorități nodurile care nu au fost deja selectate
adică S =V-S, astfel:
◼ un nod k este selectat atunci când s-a determinat costul final al
drumului cu costul minim de la nodul sursa xo la el.
◼ în coada S prioritatea cea mai mare o are nodul pentru care
costul drumului are valoarea cea mai mică dintre toate costurile
de drumuri care pornesc de la nodul xo la celelalte noduri
neselectate încă.
◼ Pentru calcularea drumurilor de lungime minimă se folosește o
mulțime L în care se memorează costul drumurilor de la nodul x la
nodurile neselectate, costuri care se recalculează la fiecare extragere
de nod.
+
Algoritmul Dijkstra
◼ Pas 1. Se inițializează mulțimea S cu mulțimea vidă, se
citeşte nodul inițial xo şi se atribuie mulțimii S
◼ Pas 2. Se inițiază mulțimea L cu costurile drumurilor de la
nodul xo la toate celelalte noduri ale grafului
◼ Pas 3. Cât timp coada de priorități S nu este vidă execută:
❑ Pas 4. se caută printre nodurile neselectate nodul k cu cel
mai mic cost al drumului
❑ Pas 5. se adaugă nodul k la mulțimea S (se extrage din
coada de priorități S şi se declară ca nod selectat)
❑ Pas 6. Pentru fiecare nod neselectat execută:
➢ Pas 7. Se recalculează costul drumului de la nodul xo la
acest nod folosind ca nod intermediar nodul extras
➢ Pas 8. Dacă acest cost este mai mic decât cel din
mulțimea L, atunci el va fi noul cost.
+
Algoritmul Dijkstra
◼ fie 𝑮=(𝑽, 𝑬, 𝑾), 𝒙𝟎 ∈𝑽, graf conex
◼ etichete pentru fiecare vârf 𝒗 ∈𝑽 : ( 𝑳(𝒗), pred(𝒗) )

◼ S={𝒙𝟎 }
◼ S =𝑽−S
◼ pentru fiecare vârf 𝒗∈𝑽, 𝑳(𝒗)=∞//sau costul initial
◼ 𝑳(𝒙𝟎 )=𝟎, ped(𝒙𝟎 )=−𝟏

◼ determină un vârf 𝒗𝒌 ∈ S astfel încât 𝑳(𝒗𝒌 ) este minimă

◼ pentru 𝟏≤𝒊<𝒏
◼ pentru fiecare vârf 𝒗∈
S
◼ dacă 𝑳(𝒗) > 𝑳(𝒗𝒌 )+𝐰(𝒗𝒌 ,𝒗)
◼ 𝑳(𝒗)= 𝑳(𝒗𝒌 )+𝐰(𝒗𝒌 ,𝒗)
◼ pred(𝒗)=𝒗𝒌
◼ S=S∪{𝒗𝒌 }
◼ S = S−{𝒗𝒌 }
+
Algoritmul Dijkstra
+
Algoritmul Dijkstra
◼ Ne propunem sa determinam costul minim de la un vârf la toate celelalte vârfuri. Fie
acesta vârful x=1 (xo=x-1=0).

◼ Vom utiliza 3 vectori :

◼ Vectorul l care va reține lungimea drumurilor de la 1 la celelalte vârfuri. (l[i]=lungime


drum de la 1 la i). Inițial l reține :
∞ 2 ∞ 10 ∞

◼ Vectorul s, numit vector caracteristic va retine 1 pentru nodurile selectate ca


participante la drumul minim, altfel s este inițial 0 pentru toate nodurile mai puțin
pentru cel de pornire. Inițial va retine 1 doar pentru vârful de pornire , in cazul nostru
vârful 1.
1 0 0 0 0

◼ Vectorul pred , vector de tip tata/părinte care va indica drumurile găsite intre vârful 1 si
celelalte vârfuri astfel încât pentru fiecare vârf se va retine precedentul (tatăl/părintele).
Pentru vârful de pornire, 1 in cazul nostru, se retine -1. Inițial pred va retine :

-1 0 -1 0 -1
+
Algoritmul Dijkstra

◼ Pentru vârurile rămase, deci de n-1 ori se executa


secvența :
◼ Se determina vârf care are costul cel mai mic de la vârful
inițial - din vectorul L (valoarea minima din L) - k;
◼ Se marchează in s ca vizitat (s[k]=1) ;
◼ Verificăm dacă pentru nodul adăugat s-ar putea realiza
îmbunătățiri ale lungimii drumurilor. Adică pentru un vârf x
daca L[x]>L[k]+c[k][x] atunci se realizează modificarea
efectiv in L (L[x]=L[k]+c[k][x]) si se adaugă vârful k ca
fiind predecessor (tata) pentru x (pred[x]=k).
+
Algoritmul Dijkstra
void dijkstra(int **c, int n, int *l, int do
*pred, int x) {
{ m=inf;
int *s, k, r, i, m, g, x0; for(i=0;i<n;i++)
s = (int*)malloc(n * sizeof(int)); if((s[i]==0)&&(l[i]<m))
x0=x-1; {
for(i=0;i<n;i++) m=l[i]; k=i;
{ }
l[i]=c[x0][i]; r=r+1;
s[i]=0; if((l[k]==inf)||(r>n-1))g=1;
if(c[x0][i]!=inf) else
pred[i]=x0; {
else pred[i]=-1; s[k]=1;
} for(i=0;i<n;i++)
g=0; if((s[i]==0)&&(l[i]>l[k]+c[i][k]))
s[x0]=1; {
r=1; l[i]=l[k]+c[i][k];pred[i]=k;
}
}
}while(g!=1);
free(s);
}
+
Bibliografie

◼ I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C.


Uscatu, Programarea calculatoarelor. Ştiinţa învăţării
unui limbaj de programare, Teorie şi aplicaţii, Ed. ASE,
2003

◼ I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C.


Uscatu, M. Mircea, Programarea calculatoarelor.
Algoritmi în programare, Ed. ASE Bucureşti, 2007

◼ C. Uscatu, M. Popa, L. Bătăgan, C. Silvestru, Programarea


Calculatoarelor. Aplicații, Ed. ASE, 2012

S-ar putea să vă placă și