Curpins
1. 2. 3.
Argument
Principalul motiv pentru care am ales aceast tem este aplicarea practic a grafurilor neorientate i neorientate n viaa de zi cu zi . Cel mai bun exemplu de aplicatie practica in viata reala a grafurilor neorientate sunt hartile rutiere. Putem afla astfel cel mai scurt drum pana intr-un anumit punct sau care puncte de pe harta sunt cel mai usor accesibil.Nodurile pot fi considerate orase, iar muchiile drumuri; grafurile orientate pot reprezente drumuri cu sens unic intre cladiri. De asemenea, ne putem reprezenta traiectoria unei calatorii cu ajutorul unui lant al unui graf neorientat. Grafurile mai pot arata legaturile dintre anuminte grupuri sau oameni; grafuri orientate pot arata transferul de informatii sau a unor bunuri.Un arbore genealogic este de asemena un graf neorientat. Cablurile de inalta tensiune care pornesc dintr-o centrala pot fi si ele reprezentate cu usurinta cu ajutorul unui graf orientat, indicand si directia de deplasare a curentului. In acest caz centrala este un nod sursa. La fel se poate reprezenta si un sistem de canalizare, de incalzire sau reteaua de apa curenta. Multitudinea cailor aeriene reprezinta grafuri. Nodurile sunt intersectiile (imaginare) si muchiile sunt rutele (imaginare). Noduri pot fi si aeroporturile. Teoria grafurilor are numeroase apeluri in chimie, contribuind in mare masura la rezolvarea problemelor de numarare a grafurilor apartinand unor clase speciale. Teoria grafurilor este folosita in domenii variate: de la chimie la economie, de la studiul retelelor electrice la critica textelor de politica, devenind o disciplina majora.
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; Adiacenta: ntr-un graf neorientat existena muchiei (v,w) presupune c w este adiacent cu v i v adiacent cu w. n exemplul din figura de mai sus vrful 1 este adiacent cu 4 dar 1 i 3 nu reprezint o pereche de vrfuri adiacente. Inciden = o muchie este incident cu un nod dac l are pe acesta ca extremitate. Muchia (v,w) este incident n nodul v respectiv w. Grad = Gradul unui nod v, dintr-un graf neorientat, este un numr natural ce reprezint numrul de noduri adiacente cu acesta (sau numarul de muchii incidente cu nodul respectiv) Nod izolat = Un nod cu gradul 0. Nod terminal= un nod cu gradul 1 Daca un graf neorientat are m muchii atunci suma gradelor tuturor nodurilor este 2m In orice graf G exista un numar par de noduri de grad impar Lan = este o secven de noduri ale unui graf neorientat G=(V,E), cu proprietatea c oricare dou noduri consecutive din secventa lant sunt adiacente: L=[w1, w2, w3,. . ,wn] cu proprietatea c (wi, wi+1)E pentru 1i<n. Lungimea unui lan = numrul de muchii din care este format. Lan simplu = lanul care conine numai muchii distincte Lan compus= lanul care nu este format numai din muchii distincte Lan elementar = lanul care conine numai noduri distincte
Ciclu = Un lan n care primul nod coincide cu ultimul. Ciclul este elementar dac este format doar din noduri distincte, excepie fcnd primul i ultimul. Lungimea unui ciclu nu poate fi 2. Graf partial = Dac dintr-un graf G=(V,E) se suprim cel puin o muchie atunci noul graf G=(V,E), E E se numete graf parial al lui G (are aceleasi noduri si o parte din muchii).
Subgraf = Dac dintr-un graf G=(V,E) se suprim cel puin un nod mpreun cu muchiile incidente lui, atunci noul graf G=(V,E), E E si VV se numete subgraf al lui G. Graf complet = graf neorientat G=(V,E) n care exist muchie ntre oricare dou noduri Numrul de muchii ale unui graf complet este: nr*(nr-1)/2.Unde nr este numarul de noduri Graf conex = graf neorientat G=(V,E) n care pentru orice pereche de noduri (v,w) exist un lan care le unete. Component conex = subgraf al grafului de referin, maximal n raport cu proprietatea de conexitate (ntre oricare dou vrfuri exist lan); Lan hamiltonian = un lan elementar care conine toate nodurile unui graf Ciclu hamiltonian = un ciclu elementar care conine toate nodurile grafului Graf hamiltonian = un graf G care conine un ciclu hamiltonian Graful anterior este graf Hamiltonian. Daca G este un graf cu n>=3 noduri astfel incat gradul fiecarui nod este mai mare sau egal decat n/2 atunci G este hamiltonian Lan eulerian = un lan simplu care conine toate muchiile unui graf Lantul: L=[1.2.3.4.5.3.6.2.5.6] este lant eulerian Ciclu eulerian = un ciclu simplu care conine toate muchiile grafului Ciclul: C=[1.2.3.4.5.3.6.2.5.6.1] este ciclu eulerian Graf eulerian = un graf care conine un ciclu eulerian. Condiie necesar i suficient: Un graf este eulerian dac i numai dac oricare vrf al su are gradul par.
clrscr(); f>>n; while(f>>i>>j) {p=new nod; //se adauga j in lista vecinilor lui i p->nd=j; p->next=L[i]; L[i]=p; q=new nod; //se adauga i in lista vecinilor lui j q->nd=i; 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.
Algoritmul
Se va folosi o coada in care se inscriu nodurile in forma in care sunt parcurse: nodul initial varf (de la care se porneste), apoi nodurile a,b,..., adiacente lui varf, apoi cele adiacente lui a, cele adiacente lui b,... ,s.a.m.d. Coada este folosita astfel: - se pune primul nod in coada; - se afla toate varfurile adiacente cu primul nod si se introduc dupa primul nod - se ia urmatorul nod si i se afla nodurile adiacente - procesul se repeta pana cand se ajunge la sfarsitul cozii -Graful se va memora utilizand matricea de adiacenta a[10][10] -pentru memorarea succesiunii nodurilor parcurse se va folosi un vector c[20] care va functiona ca o coada -pentru a nu parcurge un nod de doua ori se va folosi un vector boolean viz[20] care va retine : - viz[k]=0 daca nodul k nu a fost vizitat inca - viz[k]=1 daca nodul k a fost vizitat -doua variabile : prim si ultim vor retine doua pozitii din vectorul c si anume : - prim este indicele componentei pentru care se parcurg vecinii (indexul componentelor marcate cu rosu in sirurile parcurse anterior ). Prin urmare Varf=c[prim], este elementul pentru care se determina vecinii (nodurile adiacente) -ultim este pozitia in vector pe care se va face o noua inserare in vectorul c (evident, de fiecare data cand se realizeaza o noua inserare se mareste vectorul) -vecinii nodului varf se cauta pe linia acestui varf : daca a[varf][k]=1 inseamna ca nodurile varf si k sunt adiacente. Pentru ca nodul k sa fie adaugat in coada trebuie ca nodul sa nu fi fost vizitat : viz[k]=0 #include<fstream.h> #include<conio.h> int a[10][10],c[20],viz[10]; int n,m,prim,ultim,varf; void bf_iterativ() //parcurgerea in latime {int k; while(prim<=ultim) {varf=c[prim]; for(k=1;k<=n;k++) if(a[varf][k]==1&&viz[k]==0) //il adaug pe k in coada daca este vecin pt. varf si nu a fost vizitat {ultim++; c[ultim]=k; viz[k]=1;} prim++; } }
void main() {clrscr(); int x,y; fstream f; //memorare graf in matrice de adiacenta f.open("muchii.txt",ios::in); f>>n>>m; for(int i=1;i<=m;i++) {f>>x>>y; a[x][y]=a[y][x]=1; } cout<<"matricea de adiac "<<endl; // afisare matrice de adiacenta for( i=1;i<=n;i++) {for(int j=1;j<=n;j++) cout<<a[i][j]<<" "; cout<<endl; } int nd; prim=ultim=1; cout<<"nodul de inceput="; cin>>nd; // nodul de la care se porneste parcurgerea viz[nd]=1; c[prim]=nd; bf_iterativ(); for(i=1;i<=ultim;i++) //afisarea cozii cout<<c[i]<<" "; getch(); } Varianta recursiva de parcurgere se obtine modificand functia de parcurgere iterativa adaugand conditia necesara autoapelului: void bf_recursiv() //parcurgerea in latime {int k; if(prim<=ultim) {varf=c[prim]; for(k=1;k<=n;k++) if(a[varf][k]==1&&viz[k]==0) //il adaug pe k in coada daca este vecin pt. varf si nu a fost vizitat {ultim++; c[ultim]=k; viz[k]=1;} prim++; bf_recursiv(); } } Parcurgerea in latime a grafurilor memorate prin liste este similara cu diferenta ca vecinii unui nod adaugat in coada se cauta in lista corespunzatoare lui :
#include<conio.h> #include<fstream.h> struct nod {int nd; nod *next;}; nod *L[20]; int viz[100]; //marchez cu 1 nodurile vizitate int m,n; int prim,ultim,C[100]; void bfi_lis() {int varf,nr; nod *p; while(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 } }
void afisare(int nr_nod) {nod *p=L[nr_nod]; if(p==0) cout<<nr_nod<<" este izolat "<<endl; else {cout<<"lista vecinilor lui "<<nr_nod<<endl; nod *c=p; while(c) {cout<<c->nd<<" "; c=c->next;} cout<<endl;} } void main() {fstream f; int i,j; nod *p,*q; f.open("muchii.txt",ios::in); clrscr();
f>>n>>m;
while(f>>i>>j) {p=new nod; p->nd=j; p->next=L[i]; L[i]=p; q=new nod; q->nd=i; q->next=L[j]; L[j]=q; } f.close(); cout<<endl<<"listele de adiacente "; for(i=1;i<=n;i++) afisare(i); 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;}
Fie G=(V, E) un graf neorientat. Exista mai multe modalitati de reprezentare pentru un graf neorientat, folosind diverse tipuri de structuri de date. Reprezentarile vor fi utilizate in diversi algoritmi si in programele care implementeaza pe calculator acesti algoritmi.
Observatii:
Matricea de adiacenta asociat unui graf neorientat este o matrice simetric Suma elementelor de pe linia k reprezint gradul nodului k Suma elementelor de pe coloana k reprezint gradul nodului k
1 0 1 0 0 0 nodul 2 are gradul 2 A= 0 1 0 1 1 0 nodul 3 are gradul 3 1 0 1 0 0 0 nodul 4 are gradul 2 0 0 1 0 0 0 nodul 5 are gradul 1 0 0 0 0 0 0 nodul 6 are gradul 0 Numarul de noduri este 6 si numarul de muchii este 4 Matricea este simetrica si patratica avand 6 linii si 6 coloane Diagonala principala contine numai valori nule
Pentru a prelucra graful se citesc: 6- reprezentand n, numarul de noduri 4- reprezentand m, numarul de muchii 4 perechi x-y reprezentand extremitatile celor 4 muchii: 1-2 => a[1,2]=a[2,1]=1 1-4 => a[1,4]=a[4,1]=1 2-3 => a[2,3]=a[3,2]=1 3-4=>a[3,4]=a[4,3]=1 3-5 => a[3,5]=a[5,3]=1
#include<cstdlib> using namespace std; int a[100][100],b[100][100],n,m,k,l; void citire() {int x,y,i; cin>>n>>m; for(i=1;i<=m;i++) {cin>>x>>y; a[x][y]=1; a[y][x]=1; } cin>>k>>l; for(i=1;i<=l;i++) {cin>>x>>y; b[x][y]=1; b[y][x]=1; } } int subgraf() {for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) if(a[i][j]!=b[i][j]) return 0; return 1; } int main() {citire(); if(subgraf()) cout<<"da"; else cout<<"nu"; system("pause"); return 0; }
3. Se da un graf neorientat cu n varfuri si m muchii, citit prin vectorul muchiilor. Sa se afiseze pe linii separate componentele sale conexe. #include<iostream> #include<cstdlib> using namespace std; int a[100][100],n,m,x[100],p[100]; void citire() { int i,l,c; cin>>n>>m; for(i=1;i<=m;i++) { cin>>l>>c; a[l][c]=1;
a[c][l]=1; } } void bf(int k) { int i,s,d; x[1]=k; p[k]=1; s=d=1; while(s<=d) { for(i=1;i<=n;i++) if(a[x[s]][i]==1 && !p[i]) { d++; x[d]=i; p[i]=1; } s++; } for(i=1;i<=d;i++) cout<<x[i]<<" "; cout<<endl; } int main() { citire(); int i; for(i=1;i<=n;i++) if(!p[i]) bf(i); system("pause"); return 0; } 4. Se considera n multimi A1, A2, ... , An de forma {1,2..,an}. Sa se genereze produsul cartezian al acestor multimi.
#include<iostream> #include<cstdlib> using namespace std; int a[100][100],n,m,p[100],x[100],lmax,smax[100]; void citire() { int x,y,i; cin>>n>>m; for(i=1;i<=m;i++) { cin>>x>>y; a[x][y]=1; a[y][x]=1; } } void bf(int k) { int s,d,i;
x[1]=k; p[k]=1; s=1; d=1; while(s<=d) { for(i=1;i<=n;i++) if(a[x[s]][i]==1 && !p[i]) {d++; x[d]=i; p[i]=1; } s++; } if(d>lmax) { lmax=d; for(i=1;i<=lmax;i++) smax[i]=x[i]; } } int main() { citire(); for(int i=1;i<=n;i++) if(!p[i]) bf(i); for(int i=1;i<=lmax;i++) cout<<smax[i]<<" "; system("pause"); return 0; } 5. Se da un graf neorientat cu n varfuri si m muchii, citit prin vectorul muchiilor. Sa se afiseze cea mai mare dintre componentele sale conexe. #include<iostream> #include<cstdlib> using namespace std; int n,m,a[100][100],x[100],k,p[100]; void citire() {int x,y; cin>>n>>m; for(int j=1;j<=m;j++) {cin>>x>>y; a[x][y]=1; a[y][x]=1; } cin>>k; } int cond(int j) {if(j>1) if(a[x[j-1]][x[j]]==0) return 0; if(j==k) if(a[x[j]][x[1]]==0) return 0;
void back(int j) {for(int i=1;i<=n;i++) if(!p[i]) {x[j]=i; p[i]=1; if(cond(j)) if(j==k) afis(); else back(j+1); p[i]=0; } } int main () {citire(); back(1); system("pause"); return 0; }
Manual de utilizare
Pentru a rula o problem se va da click pe butonul Ruleaza urmnd s se introduc datele de intrare , rezultatul afindu-se la apsarea tastei Enter dup introducerea datelor de intrare.
Bibliografie
1. http://infoliceu.webnode.com/ 2. Manual de informatic clasa a XI-a , editura Didactica si Pedagogica, autor Mariana
Milosescu