Documente Academic
Documente Profesional
Documente Cultură
Curs 5drum Min Max-Copie
Curs 5drum Min Max-Copie
CUPRINS
0
0
1
0
0
0
1
1
1
1
0
1
atunci verificrile de mai sus sunt echivalente cu a verifica dac elementul de pe poziia (i,j) din A2
este egal cu 1. Valoarea 1 spune doar c exist cel puin un drum de lungime 2 de la xi la xj. Dac
dorim s vedem i cte sunt, vom folosi regulile de nmulire i adunare obinuit.
De asemenea, se poate observa c existena unui drum de lungime 3 de la xi la xj presupune
existena unui nod xk astfel nct s existe un drum de lungime 2 de la xi la xk i un arc de la xk la xj,
care este echivalent cu a verifica dac exist vreun indice k astfel nct elementul k al liniei i din
matricea A2 i elementul k al coloanei j din A sunt ambele egale cu 1 sau, mai simplu, dac
elementul (i,j) din A3 este 1.
Din cele de mai sus se observ c existena drumurilor de lungime k este dat de valorile
matricei Ak, dac s-au folosit regulile algebrei booleene i numrul lor este dat de Ak, dac s-au folosit
regulile obinuite.
Pasul 2. Vom calcula succesiv puterile lui A pn la puterea An-1
Dac ntre nodurile xi i xj exist un drum de lungime n atunci el va conine un numr de
noduri mai mare sau egal cu n+1 i, cum n graf sunt doar n vrfuri, este clar c cel puin unul, s
zicem xk, va aprea de dou ori. Vom avea n acest caz un drum de la xi pn la prima apariie a lui
xk, i un drum de la ultima apariie a lui xk la xj. Eliminnd toate nodurile dintre prima apariie a lui xk
3
i ultima apariie a sa vom obine un drum de la xi la xj, n care xk apare o singur dat. Aplicnd
acest procedeu pentru toate nodurile care apar de mai multe ori pe drum, vom obine un drum de la
xi la xj, n care fiecare nod apare o singur dat (deci un drum elementar), care are evident cel mult
n-1 arce. n concluzie, dac exist vreun drum de la xi la xj atunci exist i un drum elementar i,
deci, va exista o putere a lui A, ntre A1 i An-1, n care poziia (i,j) este diferit de 0. Pentru
deciderea existenei unui drum ntre oricare dou noduri este suficient, deci, calcularea doar a
primelor n-1 puteri ale lui A.
Se calculeaz matricea D = A + A2 + ... + An-1
Dac ne intereseaz doar existena drumurilor dintre noduri, nu i numrul lor, vom folosi
nmulirea i adunarea boolean i conform observaiei de mai sus:
1, daca exista drum de la xi la xj
0, in caz contrar
dij =
Procedeul de mai sus nu asigur dect aflarea faptului dac exist sau nu drum ntre dou
noduri, eventual ce lungime are i cte sunt de aceast lungime. Totui, n problemele practice cel
mai important este s tim care sunt efectiv aceste drumuri.
Deoarece toate drumurile pot fi descompuse n drumuri elementare i n problemele practice
n general acestea sunt cele care intereseaz, paii urmtori ai algoritmului vor fi dedicai gsirii lor.
Pentru gsirea acestora se folosete reprezentarea grafului prin matricea latin. Matricea
latin este o matrice ptratic nxn n care pe o poziie aij va fi xixj dac exist arcul (xi,xj) i 0 n
caz contrar.
Pasul 3. Construim matricea latin L asociat grafului, unde:
xixj, dac exist arcul (xi,xj)
lij =
0, n caz contrar
(1)
ij
0, n caz contrar
4
este inversabil doar dac este cuvntul vid.
Definiia 3: Se numete adunare latin o funcie definit pe mulimea cuvintelor unui alfabet cu
valori n mulimea parilor mulimi cuvintelor, notat +Lastfel:
si1si2.....sin +L sj1sj2.....sjm = (si1si2.....sin, sj1sj2.....sjm)
(suma a dou cuvinte este mulimea format din cele dou cuvinte)
Pasul 4. Se calculeaz succesiv matricile:
L2 = L xLL(1), L3 = L2xLL(1),
...,Lk+1 = LkxLL(1)
folosind operaiile de nmulire i adunare latin, alfabetul fiind mulimea nodurilor grafului, unde
operaia de nmulire este uor modificat, produsul dintre dou elemente ale matricilor fiind 0, dac
unul dintre ele este 0 sau au un nod comun i este produsul latin al lor, n caz contrar.
Din felul cum a fost construit, matricea Lk va conine toate drumurile elementare de lungime
k. Cum un drum elementar poate avea cel mult n noduri (cte are graful cu totul) rezult c:
primele n-1 puteri ale lui L conin toate drumurile elementare din graf;
puterile lui L mai mari sau egale cu n au toate elementele egale cu 0;
matricea Ln-1 conine toate drumurile hamiltoniene din graf (dac exist).
Observaie: Deoarece obinerea matricii D prin metoda de mai sus presupune un volum foarte mare
de calcule (de exemplu, dac graful are 100 de noduri, ridicarea unei matrici de 100100 la puterea
100) pentru obinerea acesteia se poate aplica i urmtorul algoritm:
Pas 1. Se construiete matricea de adiacen A;
Pas 2. Pentru fiecare linie i se adun boolean la aceasta toate liniile j pentru care aij=1.
Pas 3. Se reia pasul 2 pn cnd, dup o aplicare a acestuia, matricea rmne aceeai (nu
mai apare nici un 1)
Ultima matrice obinut este matricea drumurilor D numit i matricea conexiunilor totale.
Aceast metod, dei mai simpl nu spune ns i care sunt aceste drumuri, pentru gsirea lor
aplicndu-se, de exemplu, nmulirea latin.
5
Una din problemele care poate aprea n aceste situaii este gsirea, pentru o anumit
pereche de noduri (sau mai multe perechi), a drumului optim ntre acestea.
Pentru formalizarea problemei vom introduce noiunea de valoare a unui drum, care este
egal cu suma valorilor arcelor care l compun. Vom nota n continuare valoarea unui arc (xi,xj) cu
c(xi,xj) sau cu cij. n aceste condiii putem enuna problema drumului optim astfel:
"Dat un graf G=(X,U) i o funcie care asociaz fiecrui arc o valoare real, s se
gseasc, pentru o pereche dat de noduri, drumul (drumurile) de valoare optim (minim
sau/i maxim) ntre cele dou noduri i valoarea acestuia (acestora)"
Deoarece este vorba de gsirea minimului unei mulimi de numere reale, prima ntrebare care
se pune este dac aceasta admite minim. Dac mulimea nodurilor grafului este infinit atunci pot
exista o infinitate de drumuri elementare distincte ntre cele dou noduri i mulimea valorilor acestora
poate avea orice form (nchis sau nu, mrginit sau nu) devenind foarte greu de caracterizat
cazurile cnd minimul dorit exist. Deoarece totui majoritatea covritoare a problemelor economice
se modeleaz prin grafuri cu numr finit de noduri, ne vom limita n continuare doar la acestea.
Un numr finit de noduri n atrage dup sine existena unui numr finit de arce (cel mult n2) i a
n -1
k!
unui numr finit de drumuri elementare ( cel mult nn! k 1 ). Deoarece oricrui drum d i
corespunde un drum elementar de (obinut prin eliminarea tuturor subcircuitelor lui d) putem calcula
valoarea oricrui drum ca sum ntre valoarea drumului elementar corespunztor i valorile unor
subcircuite ale sale, fiecare nmulit cu numrul de parcurgeri ale circuitului respectiv.
n concluzie, dac exist un circuit de valoare negativ nseamn c exist drumuri de valoare
orict de mic (cele care conin acest circuit), obinut prin parcurgerea acestuia de oricte ori dorim)
i, deci, mulimea valorilor drumurilor este nemrginit inferior, neexistnd drum de valoare minim.
Dac exist un circuit de valoare pozitiv atunci exist drumuri de valoare orict de mare i mulimea
valorilor drumurilor este nemrginit superior, neexistnd drum de valoare maxim.
Dac nu exist circuite de valoare negativ atunci valoarea oricrui drum este mai mare sau
egal cu a drumului elementar corespunztor, deci drumul de valoare minim (dac exist) va fi un
drum elementar. Cum mulimea drumurilor elementare este finit (i deci i mulimea valorilor lor) va
avea minorant i am lmurit problema compatibilitii problemei. Analog, dac nu exist circuite de
valoare pozitiv atunci valoarea oricrui drum este mai mic sau egal cu a drumului elementar
corespunztor, deci drumul de valoare maxim (dac exist) va fi un drum elementar. Cum mulimea
drumurilor elementare este finit (i deci i mulimea valorilor lor), va avea majorant.
Obs. 1. Dac n graf nu exist dect arce de valoare pozitiv atunci exist drum de valoare
minim.
Obs. 1. Dac n graf nu exist dect arce de valoare negativ atunci exist drum de valoare
maxim.
Obs. 1. Dac n graf nu exist circuite atunci exist i drum de valoare minim i drum de
valoare maxim.
Deoarece din cele de mai sus se sesizeaz importana existenei circuitelor ntr-un graf vom
da n continuare un algoritm de depistare a existenei circuitelor ntr-un graf:
Pasul 1. Se construiete mulimea A format din nodurile pentru care toate arcele incidente sunt
incidente spre interior ( noduri n care toate arcele "intr" sau, altfel spus, noduri din care nu
"pleac" nici un arc).
Pasul 2. Se gsesc toate nodurile care nu sunt din A pentru care toate arcele incidente au cealalt
extremitate n A (noduri din care se poate "ajunge" doar in A). Dac nu exist nici un astfel
de arc se trece la pasul 4.
Pasul 3. Se adaug arcele gsite la pasul 2 la mulimea A apoi se reia algoritmul de la pasul 2, pentru
noua mulime A.
6
Pasul 4. Dac A conine mulimea tuturor nodurilor atunci graful nu conine circuite. Dac au rmas
noduri n afara lui A atunci graful conine circuite.
n general, algoritmii existeni pentru determinarea drumurilor optime n grafuri se mpart n
dou mari categorii: algoritmi pentru drumuri minime n grafuri orientate, respectiv algoritmi pentru
drumuri maxime. Deasemeni, exist dou mari categorii de probleme i anume:
- determinarea drumurilor optime de surs unic (surs unic, destinaii multiple)
- determinarea drumurilor optime ntre toate perechile de vrfuri (surse multiple, destinaii
multiple)
Algoritmii prezentai n continuare sunt:
3
7
1
1
2
1
5
3
4
Se pornete din x0. Evident cel mai scurt drum de la x0 la unul din celelalte vrfuri ale grafului
este dat de arcul (x0,xj) de lungime minim. Urmtorul drum n ordinea lungimilor va fi dat fie de un
alt arc cu extremitatea iniial x0, fie de un drum (x0,xj,xp). Alegem n continuare drumuri n ordinea
cresctoare a lungimilor, pn cnd am determinat drumuri minime de la x0 ctre toate vrfurile
pentru care exist drum pornind din x0. Pentru aceasta se consider S mulimea vrfurilor xjX pentru
care am gsit drum minim de la x0 la xj. Iniial S={x0}. La fiecare pas, adgm n S acel nod xkX-S
cu proprietatea c drumul minim de la x0 la xk are cel mai mic cost dintre toate drumurile de la x0 la xp,
cu xpX-S. Pentru exemplul considerat S va avea pe rnd urmtorul coninut:
S={1}
S={1,2}
S={1,2,5}
S={1,2,5,3}
S={1,2,5,3,4}
Se observ c drumul de la x0 la xk (nodul ce urmeaz s-l adugm n S la un moment dat) trece
numai prin vrfuri din S (cu excepia lui xk). Pentru a alege nodul xkX-S ce urmeaz a fi adgat n S
vom folosi un vector D=(d1,d2,...,dn) astfel nct:
lungimea drumului minim de la x0 la xi, dac xiS
di=
Iniial di=C(x0,i), ()i=1..n, adic este linia x0 din matricea costurilor. La un moment dat,
adgm n S nodul xk cu proprietatea c dk=min{dj | xjX-S}. Dup adgarea lui xk n S trebuie
actualizate valorile lui d pentru elementele care nu sunt n S, deoarece este posibil ca drumul minim
de la x0 la unul dintre aceste noduri (folosind noduri din S) s foloseasc nodul xk pe care tocmai l-am
adgat. Drumul minim de la x0 la xj ce folosete noduri din S (inclusiv xk) va fi de forma (x0,
,xk,xj). Deci pentru xjX-S, dj se modific dup adugarea lui xk la S numai dac dk+c(k,j)<dj,
caz n care dj dk+c(k,j). n final, vectorul d va conine costurile (lungimile) drumurilor minime de
la x0 la celelalte noduri; dac pentru un nod xj nu exist drum de la x0 la xj, atunci dj=.
8
Pentru a reine i drumurile minime (nu numai lungimile lor) vom considera un vector numit T
(tat) care reine indicele precedentului fiecrui nod n drumul minim de la x0 la acel nod.
Iniial:
0 dac i=x0 sau C(x0,i)=
Ti=
x0 dac C(x0,i) i ix0
La fiecare actualizare de forma djDk+c(k,j) vom avea i o actualizare a vectorului T de forma
Tjk. Algoritmul se ncheie cnd S conine toate nodurile xj pentru care exist drum de la x0 la xj,
deci fie cnd S=X (dac exist drumuri de la x0 la toate celelalte noduri), fie cnd mulimea X-S
cuprinde numai noduri pentru care nu exist drumuri pornind din x0 la ele (caz n care min{dj|
xjX-S}=). Pentru reprezentarea mulimii S se poate folosi vectorul caracteristic S cu n
componente definit astfel:
0 dac xiS
Si=
1 dac xiS
9
Relaxeaza(u,v,c)
daca D[v]D[u]+c(u,v) atunci
D[v]D[u]+c(u,v)
T[v]u
sfarsit daca
Algoritmul Dijkstra opereaz prin meninerea unei mulimi S de noduri pentru care costurile finale
corespunztoare drumurilor minime de la sursa s au fost deja determinate, respectiv pentru toate
nodurile vS avem D[v] egal cu distana mnim. Algoritmul itereaz selectarea unui nod uX-S
pentru care estimarea drumului minim este minim, introduce u n S i relaxeaz arcele divergente
din u. n algoritmul de mai sus Q este o coad coninnd toate nodurile din X-S indexate prin valorile
D corespunztoare.
Exemplu: Considerm graful anterior i punctul x0=1. Cei trei vectori au valorile iniiale:
1)
2)
3)
4)
D=(0, 1, , , 3, )
T=(0, 1, 0, 0, 1, 0)
S=(1, 0, 0, 0, 0, 0)
determinm min(D[i]| S[i]=0)=> min=1, k=2 => S[2]1
Actualizm distanele la nodurile neselectate nc.
S[3]=0; D[3]=>D[2]+C[2,3]=1+7=8 => D[3]8, T[3]2
S[4]=0; D[4]=, D[2]+C[2,4]=1+ , nu se poate actualiza
S[5]=0; D[5]=3>D[2]+C[2,5]=1+1=2 => D[5]2, T[5]2
S[6]= ; D[6]= , D[2]+C[2,6]=1+ , nu se poate actualiza
Dup primul pas configuraia celor trei vectori este:
D=(0, 1, 8, , 2, )
T=(0, 1, 2, 0, 2, 0)
S=(1, 1, 0, 0, 0, 0)
determinm min(D[i]| S[i]=0) => min=2, k=5 => S[5]1
Actualizm distanele la nodurile neselectate nc.
S[3]=0; D[3]=8>D[5]+C[5,3]=2+2=4 => D[3]4, T[3]5
S[4]=0; D[4]=, D[5]+C[5,4]=2+, nu se poate actualiza
S[6]=0; D[6]=, D[5]+C[5,6]=2+, nu se poate actualiza
Dup al doilea pas configuraia celor trei vectori este:
D=(0, 1, 4, , 2, )
T=(0, 1, 5, 0, 2, 0)
S=(1, 1, 0, 0, 1, 0)
determinm min(D[i]| S[i]=0)=> min=4, k=3 => S[3]1
Actualizm distanele la nodurile neselectate nc.
S[4]=0; D[4]= > D[3]+C[3,4]=4+1=5 => D[4]5, T[4]3
S[6]=0; D[6]= , D[3]+C[3,6]=4+, nu se poate actualiza
Dup al treilea pas configuraia celor trei vectori este:
D=(0, 1, 4, 5, 2, )
T=(0, 1, 5, 3, 2, 0)
S=(1, 1, 1, 0, 1, 0)
determinm min(D[i]| S[i]=0)=> min=5, k=4 => S[4]1
Actualizm distanele la nodurile neselectate nc.
S[6]=0; D[6]=, D[4]+C[4,6]=5+, nu se poate actualiza
Dup al patrulea pas configuraia celor trei vectori este:
D=(0, 1, 4, 5, 2, )
T=(0, 1, 5, 3, 2, 0)
10
S=(1, 1, 1, 1, 1, 0)
11
{
}
D[j]=D[k]+c[k][j]; T[j]=k;
}
}while(ok);
}
void drum(int i)
{
if(i)
{
drum(T[i]); cout<<i<< ; }
}
void afisare_drumuri()
{
int i;
for(i=1;i<=n;i++)
if(i!=xp)
if(D[i]==INF)
cout<<"\nNu exista drum de la :<<xp<<la <<i<endl;
else
{
cout<<"\nDrumul minim de la <<xp<<la <<i<<:
";
drum(i); cout<<"\n";
cout<<"\tLungimea drumului este "<<D[i]<<endl;
}
}
void main()
{
citire(); determinare_drumuri();
afisare_drumuri();}
12
2
4
2
2
2
1
2
1
3
2
6
1
2
6
1
2
3
Pasul 3: se selecteaz nodul 5, d[5]=3, se adaug la graful parial arcele (2,5) i (3,5)
13
2
1
6
1
2
1
3
Pasul 4: se selecteaz nodul 4, d[4]=4, se adaug la graful parial arcele (2,4) i (5,4)
4
2
2
1
1
6
2
1
3
4
2
2
2
1
1
6
2
1
3
14
1 2 4 6
1 2 5 4 6
1 3 5 4 6
Programul urmtor implementeaz algoritmul lui Dantzig. Complexitatea este O(n2).
#include<iostream.h>
#include<fstream.h>
#define N 50
#define INF 1<<15
int a[N][N],b[N][N],d[N],st[N],sos,p;
int sel[N],n,min,k;
void citire()
{
ifstream f(graf.in);
int i,j,x,y,z;
f>>n; //numarul de noduri
//initializam matricea costurilor si matricea grafului partial
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) { a[i][j]=INF; b[i][j]=INF; }
for(i=1;i<=n;i++) { a[i][i]=0; b[i][i]=0; }
while(!feof(f))
{
f>>x>>y>z;//arcul si costul sau
a[x][y]=z;
}
f.close();
}
void minim()
{
int i,j;
min=INF;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(sel[i]&&!sel[j])
if(min>d[i]+a[i][j])
{
min=d[i]+a[i][j]; k=j;
}
}
void dantzig()
{
int i,j;
for(i=1;i<=n-1;i++)
{
minim();
if(min!=INF)
{
sel[k]=1; d[k]=min;
for(j=1;j<=n;j++)
if(sel[j]&&(d[j]+a[j][k]==min)) b[j][k]=a[j][k];
}
}
}
void drum(int k)
{
int i,j;
for(i=1;i<=n;i++)
if(b[st[k-1]][i]<INF && !sel[i])
{
15
if(i!=sos)
{
sel[i]=1; st[k]=i; drum(k+1); sel[i]=0;
}
else
{
for(j=1;j<k;j++) cout<<st[j];
cout<<sos<<" de cost <<d[sos]<<\n";
}
}
void main()
{
int i;
citire();
cout<<"plecare: "; cin>>p;
sel[p]=1; d[p]=0;
dantzig();
for(sos=1;sos<=n;sos++)
if(p!=sos)
{
for(i=1;i<=n;i++) sel[i]=0;
sel[p]=1; st[1]=p; drum(2);
}}
Algoritmul Bellman-Ford
Enun: Se consider un graf orientat G=(X,U) i o funcie de cost c:U R. Mulimea X conine n
vrfuri. Se desemneaz un nod p de plecare. Pentru orice nod j X se cere determinarea drumului
de cost minim de la p la j. Se vor detecta situaiile n care exist circuite de cost negativ care includ
nodul p.
Algoritmul Bellman-Ford determin drumurile de cost minim dintre un nod desemnat ca surs
(plecare) i restul nodurilor accesibile lui chiar dac exist costuri negative pe arce. Aceste rezultate
sunt furnizate numai n situaia n care nodul de plecare nu face parte dintr-un circuit de cost negativ.
Strategia algoritmului este aceea de minimizare succesiv a costului drumului minim de la p la
orice nod j din graf (d[j]) pn la obinerea costului minim.
Aceast operaie se face prin verificarea posibilitii ca fiecare arc (i,j)U s participe la
minimizarea distanei de la p la j. Operaia se face cu o trecere complet prin toate arcele grafului.
d[j]
c[i,j]
d[i]
i
16
Condiia ca distana de la p la j s poat fi minimizat prin arcul (i,j) este ca d[j]d[i]+c[i,j]
Notm cu n numrul de noduri din graf. Algoritmul efectueaz n-1 treceri complete prin
mulimea arcelor grafului (orice drum elementar poate fi format din maxim n-1 arce). n final, existena
unui circuit negativ face ca la o nou trecere prin mulimea arcelor s fie n continuare posibil
minimizarea costului unui drum. n acest caz algoritmul evideniaz prezena circuitului de cost
negativ ce cuprinde nodul surs.
Algoritmul pseudocod poate fi descris astfel:
algoritm Bellman_Ford(G,c,s)
Initializeaza_Sursa_Unica(G,s)
pentru i=1,n-1 executa
Relaxeaza(u,v,c)
sfarsit pentru
pentru fiecare arc (u,v)U(G) executa
daca D[v]D[u]+c(u,v) atunci
returneaza FALS
sfarsit daca
sfarsit pentru
returneaza ADEVARAT
Funciile Initializeaza_Sursa_Unica() i Relaxeaza() sunt cele prezentate la algoritmul
lui Dijkstra. Ca i algoritmul lui Dijkstra, algoritmul Bellman_Ford utilizeaz tehnica de
relaxare, procednd la descreterea estimrii D[v] a drumului minim de la sursa s la fiecare nod vX
pn cnd este obinut costul adevrat corespunztor unui drum minim. Algoritmul returneaz
ADEVARAT dac i numai dac graful nu conine circuite de cost negativ accesibile din sursa s.
n situaia n care graful este memorat n liste de adiacen complexitatea algoritmului este
O(n*m).
n programul urmtor, funcia minimizeaza() efectueaz o trecere complet prin toate arcele
grafului n vederea descreterii costurilor drumurilor cu plecare din nodul surs desemnat prin start.
#include<iostream.h>
#include<fstream.h>
#include<stdlib.h>
#define N 50
#define INF 32000
typedef struct nod{
int inf; //vecinul
int c;
//costul muchiei
nod *leg; //adresa urmatorului vecin
}LST;
LST *lc[N]; //listele de adiacenta (vecini)
int t[N],d[N],n,start;
void citire()
{
ifstream f(graf.in);
LST *x;
int i,j,co;
f>>n;
for(i=1;i<=n;i) //aloca adrese pentru listele de vecini
{
lc[i]=new LST; lc[i]->leg=0;
}
17
while(!feof(f))
{
f>>i>>j>>co;
x=new LST; x->inf=j; x->c=co;
x->leg=lc[i]->leg; lc[i]->leg=x;
//adaug pe j in lista vecinilor lui i
}
f.close();
}
int minimizeaza()
{
int ok=0,j;
LST *p;
for(j=1;j<=n;j)
{
p=lc[j]->leg;
while(p)
{
if(d[p->inf]>d[j]>c)
{
ok=1; d[p->inf]=d[j]>c; t[p->inf]=j;
}
p=p->leg;
}
}
return ok;
}
void bellman_ford()
{
int ok,i;
for(i=1;i<=n;i)
{
t[i]=0; d[i]=INF;
//initializari
}
d[start]=0;
for(i=1;i<=n-1;i) ok=minimizeaza();
ok=minimizeaza();
//a n-a trecere va depista circuite
negativ
if(ok)
{
cout<<"\nCircuit de cost negativ\n";
exit(0);
}
}
void drum(int x)
{
if(x)
{
drum(t[x]); cout<<x;
}
}
void main()
{
int i;
citire();
de
cost
18
19
{
}
void Init()
{
int i;
for(i=1;i<=n;i++)
{
viz[i]=0; m[i]=0;
}
k=n;
}
void DF(int i)
{
AD *x=L[i]->leg; //lista vecinilor lui i
viz[i]=1;
while(x)
{
if(!viz[x->inf]) DF(x->inf);
x=x->leg;
}
//i a fost complet explorat, il adaug la inceputul listei m
m[k]=i; k--; //m contine nodurile in ordine topologica
}
void Parcurge()
{
int i;
Init();
for(i=1;i<=n;i++)
if(!viz[i]) DF(i);
}
void Initializare(int s) //nodul sursa
{
int i;
for(i=1;i<=n;i++)
//pentru fiecare nod din graf
{
d[i]=INF;
20
//nu s-a determinat nici-un drum de la s la v, cost infinit
T[i]=0;
//nodul tata pe drumul minim de la s la v
}
d[s]=0; //pentru nodul sursa
Algoritmul Floyd-Warshall
21
Enunt: Fiind dat un graf orientat G=(X,U) cu X={x1,x2,.,xn} i o funcie de cost l:U R+
s se determine pentru fiecare pereche de noduri xi,xj (i j) lungimea minim a drumurilor de la
xi la xj precum i aceste drumuri (n caz c exist drumuri de la xi la xj).
Algoritmul Floyd-Warhall determin lungimile minime ale drumurilor ntre oricare dou
noduri ale grafului ntr-o matrice C=(cij)nxn unde :
cij=
i ij
dij=
dac cij= sau i=j
22
sf_pentru
sf_pentru
sf_pentru
Complexitatea algoritmului este de ordinul O(n3). Programul urmtor implementeaz algoritmul n
varianta de mai sus.
#include<iostream.h>
#include<fstream.h>
#define N 50
#define INF 1<<15
int C[N][N]; //initial matricea costurilor,apoi a costurilor drumurilor
int D[N][N]; //pe linia d[i,j]=nodul care poate precede nodul j
// pe drumul minim de la nodul i la nodul j
int n,m;
void INITC()
//initializeaza matricea costurilor C
{
int i,j,x1,x2,cost;
ifstream f(graf.in);
f>>n>>m;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++) C[i][j]=INF;
C[i][i]=0;
}
for(i=1;i<=m;i++)
{
f>>x1>>x2>>cost;
C[x1][x2]=cost;
}
f.close();
}
void Drum(int i,int j)
//afiseaza un drum de la i la j pornind de la nodul j
{
if(j)
{
Drum(i,D[i][j]);
cout<<j<< ;
}
}
void AFISARE()
//afisarea rezultatelor
{
int i,j;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
if(C[i][j]==INF)
cout<<"\nnu exista drum intre <<i<< si <<j<<endl;
else
if(i!=j)
{
cout<<"\ncost drum de la <<i<< la <<j<<este <<C[i][j];
23
}
void main()
{
int i,j,k;
INITC();
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(C[i][k]<INF&&C[k][j]<INF)
if(C[i][j]>C[i][k]+C[k][j])
{
C[i][j]=C[i][k]+C[k][j];
D[i][j]=k;
}
AFISARE();
}
dac
(xi,xj)U
dac
i=j
dac
(xi,xj)U
dij=
24
daca cij<cik+ckj
atunci
cijcik+ckj
dijdkj
altfel
daca cij=cik+ckj
atunci dijdijdkj
sf_daca
sf_daca
sf_pentru
sf_pentru
sf_pentru
n matricea C vom avea n final lungimile drumurilor maxime ntre oricare dou noduri, iar n dij vom
avea mulimea nodurilor ce pot precede pe xj ntr-un drum maxim de la xi la xj. Programul pentru
determinarea tuturor drumurilor maxime ntre oricare dou vrfuri ale unui graf G=(X,U) este analog
celui de la problema anterioar; se folosete acelai algoritm ,adaptat noilor cerine.
Algoritmul Bellman-Kalaba
Enun: Se consider un graf orientat G=(X,U) conex, fr circuite i o funcie de cost a:U R.
Desemnndu-se un nod x, se cere determinarea drumurilor de cost maxim de la toate celelalte noduri
la el, drumuri care includ un numr impus de arce (pai).
Pe baza matricii costurilor grafului considerat, algoritmul construiete o nou matrice c n care
pe coloana j (jn) se vor afla costurile maxime ale drumurilor ce ajung n x folosind j arce. Astfel
c[i,j] reprezint costul maxim al unui drum de la i la x i trece prin j arce.
Iniial, matricea c va conine pe prima coloan costul arcelor spre nodul x, deci drumuri de
lungime 1. A doua iteraie va determina costurile maxime ale drumurilor ce ajung n x i conin dou
arce. Ele se vor memora n a doua coloan i se vor determina pe baza valorilor din coloana
anterioar.
Astfel, pentru determinarea valorii c[i,2] vom aduga pe rnd arcele ce pleac din i ctre
drumurile de lungime 1 anterior determinate. Se va identifica valoarea maxim dintre acestea:
c[i,2]=max(c[j,1]+a[i,,j]jX).
25
a[i,j]
c[i,k-1]
x
4
2
2
2
1
1
6
1
5
1
Notm cu c(i) coloana i din matricea c a drumurilor maxime ce ajung n x=6 folosind i arce.
Algoritmul se ncheie la iteraia 5 deoarece c(4)=c(5).
c(1)
-1
-1
-1
c(2)
-1
8
8
c(3)
10
12
8
c(4)
14
12
8
c(5)
14
12
8
26
2
7
0
2
7
0
2
7
0
2
7
0
2
7
0
#include<iostream.h>
#include<fstream.h>
#define N 50
int a[N][N],c[N][N];
int pasi,p1,n,x;
unsigned char t[N][N];
void citire()
{
ifstream f(graf.in);
int i,j,x,y,z;
cin>>n; //numarul de noduri
//initializam matricea costurilor si matricea grafului partial
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) a[i][j]=-1; //pentru arcele lipsa
for(i=1;i<=n;i++) a[i][i]=0;
while(!feof(f))
{
f>>x>>y>z;//arcul si costul sau
a[x][y]=z;
}
f.close();
}
void initializari()
{
int i,j;
for(i=0;i<=n;i++)
for(j=0;j<=n;j++) { c[i][j]=-1; t[i][j]=0; }
for(j=1;j<=n;j++)
{
c[j][1]=a[j][x];
if(c[j][1]!=-1) t[j][1]=j;
}
}
void kalaba()
{
int i,j,ok,k;
k=2;
do{
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(a[i][j]!=-1 && c[j][k-1]!=-1)
if(c[i][k]<c[j][k-1]+a[i][j])
{
c[i][k]=c[j][k-1]+a[i][j]; t[i][k]=j; }
ok=1;
for(i=1;i<=n;i++)
if(c[i][k-1]!=c[i][k]) { ok=0; break; }
k++;
}while(!ok);
}
void drum(int x,int y)
{
27
cout<<x<< ;
if(y!=1) drum(t[x][y],y-1);
}
void scrie()
{
if(c[p1][pasi]!=-1)
{
while(c[p1][pasi]==c[p1][pasi-1]) pasi--;
cout<<"drum maxim <<p1<< -> <<x<< prin <<pasi<<arce\n";
cout<<"costul <<c[p1][pasi]<<endl;
drum(p1,pasi); cout<<x<<endl;
}
else cout<<"nu exista\n";
}
void main()
{
citire(); cout<<"plecare: "; cin>.p1; cout<<"sosire: ";
cin>.x; cout<<"numar pasi: "; cin>>pasi;
initializari(); kalaba(); scrie();
}
28
Vom ataa acestei probleme un graf neorientat cu n*n noduri reprezentnd camerele labiruntului, ele
fiind numerotate n ordine 1,2,..,n*n. Dac dou camere sunt libere i nvecinate, atunci n graf
cele dou noduri vor fi adiacente. Vom memora graful prin matricea costurilor A n care costul unei
muchii este 1.
Pe baza matricii L i a regulilor de vecintate din enun vom construi matricea costurilor A de
dimensiune n*n astfel:
- se iniializeaz matricea A cu o valoare foarte mari (infinit)
- cum avem n*n camere, la generarea matricii costurilor ne vom referi la configuraia
labiruntului astfel: nodul i din graf corespunde camerei (x=(i-1)/n+1,y=(i-1)%n+1) din
labirint (relaii obinute prin liniarizarea matricii L)
- reciproc, nodul reprezentat de (x,y) este elementul p=n*(x-1)+y din matricea L.
n acest fel se verific toate camerele libere vecine i se completeaz matricea costurilor.
Pentru a determina cel mai scurt drum ntre camera de plecare i cea de sosire vom folosi
algoritmul Floyd-Warshall. Pentru a afia efectiv camerele de pe drumul minim determinat de la
nodul sursa nod_i la destinaia nod_f vom folosi strategia Divide et Impera, considernd c
dac avem un nod intermediar k i A[nod_i,nod_f]=A[nod_i,k]+A[k,nod_f], atunci nodul k
se afl pe drumul optim cutat, deci vom genera drumurile (nod_i,k) i (k,nod_f). Complexitatea
acestei metode este dat de complexitatea algoritmului Floyd-Warhall adic este O((n*n)3) ,
inacceptabil pentru valori n chiar relativ mici.
Programul urmtor implementeaz metoda descris mai sus.
#include<iostream.h>
#include<fstream.h>
#define N 15
#define INF 10000
short int L[N][N],A[N*N][N*N],n;
void citire()
{
ifstream f("lab.in";
char s[20]; int i,j;
f>>n; fgetc(f);
for(i=1;i<=n;i++)
{
fgets(s,20,f);
for(j=0;j<n;j++)
if(s[j]=='*') L[i][j+1]=0;
else L[i][j+1]=1;
29
}
f.close();
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++) cout<<L[i][j]<<\t;
cout<<"\n";
}
}
void construieste_graf()
{
int i,j,x,y;
for(i=1;i<=n*n;i++) //initializam matricea costurilor
for(j=1;j<=n*n;j++) A[i][j]=INF;
for(i=0;i<=n+1;i++) //bordam matricea L cu 0
L[0][i]=L[i][0]=L[i][n+1]=L[n+1][i]=0;
for(i=1;i<=n*n;i++)
{
x=(i-1)/n+1; y=(i-1)%n+1;
if(L[x][y]==1)
{
if(L[x][y-1]==1) //la stanga
A[n*(x-1)+y-1][i]=A[i][n*(x-1)+y-1]=1;
if(L[x][y+1]==1) //la dreapta
A[n*(x-1)+y+1][i]=A[i][n*(x-1)+y+1]=1;
if(L[x-1][y]==1) //deasupra
A[i][n*(x-2)+y]=A[n*(x-2)+y][i]=1;
if(L[x+1][y]==1) //dedesubt
A[i][n*x+y]=A[n*x+y][i]=1;
}
A[i][i]=0;
}
}
void Floyd_Warhall()
{
int i,j,k;
for(k=1;k<=n*n;k++)
for(i=1;i<=n*n;i++)
for(j=1;j<=n*n;j++)
if(A[i][j]>A[i][k]+A[k][j]) A[i][j]=A[i][k]+A[k][j];
}
void drum(int x,int y)
{
int k=1,gata=0;
while(k<=n*n && !gata)
{
if(x!=k && y!=k && A[x][y]==A[x][k]+A[k][y])
{
drum(x,k); drum(k,y); gata=1;
}
k++;
}
if(!gata) cout<<y<< ;
}
void afisare_drum(int x,int y)
{
30
if(A[x][y]<INF)
{
cout<<"\nlungime=<,A[x][y];
cout<<"prin celulele: x; drum(x,y);
}
else cout<<"\nNu exista drum\n";
}
void main()
{
int xi,yi,xf,yf,nod_i,nod_f;
citire(); construieste_graf();
cout<<"punctul de plecare (x,y)="; cin>>xi>>yi;
cout<<"punctul de sosire (x,y)="); cin>>xf>>yf;
nod_i=n*(xi-1)+yi; nod_f=n*(xf-1)+yf;
Floyd_Warhall(); afisare_drum(nod_i,nod_f); cout<<"\n";
}
Observaie: Complexitatea algoritmul devine O(n2) dac n loc de algoritmul Floyd-Warshall
folosim o parcurgere BF a grafului reprezentat prin liste de adiacen i atam nodurilor un vector de
marcare (algoritmul lui Lee).
Programul urmtor determin drumul minim ntre camera de plecare nod_i i camera de
sosire nod_f folosind pentru graful asociat listele de adiacen alocate dinamic. Tablourile utilizate de
parcurgerea BF sunt deasemenea alocate dinamic n heap pentru a mri spaiul de memorie
disponibil.
#include<iostream.h>
#include<fstream.h>
#define N 50
typedef struct nod{
short int inf;
nod *leg;
}AD;
AD *LA[N*N];
short int L[N][N],n;
short int *C,*T,*VIZ,*DMIN; //tablouri alocate dinamic in heap
void citire()
{
ifstream f(lab.in");
char s[20]; int i,j;
cin>>n; f.getc(f);
cout<<"\tLabirintul este:\n";
for(i=1;i<=n;i++)
{
f.gets(s,20,f); puts(s);
for(j=0;j<n;j++)
if(s[j]=='*') L[i][j+1]=0;
else L[i][j+1]=1;
}
f.close();
}
void Init() //aloca spatiu pentru tablourile C,T,VIZ,DMIN
{
int i;
C=new short int [n*n+1];
31
T=new short int [n*n+1];
VIZ=new short int [n*n+1];
for(i=1;i<=n*n;i++) VIZ[i]=0;
DMIN=new short int [n*n+1];
}
void construieste_graf()
{
int i,x,y;
AD *p;
for(i=1;i<=n*n;i++)
{
LA[i]=new AD; LA[i]->leg=0;
}
for(i=0;i<=n+1;i++) //bordam matricea L cu 0
L[0][i]=L[i][0]=L[i][n+1]=L[n+1][i]=0;
for(i=1;i<=n*n;i++)
{
x=(i-1)/n+1; y=(i-1)%n+1;
if(L[x][y]==1)
{
if(L[x][y-1]==1) //vecin la stanga
{
p=new AD; p->inf=n*(x-1)+y-1; p->leg=LA[i]->leg;
LA[i]->leg=p;
p=new AD; p->inf=i; p->leg=LA[n*(x-1)+y-1]->leg;
LA[n*(x-1)+y-1]->leg=p;
}
if(L[x][y+1]==1) //vecin la dreapta
{
p=new AD; p->inf=n*(x-1)+y+1; p->leg=LA[i]->leg;
LA[i]->leg=p;
p=new AD; p->inf=i; p->leg=LA[n*(x-1)+y+1]->leg;
LA[n*(x-1)+y+1]->leg=p;
}
if(L[x-1][y]==1) //vecin deasupra
{
p=new AD; p->inf=n*(x-2)+y; p->leg=LA[i]->leg;
LA[i]->leg=p;
p=new AD; p->inf=i; p->leg=LA[n*(x-2)+y]->leg;
LA[n*(x-2)+y]->leg=p;
}
if(L[x+1][y]==1) //vecin dedesubt
{
p=new AD; p->inf=n*x+y; p->leg=LA[i]->leg;
LA[i]->leg=p;
p=new AD; p->inf=i; p->leg=LA[n*x+y]->leg;
LA[n*x+y]->leg=p;
}
}
}
}
void BF(int nod,int s)
{
AD *x; int prim,ultim;
VIZ[nod]=1; T[nod]=0; DMIN[nod]=0;
32
prim=ultim=1; C[prim]=nod;
while(prim<=ultim)
{
nod=C[prim];
//extrag nodul din varful cozii
x=LA[nod]->leg; //lista vecinilor lui nod
while(x)
{
if(!VIZ[x->inf])
{
T[x->inf]=nod; VIZ[x->inf]=1;
DMIN[x->inf]=DMIN[nod]+1;
if(x->inf==s) return;
//am ajuns in punctul de sosire, opresc parcurgerea
C[++ultim]=x->inf; //adaug x->inf in coada
}
x=x->leg;
}
prim++; //avansez la urmatorul nod din coada
}
}
void drum(int i,int nod)
{
if(i!=nod) drum(T[i],nod);
cout<<i<< ;
}
void main()
{
int xi,yi,xf,yf,nod_i,nod_f;
citire(); construieste_graf(); Init();
cout<<"punctul de plecare (x,y)="; cin>>xi>>yi;
cout<<"punctul de sosire (x,y)="; cin>>xf>>yf;
nod_i=n*(xi-1)+yi; nod_f=n*(xf-1)+yf;
BF(nod_i,nod_f); //parcurg BF graful din nodul de plecare
if(DMIN[nod_f])
{
cout<<"\nlungime=<< DMIN[nod_f]<<este <<drum(nod_f,nod_i)<<
\ndrum prin nodurile: ";,
cout<<"\n";
}
else
cout<<"\nNu exista drum intre camera <<nod_i<< si camera <<nod_f<<endl;
}
33
Bibliografie