Documente Academic
Documente Profesional
Documente Cultură
B5 Cap 3
B5 Cap 3
GRAFURI ORIENTATE
3.1 Introducere
V = {x1 , x2 ,..., xn }
G (V , E ) =
E = {(a, b) / a, b ∈ V , (a, b) ≠ (b, a )}
163
3.2 NoŃiuni de bază
164
3.3. Drum, circuit, tare-conexitate
Un graf tare conex este un graf în care există cel puŃin un drum
între oricare două vârfuri.
Dacă un graf nu este tare conex, el este format din componente
tare conexe. O componentă tare conexă este un subgraf maximal tare
conex al unui graf dat. Graful din figura 3.2 are trei componente conexe
formate din următoarele submulŃimi de vârfuri: {1}, {2,5}, {3,4,6}
165
3
1
2
4
5 6
fig.3.2: Graf orientat cu 3 componente tare conexe
166
3.4 Metode de reprezentare a digrafurilor
0 0 0 0 0 0
1 0 0 1 1 0
A= 0 0 0 1 0 0
0 0 0 0 0 1
0 1 0 1 0 1
0 0 1 0 0 0
167
3.4.2 Liste de adiacenŃă
168
3.4.3 Lista de arce
169
//program 3.1 metode de reprezentare a digrafurilor
//determinare componente tare conexe
#include <iostream>
#include <fstream>
#include <deque>
#include <algorithm>
#include <vector>
#define nmax 101
using namespace std;
int n,m;
bool A[nmax][nmax]; //matricea de adiacenta
vector <pair <int,int> > LARCE; //lista de arce
deque <int> LA[nmax]; //listele de adiacenta
int ctcx[nmax];
void citiremat(bool A[nmax][nmax], int &n, int &m,
char numefisier[])
{ ifstream fin (numefisier);
fin >> n >> m;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
A[i][j]=0;
for (int i=1;i<=m;i++)
{
int x,y;
fin >> x >> y;
A[x][y] = 1;
}
fin.close();
}
void afisaremat(bool A[nmax][nmax], int n)
{ for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{ cout << A[i][j]<<' ';
if (j==n)
cout << endl;
}
}
int gradinternma(bool A[nmax][nmax], int n, int
varf)
{ int s=0,i;
if (varf<1 || varf > n)
{ cout << "varf inexistent" << endl;
return -1;
}
for (i=1;i<=n;i++)
170
s+=A[i][varf];
return s;
}
void citirela(deque <int> LA[], int &n, int &m, char
numefisier[])
{ ifstream fin (numefisier);
fin >> n >> m;
for (int i=1;i<=m;i++)
{ int x,y;
fin >> x >> y;
LA[x].push_back(y);
}
for (int i=1;i<=n;i++)
sort(LA[i].begin(), LA[i].end());
fin.close();
}
void afisaredeq(deque <int> q)
{ for (int i=0;i<q.size();i++)
cout << q[i] << ' ';
cout << endl;
}
int gradinternla(deque <int> LA[], int n, int varf)
{ if (varf<1 || varf > n)
{ cout << "varf inexistent" << endl;
return -1;
}
int s=0;
for (int i=1;i<=n;i++)
s +=
binary_search(LA[i].begin(),LA[i].end(),varf);
return s;
}
bool existaarcla(deque <int> LA[], int &n, int v1,
int v2)
{ if (v1<1 ||v1>n ) return false;
if (v2<1 ||v2>n ) return false;
return
binary_search(LA[v1].begin(),LA[v1].end(),v2);
}
bool existaarcma(bool A[nmax][nmax], int n, int v1,
int v2)
{ if (v1<1 ||v1>n ) return false;
if (v2<1 ||v2>n ) return false;
return A[v1][v2];
}
171
void citirelarce(vector <pair <int,int> > &LM, int
&n, int &m, char numefisier[])
{ ifstream fin (numefisier);
fin >> n >> m;
for (int i=1;i<=m;i++)
{ pair <int,int> aux;
fin >> aux.first >> aux.second;
LM.push_back(aux);
}
fin.close();
}
void afisarelarce(vector <pair <int,int> > LM)
{ int i;
for (i=0;i<LM.size()-1; i++)
cout<< LM[i].first << '-' << LM[i].second<<" ; ";
cout << LM[i].first << '-' << LM[i].second<< endl;
}
bool viz1[nmax], viz2[nmax];
void dfsd(int x)
{ viz1[x] = true;
for (int i=1;i<=n;i++)
if (A[x][i]==1 && viz1[i] == false)
{ viz1[i]=true;
dfsd(i);
}
}
void dfsi(int x)
{ viz2[x] = true;
for (int i=1;i<=n;i++)
if (A[i][x]==1 && viz2[i] == false)
{ viz2[i]=true;
dfsi(i);
}
}
int main()
{ citiremat(A,n,m,"graf.in");
cout << "Matricea de adiacenta\n";
afisaremat(A,n);
cout<< lista gradelor interne din matrice:\n"<<;
for (int k=1;k<=n;k++)
cout<<k<<":"<<gradinternma(A,n,k)<<" ";
cout<<endl;
cout << "\nListe de adiacenta\n";
citirela(LA,n,m,"graf.in");
172
for (int i=1;i<=n;i++)
{ cout << i << " : ";
afisaredeq(LA[i]);
}
cout << endl;
cout << "lista gradelor interne: " << endl;
for (int k=1;k<=n;k++)
cout<<k<<":"<<gradinternla(LA,n,k)<<" ";
cout << endl;
cout << "\nLista arcelor:\n";
citirelarce(LARCE,n,m,"graf.in");
sort(LARCE.begin(), LARCE.end());
afisarelarce(LARCE);
int v1,v2;
cout<<"dati 2 noduri (intre 1 si "<<n<<"): ";
cin >> v1 >> v2;
bool rez = existaarcma(A,n,v1,v2);
if (rez)
cout<<"exista arcul intre cele doua varfuri\n";
else
cout << "nu exista arc intre aceste varfuri\n";
bool rez1 = existaarcla(LA,n,v1,v2);
if (rez1)
cout<<"exista arcul intre cele doua varfuri\n";
else
cout << "nu exista arcul intre aceste varfuri\n";
int k=0;
for (int i=1;i<=n;i++)
{ if (ctcx[i]==0)
{ k++;
cout<<"componenta tare conexa "<<k<<endl;
fill(viz1+1,viz1+n+1,false);
fill(viz2+1,viz2+n+1,false);
dfsd(i);
dfsi(i);
for (int j=1;j<=n;j++)
if (viz1[j] == true && viz2[j]==true)
{ ctcx[j]=k;
cout << j << ' ';
}
cout << endl;
}
}
return 0;
}
173
Pentru digraful din figura 3.2 programul va afişa în fereastra consolă
următoarele:
Matricea de adiacenta
0 0 0 0 0 0
1 0 0 1 1 0
0 0 0 1 0 0
0 0 0 0 0 1
0 1 0 1 0 1
0 0 1 0 0 0
lista gradelor interne din matrice:
1:1 2:1 3:1 4:3 5:1 6:2
Liste de adiacenta
1 :
2 : 1 4 5
3 : 4
4 : 6
5 : 2 4 6
6 : 3
lista gradelor interne:
1:1 2:1 3:1 4:3 5:1 6:2
Lista arcelor:
2-1 ; 2-4 ; 2-5 ; 3-4 ; 4-6 ; 5-2 ; 5-4 ; 5-6 ; 6-3
174
3.5 Drumuri de cost minim în grafuri orientate
4 2
1
9 8 6 9 9
4 2
3 3
1 6
3 1 7
7
5 2
fig.3.3: graf ponderat
j
cij
i ckj
cik k
A) Matricea ponderilor
0 pentru i = j
hi , j = ∞ daca nu exista arcul (i, j)
C(i, j) daca exista arcul (i, j)
0 469 ∞ ∞
∞ 09 ∞ ∞ 9
∞ ∞ 011 7
H=
8 ∞ 20 3 ∞
∞ ∞ 7 ∞ 0 2
∞ ∞ 3 ∞ ∞ 0
177
Tabloul d ne dă valoarea drumului de cost minim de la vârful sursă la
fiecare dintre celelalte vârfuri ale grafului iar tabloul T ne permite să
determinăm arcele din care sunt formate aceste drumuri.
Algoritmul mai utilizează un tablou v cu semnificaŃia:
• v[i] = true dacă vârful i a fost analizat şi
false în caz contrar.
178
p=1
2 4 2
1 1
9 6
4 4
3 6 3 6
5 5
d: 0 oo oo oo oo oo d: 0 4 6 9 oo oo
T: 0 -1 -1 -1 -1 -1 T: 0 1 1 1 -1 -1
v: f f f f f f v: t f f f f f
p=2 p=3
4 2 4 2
1 1
9 6 9 6 9
4 4
3 6 1 3 6
1
5 5
d: 0 4 6 9 oo 13 d: 0 4 6 7 7 13
T: 0 1 1 1 -1 2 T: 0 1 1 3 3 2
v: t t f f f f v: t t t f f f
p=5 p=4
4 4 2
1 1
6 6
4 4
1 3 6 1 3 6
1 1
5 2 5 2
d: 0 4 6 7 7 9 d: 0 4 6 7 7 9
T: 0 1 1 3 3 5 T: 0 1 1 3 3 5
v: t t t f t f v: t t t t t f
Fig. 3.5 Desfăşurarea algoritmului Dijsktra
179
using namespace std;
int h[nmax][nmax],T[nmax],d[nmax];
bool v[nmax];
int s,i,j,k,p,n,m;
int oo = 1000000;
ifstream fin("grafponderat.in");
void afisaretab(int a[], int n)
{for (i=1;i<=n;i++)
{ cout.width(6); cout << a[i];}
cout << endl;
}
int main()
{ fin >> n >> m;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (i==j)h[i][j] = 0;
else h[i][j] =oo;
for (i=1;i<=m;i++)
{ int x,y,cost;
fin >> x >> y >> cost;
h[x][y] = cost;
}
for (i=1;i<=n;i++)
{ for (j=1;j<=n;j++)
{cout.width(4);
if (h[i][j]==oo) cout <<"oo";
else cout << h[i][j];
}
cout << endl;
}
cout << endl;
fill(v,v+n+1,false);
fill(d,d+n+1,oo);
fill(T,T+n+1,-1);
cout << "sursa = "; cin >> s;
d[s]=0; T[s]=0;
for (k=1;k<=n-1;k++)
{ int minim = oo;
for (i=1;i<=n;i++)
if (d[i] <= minim && v[i]==false)
minim=d[i],p=i;
v[p] = true;
for (i=1;i<=n;i++)
if (d[i] > d[p] + h[p][i])
{ d[i] = d[p] + h[p][i];
180
T[i] = p;
}
}
cout << "tabloul d:"<<endl;
afisaretab(d,n);
cout << "tabloul T:"<<endl;
afisaretab(T,n);
return 0;
}
Pentru digraful din figura 3.3 programul va afişa în fereastra consolă
următoarele:
0 4 6 9 oo oo
oo 0 9 oo oo 9
oo oo 0 1 1 7
8 oo 2 0 3 oo
oo oo 7 oo 0 2
oo oo 3 oo oo 0
sursa = 1
tabloul d:
0 4 6 7 7 9
tabloul T:
0 1 1 3 3 5
181
Algoritmul este următorul:
182
//program 3.3 Algoritmul Roy-Floyd
#include <iostream>
#include <fstream>
#include <stack>
#define nmax 101
using namespace std;
int h[nmax][nmax],T[nmax][nmax],D[nmax][nmax];
int oo = 100000;
void afisardrum(int v1, int v2, stack<int>&st)
{ while (v2 != 0)
{ st.push(v2);
v2 = T[v1][v2];
}
}
void afisarematrice(int a[nmax][nmax], int n)
{for (int i=1;i<=n;i++)
{ for (int j=1;j<=n;j++)
{cout.width(4);
if (a[i][j]==oo) cout <<"oo";
else cout << a[i][j];
}
cout << endl;
}
}
int main()
{ int s,i,j,k,p,n,m;
ifstream fin("ponderi.in");
stack <int> st;
fin >> n >> m;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (i==j)h[i][j] = 0;
else h[i][j] =oo;
for (i=1;i<=m;i++)
{ int x,y,cost;
fin >> x >> y >> cost;
h[x][y] = cost;
}
//afisarematrice(h,n);
cout << endl;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
{ D[i][j] = h[i][j];
if (D[i][j] != oo && i!=j)
T[i][j] = i;
183
}
for (k=1;k<=n;k++)
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (D[i][j] > D[i][k] + D[k][j])
{ D[i][j] = D[i][k] + D[k][j];
T[i][j] = T[k][j];
}
cout << "Matricea D"<<endl;
afisarematrice(D,n);
cout << "Matricea T"<<endl;
afisarematrice(T,n);
cout << "\nDrumul de la varful 5 la varful 1\n";
afisardrum(5,1,st);
while (!st.empty())
{ cout << st.top() << ' ';
st.pop();
}
return 0;
}
184
j. Pentru aceasta se atribuie fiecărui arc din G costul 1 şi în urma
aplicării algoritmului Roy-Floyd se foloseşte matricea D astfel:
dacă D[i][j]<n atunci
există drum de la vârful i la vârful j.
185
3.6 Flux maxim într-o reŃea de transport
3.6.1 DefiniŃii
2 7 3
5 4
3 3 2
1 4 5 9
3 5 9
6 4 6
7 8
Γ = ∑ϕ ( srs, j )
j∈V
adică suma fluxurilor asociate arcelor care pleacă din vârful sursă.
Evident această valoare va fi egală cu suma fluxurilor asociate arcelor
care intră în vârful destinaŃie.
Figura 3.7 ilustrează un flux pentru reŃeaua de transport din fig. 3.6.
Valorile fluxului ataşat fiecărui arc sunt scrise în chenar. Valoarea
fluxului din reŃeaua ilustrată în această figură este 6.
2 7 3
5 1
1 4 1
2 3 2
1 4 5 9
4 1 2
3 1 5 3
4 6 4
6 7 8
1 4
fig.3.7: reŃea de transport
188
fluxului dacă se caută la fiecare iteraŃie drumul de lungime minimă.
Pentru aceasta, drumurile în creştere se vor determina printr-un
algoritm foarte asemănător cu cel de traversare în lăŃime. Deosebirea
faŃă de cel prezentat în capitolele anterioare este că în acest caz se va
putea parcurge un arc şi în sens invers dacă fluxul asociat lui la
momentul efectuării parcurgerii este strict pozitiv. Complexitatea
algoritmului în acest caz este O(n×m2). O complexitate și mai bună
este dată de algoritmul de preflux [8] care a condus la algoritmul de flux
maxim prezentat de Goldberg şi Tarjan cu complexitatea O(n2×m).
Să ilustrăm aplicarea algoritmului Edmonds–Karp pentru reŃeaua
de transport din figura 3.6:
2 7 3
5 0
0 4 0
2 3 2
1 4 5 9
0 0 0
3 0 5 0
9 0
6 4 6
7 8
0 0
2 7 3
5 0
0 4 0
2 3 2
1 4 5 9
2 2 2
3 0 5 0
9 0
6 4 6
7 8
0 0
fig.3.8.b: flux actualizat după determinarea primului drum în creştere.
Drumul în creştere de lungime minimă pentru această configuraŃie este
1,6,7,8,9. (ObservaŃie: Drumul 1,2,3,5,9 nu este în creştere deoarece arcul
(5,9) este „saturat”: valoarea fluxului este egală cu valoarea capacităŃii).
Valorile reziduale ale arcelor care îl compun sunt: (1,6):3, (6,7):4, (7,8):6,
(8,9):9 => valoarea reziduală a drumului în creştere este 3.
189
2 7 3
5 0
0 4 0
2 3 2
1 4 5 9
2 2 2
3 3 5 0
9 3
6 2 6
7 8
3 3
fig.3.8.c: fluxul după determinarea celui de-al doilea drum în creştere.
Drumul în creştere de lungime minimă pentru această configuraŃie este
1,2,3,5,4,7,8,9. (ObservaŃie: arcul 5,4 este parcurs în sens invers!). Valorile
reziduale ale arcelor care îl compun sunt: (1,2):5, (2,3):7, (3,5):4, (5,4):2,
(4,7):5, (7,8):3, (8,9):6 => valoarea reziduală a drumului în creştere este 2.
2 7 3
5 2
2 4 2
2 3 2
1 4 5 9
2 0 2
3 3 5 2
9 5
6 2 6
7 8
3 5
fig.3.8.d: fluxul după determinarea celui de-al treilea drum în creştere. Nu
mai există drumuri în creştere, deci fluxul maxim din reŃea este 7.
192
gasit un drum de lungime 3 capacitate reziduala 2
format din arcele: 5-9 , 4-5 , 1-4 ,
193
3.7 Sortarea topologică
194
lenjerie 7 ciorapi 5
15/16 9/12
ceas 8
17/18
pantaloni 6 pantofi 9
13/14 10/11
curea 3 cămaşă 2
6/7 5/8
cravată 1
1/4
sacou 4
2/3
int culoare[nmax];
int T[nmax]; //pentru memorarea predecesorilor
int tg[nmax], tn[nmax], timp;
int n,m;
deque <int> LA[nmax];
list <string> listatopo;
195
string numeactiune[nmax] =
{"","cravata","camasa","curea","sacou","ciorapi",
"pantaloni", "lenjerie","ceas","pantofi"
};
void citirelistead(deque <int> LA[], int &n, int &m,
char numefisier[])
{ ifstream fin (numefisier);
fin >> n >> m;
for (int i=1;i<=m;i++)
{
int x,y;
fin >> x >> y;
LA[x].push_back(y);
}
for (int i=1;i<=n;i++)
sort(LA[i].begin(), LA[i].end());
fin.close();
}
196
int main()
{
citirelistead(LA,n,m,"digraf.in");
fill(culoare, culoare+n+1, alb);
timp = 0;
for (int x=1;x<=n;x++) /*1*/
if (culoare[x] == alb)
{
dfs(x,listatopo);
}
cout << "nodurile sortate topologic:\n";
afisarelist(listatopo);
return 0;
}
197
3.8 Probleme propuse
0 ∞ ∞ ∞ ∞ 1 5 12 ∞
11 0 ∞ 13 7 ∞ 4 2 ∞
1 ∞ 0 9 8 3 ∞ ∞ ∞
6 8 ∞ 0 ∞ ∞ ∞ 8 2
13 2 ∞ ∞ 0 8 ∞ 13 ∞
13 12 ∞ 10 ∞ 0 ∞ ∞ 3
∞ 8 ∞ ∞ ∞ 8 0 ∞ 3
9 4 1 ∞ ∞ 6 ∞ 0 9
∞ 8 ∞ ∞ 3 ∞ ∞ ∞ 0
198
Problema 4. ConcepeŃi un algoritm pentru determinarea drumurilor de
cost minim care leagă un unic vârf sursă cu toate celelalte vârfuri din
digraf mai eficient decât algoritmul Dijsktra.
(IndicaŃie: sortaŃi topologic vârfurile grafului, apoi parcurgeŃi structura
liniară rezultată şi aplicaŃi operaŃiunea de relaxare fiecărui vârf spre
care pleacă câte un arc din vârful curent.)
0 5 8 6 9 1 1 2 6
3 0 8 2 2 1 2 2 7
3 5 0 6 3 1 1 2 6
4 5 8 0 9 1 1 4 4
3 5 8 2 0 1 2 2 7
3 5 8 6 9 0 2 2 6
3 5 8 6 9 7 0 2 7
3 8 8 6 3 1 1 0 6
3 5 8 2 9 1 2 2 0
199
- pe următoarele m linii câte două numere v1 şi v2 1≤v1,v2≤n,
reprezentând câte o muchie a grafului;
DeterminaŃi un cuplaj maxim în acest graf.
(IndicaŃie: transformaŃi graful într-o reŃea de transport în care muchiile
vor fi orientate de la una dintre partiŃii la cealaltă partiŃie – vezi
observaŃiile finale de la subcapitolul 3.4.1. Fiecare muchie
(transformată deci într-un arc) va avea capacitate egală cu 1. CalculaŃi
fluxul maxim din această reŃea iar valoarea acestuia va reprezenta
numărul de muchii din cuplajul maxim).
200