Documente Academic
Documente Profesional
Documente Cultură
1.Terminologie
Graf neorientat= o pereche de multimi =(V, E)unde V este o mulime
finit nevida de elemente numite noduri iar E o multime de perechi
neordonate din V, numite muchii. Notm graful cu G=(V, E).
Intr-un un graf G=(V, E) neorientat relaia binar este simetric:
(v,w)E atunci (w,v) E.
Nod = element al mulimii V, unde G=(V, E) este un graf neorientat.
Muchie = element al mulimii E ce descrie o relaie existent ntre dou
noduri din V, unde G=(V, E) este un graf neorientat;
1
In figura alaturata:
V={1,2,3,4,5,6} sunt noduri
E={[1,2], [1,4], [2,3],[3,4],[3,5]} sunt muchii
G=(V, E)=({1,2,3,4,5,6}, {[1,2], [1,4], [2,3],[3,4],[3,5]})
6
5
2
3
6
5
Nodurile 1, 2, 4 au gradele
egale cu 2.
Daca un graf neorientat are m muchii atunci suma gradelor tuturor
nodurilor este 2m
6
5
2
3
6
5
2
3
6
5
2
3
2
3
6
5
2
3
2
3
6
5
2
3
graf conex
6
5
2
3
6
5
2
3
6
5
2
3
nxn
cu
1, daca [i,j]E
a[i,j]=
0, altfel
Observatii:
6
5
2
3
A=
6
5
2
3
6
5
2
3
Nodul 3 : 2
Nodul 4 : 1
Nodul 5 : 3
q->next=L[j];
L[j]=q;
}
f.close();
cout<<endl<<"listele de adiacente ";
for(i=1;i<=n;i++)
afisare(i);
getch();
}
Observatie : In exemplul anterior adaugarea unui nou element in lista se face inainte
celorlalte din lista corespunzatoare.
Aceste doua moduri de reprezentare (prin matrice de adiacenta si prin liste de vecini) se
folosesc dupa natura problemei. Adica, daca in problema se doreste un acces frecvent la
muchii, atunci se va folosi matricea de adiacenta; daca numarul de muchii care se reprezinta
este mult mai mic dect nxn, este de preferat sa se folosesca listele de adiacenta, pentru a se
face economie de memorie.
Parcurgerea
Rezolvarea multor probleme de grafuri, presupune parcurgerea lor de la un anumit nod.
Pentru explorarea grafurilor, exista doua tipuri de algoritmi: de explorarea in latime si de
explorare in adancime.
Explorarea grafurilor in latime
La explorarea in latime, dupa vizitarea nodului initial, se exploreaza toate nodurile adiacente
lui, se trece apoi la primul nod adiacent si se exploreaza toate nodurile adiacente acestuia si
neparcurse inca, s.a.m.d.
Fiecare nod se parcurge cel mult odata (daca graful nu este conex nu se vor putea parcurge
toate nodurile)
De exemplul pentru garful din figura de mai jos, se va proceda in felul urmator:
se porneste din nodul 1, (se poate incepe de la oricare alt nod)
1
6
5
2
3
6
5
2
3
se obtine
1,2,4
dupa care din 2 se exploreaza nodul adiacent acestuia 3. Nodul 1 nu se mai viziteaza odata
1
6
5
2
3
se obtine 1,2,4,3
6
5
2
3
se obtine 1, 2, 4, 3, 5
int ndr;
cout<<endl<<"nodul de inceput ";
cin>>ndr;
viz[ndr]=1;
prim=ultim=1;
C[prim]=ndr;
cout<<endl<<"parcurgere in latime "<<endl;
bfi_lis();
for(i=1;i<=ultim;i++)
cout<<C[i]<<" ";
getch();
}
Functia recursiva :
void bfr_lis()
{int varf,nr;
nod *p;
if(prim<=ultim)
{varf=C[prim];
p=L[varf];// se parcurge lista elementelor din varful cozii
while(p)
{nr=p->nd;
if(viz[nr]==0)//numai daca nu a fost vizitat
{ultim++;//maresc coada
C[ultim]=nr;//il adaug in coada
viz[nr]=1;};//il marchez ca fiind vizitat
p=p->next;}
prim++; //avansez la urmatorul nod din coada
bfr_lis();
}
}
Parcurgerea grafurilor in adancime (depth first)
Parcurgerea unui graf in adancime se face prin utilizarea stivei (alocate implicit
prin subprograme recursive).
Pentru fiecare nod se parcurge primul dintre vecinii lui neparcursi inca
Dupa vizitarea nodului initial x 1, se exploreaza primul nod adiacent lui fie acesta
x2 , se trece apoi la primul nod adiacent cu x 2 si care nu a fost parcurs inca ,
s.a.m.d.
Fiecare nod se parcurge cel mult odata (daca graful nu este conex nu se vor
putea parcurge toate nodurile)
De exemplul pentru garful din figura de mai jos, se va proceda in felul urmator:
se porneste din nodul 1, (se poate incepe de la oricare alt nod)
6
5
2
3
6
5
2
3
se obtine
1,2
dupa care din 2 se exploreaza un nod adiacent cu acesta si care nu a fost vizitat :
3.( Nodul 1 nu se mai viziteaza odata)
1
6
5
2
3
se obtine 1,2,3
6
5
2
3
se obtine 1, 2, 3, 4
Pentru nodul 4 ar trebui sa se parcurga primul sau vecin neparcurs (nodul 1 dar
acesta a fost deja parcurs. Si nodul 3 a fost parcurs. Din 4 nu mai avem ce vizita
si se trece la nivelul anterior din stiva, la nodul 3 :
1, 2, 3, 4 Se parcurge vecinul sau nevizitat, nodul 5 .
6
5
2
3
SE obtine : 1, 2, 3, 4 , 5.
Nodul 3 nu mai are vecini nevizitati si se trece pe nivelul anterior din stiva, nodul
2 : 1, 2, 3, 4 , 5. Nici acesta nu mai are vecini nevizitati si se trece pe nivelul
anterior la nodul 1 : 1, 2, 3, 4 , 5. Cum nici acesta nu mai are vecini nevizitati se
incheie algoritmul.
Nodul 6 ramane nevizitat.
Algoritmul
-Graful se va memora utilizand matricea de adiacenta a[10][10]
-pentru a nu parcurge un nod de doua ori se va folosi un vector boolean vizcare
va retine :
- viz[k]=0 daca nodul k nu a fost vizitat inca
- viz[k]=1 daca nodul k a fost vizitat
-ca si la parcurgerea in latime vecinii unui nod se cauta pe linia acestui nod :
daca a[nod][k]=1 inseamna ca nodurile nod si k sunt adiacente. Pentru ca nodul
k sa fie fie parcurs trebuie ca nodul sa nu fi fost vizitat : viz[k]=0
#include<fstream.h>
#include<conio.h>
int a[20][20],n,m,viz[100],gasit;
void main()
{int x,y,j;
fstream f;
clrscr();
f.open("graf.txt",ios::in);
if(f)
cout<<"ok!"<<endl;
else
cout<<"eroare la deschidere de fisier!";
f>>n>>m;
for(int i=1;i<=m;i++)
{f>>x>>y;
a[x][y]=a[y][x]=1;}
cout<<endl<<"matricea de adiacente"<<endl;
for(i=1;i<=n;i++)
{for(j=1;j<=n;j++)
cout<<a[i][j]<<" ";
cout<<endl;}
cout<<endl<<"parcurgere in adancime incepand de la varful 1"<<endl;
dfmr(1)
getch();}
Matricea lanturilor
Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m elemente (m
muchii). Lui G i se asociaza matricea D numita matricea lanturilor, cu n linii si n coloane.
Astfel: D = (di j)
nxn
cu:
d[i,j]=
0, altfel
Matricea lanturilor ca si matricea de adiacenta, in cazul grafurilor neorientate , este o matrice
simetrica (daca exista lant de la i la j exista si lant de la j la i).
Grafului de mai jos i se asociaza urmatoarea matrice a lanturilor:
1
7
5
2
3
0111100
1011100
1101100
1110100
1111000
0000001
0000010
Linia k (sau coloana k) a matricei lanturilor ne arata nodurile j pentru care exista lant de k la j
( si de la j la k). Spre exemplu, pentru nodul 3 .
1
7
5
2
3
0111100
1011100
1101100
1110100
1111000
0000001
0000010
dfmr(k);
}
void main()
{int x,y,j;
fstream f;
clrscr();
f.open("muchii.txt",ios::in); //citire matrice de adiacenta din fisier
if(f)
cout<<"ok!"<<endl;
else
cout<<"eroare la deschidere de fisier!";
f>>n>>m;
for(int i=1;i<=m;i++)
{f>>x>>y;
a[x][y]=a[y][x]=1;}
cout<<endl<<"matricea de adiacente"<<endl;
for(i=1;i<=n;i++)
{for(j=1;j<=n;j++)
cout<<a[i][j]<<" ";
cout<<endl;}
int nod;
for(nod=1;nod<=n;nod++) //parcurgere graf incepand de la fiecare nod
{for(j=1;j<=n;j++)
viz[j]=0; //pentru fiecare nod se face vectorul viz in prealabil 0
dfmr(nod);
viz[nod]=0;
for(j=1;j<=n;j++) //incarcare vector viz in matrice drum
drum[nod][j]=viz[j];
}
cout<<endl<<"matricea drumurilor "<<endl;
for(i=1;i<=n;i++)
{for(j=1;j<=n;j++)
cout<<drum[i][j]<<" ";
cout<<endl;}
getch();}
Componente conexe
Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m
elemente (m muchii).
Definitie: G1=(V1, E1)este o componenta conexa daca:
-
7
5
2
3
Definitie: Un graf G=(V, E)este conex daca pentru orice pereche x,y de noduri
din V exista un lant de la x la y (implicit si de la y la x).
Observatii:
Un graf este conex daca admite o singura componenta conexa.
Graful anterior nu este conex pentru ca admite doua componente
conexe
Graful urmator este conex:
-
7
5
2
3
Probleme:
1. Fiind dat un graf memorat prin intermediul matricei de adiacenta sa se
determine daca graful este conex.
2. Fiind dat un graf memorat prin intermediul matricei de adiacenta si un
nod k sa se determine componenta conexa careia ii apartine nodul k
3. Sa se afiseze toate componentele conexe ale unui graf neorientat
Indicatii :
-In vectorul viz se incarca numarul componentei conexe astfel pentru graful
urmator, vectorul viz va retine:
viz=[1,1,1,1,2,1,2].
7
6
2
3
#include<fstream.h>
#include<conio.h>
int a[20][20],n,m,viz[100],gasit;
int nrc; //pastreaza numarul componentei conexe
void main()
{int x,y,j;
fstream f;
clrscr();
f.open("muchii.txt",ios::in); //memorare matrice de adiacenta
if(f)
cout<<"ok!"<<endl;
else
cout<<"eroare la deschidere de fisier!";
f>>n>>m;
for(int i=1;i<=m;i++)
{f>>x>>y;
a[x][y]=a[y][x]=1;}
cout<<endl<<"matricea de adiacente"<<endl;
for(i=1;i<=n;i++)
{for(j=1;j<=n;j++)
cout<<a[i][j]<<" ";
cout<<endl;}
for(int nod=1;nod<=n;nod++)
if(viz[nod]==0) //se incearca parcurgerea numai daca un nod nu a fost deja
parcurs
{nrc++;
dfmr(nod);}
Matricea ponderilor
Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m elemente (m
muchii). Atasam fiecarei muchii o pondere (sau cost) ci,j. Spre exemplu, ne putem imagina
nodurile unui graf ca fiind niste repere pe o harta si costurile distante intre repere adiacente.
Matricea ponderilor asociata grafului este o matrice simetrica cu nxn elemente
Astfel: p= (pi j)
nxn
cu:
0, daca i=j
p[i,j]= ci,j, daca [i,j]E
altfel
1
2
2
6
8
3
025
206
6018
510
80
Problema:
Sa se genereze matricea ponderilor pentru un graf citit (n noduri, m muchii si m triplete).
Indicatii:
- pentru infinit se va folosi o valoare mai mare decat oricare dintre valorile posibile
pentru costuri. De obicei intervalul de valori pentru costuri este precizat.
- Initial se incarca in matrice infinit apoi se suprascrie matricea pentru fiecare triplet
citit:
Ex: cin>>x>>y>>cost
p[x][y]=p[y][x]=cost
Astfel:
1
2
10
2
3
8
3
Algoritmul:
-se genereaza matricea ponderilor:
0 2 pinf 10 pinf
1
2
10
2
3
8
3
2 0 3 pinf pinf
pinf 3 0 1 8
10 pinf 1 0 pinf
pinf pinf 8 pinf 0
Unde pinf reprezinta plus infinit
-se incearca pentru oricare pereche de noduri i,j sa se obtina drumuri mai
scurte prin noduri intermediare k (k1n).
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(a[i][j]>a[i][k]+a[k][j])
a[i][j]=a[i][k]+a[k][j];
Se obtine:
0 2 5 6 13
1
2
10
2
3
8
3
2 0 3 4 11
53018
64109
13 11 8 9 0
Solutie:
Iata o solutie:
#include<fstream.h>
#include<conio.h>
void citire_cost()
{fstream f;
int i,j,x,y,c;
f.open("roy.in",ios::in);
if(f)
cout<<"deschiderea a reusit";
else
cout<<"eroare la deschidere!";
cout<<endl;
f>>n>>m;
//initializare matrice
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(i==j)
a[i][j]=0;
else
a[i][j]=pinf;
for(i=1;i<=m;i++)
{f>>x>>y>>c;
a[x][y]=a[y][x]=c;}
}
void afisare_mat()
{for(int i=1;i<=n;i++)
{for(int j=1;j<=n;j++)
if(a[i][j]==pinf)
cout<<"pinf ";
else
cout<<a[i][j]<<" ";
cout<<endl;}
}
void genarare_matrice_drumuri_optime()
{for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(a[i][j]>a[i][k]+a[k][j])
a[i][j]=a[i][k]+a[k][j];
}
while(k<=n&&!g)
{if(i!=k&&j!=k)
if(a[i][j]==a[i][k]+a[k][j])
{descompun_drum(i,k);
descompun_drum(k,j);
g=1;} //g marcheaza daca se poate realiza descompunerea
k++;
}
if(!g)
cout<<j<<" "; //cand drumul nu mai poate fi descompus afisez extremitatea
finala
void main()
{clrscr();int x,y;
citire_cost();
cout<<endl<<"matricea ponderilor "<<endl;
afisare_mat();
genarare_matrice_drumuri_optime();
cout<<endl<<"matricea drumurilor optime "<<endl;
afisare_mat();
cout<<endl<<"Se determina drumul minim intre varfurile x si y "<<endl;
cout<<"x=";
cin>>x;
cout<<"y=";
cin>>y;
scriu_drum(x,y);
getch();
}
Graf hamiltonian
Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m
elemente (m muchii).
Lan hamiltonian = un lan elementar care conine toate nodurile unui graf
6
5
2
3
6
5
2
3
#include<fstream.h>
#include<conio.h>
#include<stdio.h>
int st[100],n,m,k,a[20][20];
int ns;
//k este este nivelul din stiva (indexul - vetorul solutie),curent
int e_valid()
{if(k>1)
if(!a[st[k-1]][st[k]])
return 0;
else
for(int i=1;i<=k-1;i++)//parcurg nivelurile anterioarenivelului curent
if(st[i]==st[k])
return 0;
if(k==n)
if(!a[st[1]][st[k]])
return 0;
return 1;
}
void afisare()
{for(int i=1;i<=n;i++)
cout<<st[i]<<" ";
cout<<st[1];
k=0; //determina iesirea la prima solutie
ns++;
}void back()
{k=1; //pe primul nivel initial
while(k>0)//cand k va descreste la 0 algoritmul se incheie
if(st[k]<n)
{st[k]++;
if(e_valid())//daca elementul incarcat este valid
if(k==n)//verific daca am ajuns la solutia completa.
afisare();
}
else
k--;
void main()
{clrscr();
fstream f;
f.open("ham.in",ios::in);
int u,v;
if(f)
cout<<"ok!";
else
cout<<"eroare";
cout<<endl;
f>>n>>m;
for(int i=1;i<=m;i++)
{f>>u>>v;
a[u][v]=a[v][u]=1;
Grafuri euleriene
Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are
m elemente (m muchii).
Lan eulerian = un lan simplu care conine toate muchiile unui graf
for(int i=1;i<=k-2;i++)
if((st[i]==x && st[i+1]==y) || (st[i]==y && st[i+1]==x))
return 0; //muchia a mai fost luata odata
//ultima muchie sa fie incidenta cu prima
if(k==m)
if(a[st[m]][st[1]]==0)
return 0;
return 1;}
void tipar()
{for(int i=1;i<=m;i++)
cout<<st[i]<<" ";
cout<<st[1];
cout<<endl;
}
void back()
{ k=1;
while(k>0)
{if(st[k]<n)
{st[k]++;
if(e_valid())
if(k==m)
tipar();
else{k++;
st[k]=0;
}
}
else
k--;}
}
void main()
{clrscr();int x,y;
fstream f;//int a[10][10];// citire matrice din fisier
f.open("matsim.txt",ios::in);
if(f)
cout<<"ok";
else
cout<<"error";
f>>n>>m;
for(int i=1;i<=m;i++)
{f>>x>>y;
a[x][y]=a[y][x]=1;
}
-se determina daca graful contine un ciclu eulerian (toate nodurile au grad
par si este conex)
void generezc1(nod*&p,nod*&u,int x)
{nod *q;
q=new nod;
q->info=x;
p=u=q;
do
{ int gas=0;
for(int i=1;i<=n&&!gas;i++)
if(a[i][u->info]==1)
{g[u->info]--;
g[i]--;
k++;
a[i][u->info]=a[u->info][i]=0;
q=new nod;
q->info=i;
u->next=q;
u=q;
gas=1;}
}
while(p->info!=u->info);
u->next=0;
}
void afisare(nod *q)
{while(q)
{cout<<q->info<<" ";
q=q->next;}
}
int cauta(nod *&q)
{
while(q)
{if(g[q->info])
return q->info;
q=q->next;
}
return 0;
}
void main()
{clrscr();
citire();
if(verific()==0)
cout<<"gf nu este eulerian!";
else
{ cout<<"este eulerian!";
nod *p=0,*u;
cout<<endl;
generezc1(p,u,1);
cout<<endl;
nod *p1=0,*u1;
while(k<m)
{nod *q=p;
int x=cauta(q);
generezc1(p1,u1,x);
u1->next=q->next;
q->next=p1->next;
}
afisare(p); }
getch();
}