1

CUPRINS

1. Găsirea drumurilor într-un graf orientat 2. Drumuri optime într-un graf orientat 3. Algoritmul lui Dijkstra 4. Algoritmul lui Dantzig 5. Algoritmul Bellman-Ford 6. Determinarea drumurilor minime în grafuri orientate aciclice 7. Algoritmul Floyd-Warhall pentru drumuri minime 8. Algoritmul Floyd-Warshall pentru drumuri maxime 9. Algoritmul Bellman-Kalaba 10. Drumuri minime într-un labirint

2

11. Găsirea drumurilor într-un graf orientat
Dacă privim graful ca imagine a unui sistem, nodurile reprezentând componentele sistemului, atunci o interpretare imediată a unui arc (xi,xj) este că, componenta xi influenţează direct componenta xj. Dacă nodurile au semnificaţia de stări posibile ale unui sistem atunci un arc (xi,xj) semnifică faptul că sistemul poate trece direct din starea xi în starea xj. În ambele cazuri se vede că avem de-a face doar cu informaţii despre legături directe; totuşi, chiar dacă o componentă xi nu influenţează direct componenta xj ea o poate influenţa prin intermediul altor componente, existând un şir de componente intermediare: x1 x2 ,..., xk, fiecare influenţând-o direct pe următoarea şi xi direct pe x1 iar xk direct pe xj. Astfel, dacă dintr-o stare xi nu se poate trece direct într-o stare xj s-ar putea totuşi în mai multe etape, prin alte stări intermediare. Deoarece găsirea acestor influenţe sau treceri posibile este de obicei foarte importantă iar pentru un sistem cu mii sau zeci de mii de componente acest lucru nu mai poate fi făcut "din ochi", este necesară formalizarea noţiunii de "influenţe" şi "treceri" posibile, nu neapărat directe. Acest lucru a şi fost făcut mai sus, deoarece este evident că "xi influenţează xj" sau "din starea xi se poate trece în starea xj" este echivalent cu existenţa în graf a unui drum de la nodul xi la nodul xj. În continuare vom da un algoritm prin care putem găsi toate drumurile dintr-un graf orientat cu un număr finit de noduri. Pasul 1. Se construieşte matricea booleană a adiacenţelor directe corespunzătoare grafului, notată cu A. În aceasta se află, evident, toate drumurile de lungime 1. Este interesant de văzut ce legătură există între această matrice şi drumurile de lungime 2. Fie două noduri xi şi xj oarecare din graf. Existenţa unui drum de lungime 2 între ele presupune existenţa unui nod xk, din graf, cu proprietatea că există atât arcul (xi,xk) cât şi arcul (xi,xk). Pentru a vedea dacă acesta există, luăm pe rând fiecare nod al grafului şi verificăm dacă există sau nu ambele arce ((xi,xk) şi (xi,xk)). Aceasta este echivalent cu a verifica dacă, în matricea booleană a adiacenţelor directe, există vreun indice k astfel încât elementul k al liniei i şi elementul k al coloanei j să fie ambele egale cu 1. Dacă folosim operaţiile algebrei booleene de adunare şi înmulţire:   0 1 0 1 + × 0 0 1 0 0 0 1 1 1 1 0 1 atunci verificările de mai sus sunt echivalente cu a verifica dacă elementul de pe poziţia (i,j) din A2 este egal cu 1. Valoarea 1 spune doar că există cel puţin un drum de lungime 2 de la xi la xj. Dacă dorim să vedem şi câte sunt, vom folosi regulile de înmulţire şi adunare obişnuită. De asemenea, se poate observa că existenţa unui drum de lungime 3 de la xi la xj presupune existenţa unui nod xk astfel încât 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 încât 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ă existenţa drumurilor de lungime k este dată de valorile matricei Ak, dacă s-au folosit regulile algebrei booleene şi numărul lor este dat de Ak, dacă s-au folosit regulile obişnuite. Pasul 2. Vom calcula succesiv puterile lui A până la puterea An-1 Dacă între nodurile xi şi xj există un drum de lungime ≥ n atunci el va conţine un număr de noduri mai mare sau egal cu n+1 şi, cum în graf sunt doar n vârfuri, este clar că cel puţin unul, să zicem xk, va apărea de două ori. Vom avea în acest caz un drum de la xi până la prima apariţie a lui xk, şi un drum de la ultima apariţie a lui xk la xj. Eliminând toate nodurile dintre prima apariţie a lui xk şi ultima apariţie a sa vom obţine un drum de la xi la xj, în care xk apare o singură dată. Aplicând

3
acest procedeu pentru toate nodurile care apar de mai multe ori pe drum, vom obţine 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 poziţia (i,j) este diferită de 0. Pentru deciderea existenţei 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 existenţa drumurilor dintre noduri, nu şi numărul lor, vom folosi înmulţirea şi adunarea booleană şi conform observaţiei de mai sus: dij = 1, daca exista drum de la xi la xj 0, in caz contrar

Procedeul de mai sus nu asigură decât aflarea faptului dacă există sau nu drum între două noduri, eventual ce lungime are şi câte sunt de această lungime. Totuşi, î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ă, paşii următori ai algoritmului vor fi dedicaţi găsirii lor. Pentru găsirea acestora se foloseşte reprezentarea grafului prin matricea latină. Matricea latină este o matrice pătratică nxn în care pe o poziţie 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 = şi matricea L(1), definită prin: l(1)ij = numită matricea latină redusă. Găsirea unui drum de lungime 2 de la xi la xj presupune găsirea unui nod cu proprietatea că există arcele (xi,xk) şi (xk,xj) şi memorarea vectorului (xi, xk, xj). Aceasta este echivalent cu a găsi un indice k astfel încât elementul de pe poziţia k a liniei i, din matricea L, să fie xi,xk şi elementul de pe poziţia k al coloanei j, din matricea L(1), să fie xj. Vom înmulţi deci matricea L cu matricea L(1), folosind însă nişte reguli de calcul speciale, numite înmulţire şi adunare latină. Definiţia 1: Se numeşte alfabet o mulţime de semne numite simboluri sau litere {si│i∈ I} unde I este o mulţime oarecare de indici, finită sau nu. Definiţia 2: Se numeşte cuvânt un şir finit de simboluri notat si1si2.....sin. Definiţia 3: Se numeşte înmulţire latină o operaţie definită pe mulţimea cuvintelor unui alfabet, notată "xL", astfel: si1si2.....sin xL sj1sj2.....sjm = si1si2.....sin sj1sj2.....sjm (produsul a două cuvinte se obţine prin concatenarea lor) Înmulţirea latină este asociativă, are ca element neutru cuvântul vid, nu e comutativă şi un element este inversabil doar dacă este cuvântul vid. Definiţia 3: Se numeşte adunare latină o funcţie definită pe mulţimea cuvintelor unui alfabet cu valori în mulţimea parţilor mulţimi cuvintelor, notată „+Lastfel: 0, în caz contrar xj, dacă există arcul (xi,xj) 0, în caz contrar

sin... Din felul cum a fost construită.. unde operaţia de înmulţire este uşor modificată. Această metodă. deşi mai simplă nu spune însă şi care sunt aceste drumuri. în caz contrar.. − puterile lui L mai mari sau egale cu n au toate elementele egale cu 0.. În aceste condiţii putem enunţa problema drumului optim astfel: .. Această intensitate are semnificaţia unei valori numerice (pozitive sau negative) asociate arcului corespunzător legăturii a cărei intensitate o măsoară. Pentru formalizarea problemei vom introduce noţiunea de valoare a unui drum. a drumului optim între acestea. Se calculează succesiv matricile: L2 = L xLL(1). − punctaj realizat etc. − cantitatea transportată pe ruta respectivă.sjm) (suma a două cuvinte este mulţimea formată din cele două cuvinte) Pasul 4.xj) sau cu cij. de exemplu.xj) cu c(xi. Vom nota în continuare valoarea unui arc (xi.. pentru o anumită pereche de noduri (sau mai multe perechi). Pas 2. după o aplicare a acestuia.. Pentru fiecare linie i se adună boolean la aceasta toate liniile j pentru care aij=1. produsul dintre două elemente ale matricilor fiind 0. Drumuri optime într-un graf orientat În marea majoritate a problemelor care pot fi modelate prin grafuri nu ne interesează numai dacă există sau nu legături între componentele reprezentate prin nodurile grafului ci şi intensitatea acestora.. Se construieşte matricea de adiacenţă A. − matricea Ln-1 conţine toate drumurile hamiltoniene din graf (dacă există). pentru găsirea lor aplicându-se. matricea Lk va conţine toate drumurile elementare de lungime k.Lk+1 = LkxLL(1) folosind operaţiile de înmulţire şi adunare latină.. dacă unul dintre ele este 0 sau au un nod comun şi este produsul latin al lor. L3 = L2xLL(1). − câştigul realizat prin trecerea de la o stare la alta a sistemului. Se reia pasul 2 până când.sjm = (si1si2. ridicarea unei matrici de 100×100 la puterea 100) pentru obţinerea acesteia se poate aplica şi următorul algoritm: Pas 1. sj1sj2.. care este egală cu suma valorilor arcelor care îl compun. dacă graful are 100 de noduri.4 si1si2. alfabetul fiind mulţimea nodurilor grafului.. − costul parcurgerii rutei reprezentate prin arcul corespunzător.. − consum de energie pentru efectuarea trecerii respective...sin „+L sj1sj2. Cum un drum elementar poate avea cel mult n noduri (câte are graful cu totul) rezultă că: − primele n-1 puteri ale lui L conţin toate drumurile elementare din graf. Pas 3. matricea rămâne aceeaşi (nu mai apare nici un 1) Ultima matrice obţinută este matricea drumurilor D numită şi matricea conexiunilor totale. înmulţirea latină.... Observaţie: Deoarece obţinerea matricii D prin metoda de mai sus presupune un volum foarte mare de calcule (de exemplu. . Una din problemele care poate apărea în aceste situaţii este găsirea. În aplicaţiile economice această valoare poate fi: − lungimea drumului dintre două localităţi. − capacitatea maximă a rutei respective.. − durata parcurgerii rutei respective.

algoritmii existenţi pentru determinarea drumurilor optime în grafuri se împart în două mari categorii: algoritmi pentru drumuri minime în grafuri orientate. Se adaugă arcele găsite la pasul 2 la mulţimea A apoi se reia algoritmul de la pasul 2. În general. Dacă nu există circuite de valoare negativă atunci valoarea oricărui drum este mai mare sau egală cu a drumului elementar corespunzător.U) şi o funcţie care asociază fiecărui arc o valoare reală. Dacă în graf nu există circuite atunci există şi drum de valoare minimă şi drum de valoare maximă. 1. respectiv algoritmi pentru drumuri maxime. deci drumul de valoare minimă (dacă există) va fi un drum elementar. pentru o pereche dată de noduri. Dacă mulţimea nodurilor grafului este infinită atunci pot exista o infinitate de drumuri elementare distincte între cele două noduri şi mulţimea valorilor acestora poate avea orice formă (închisă sau nu. Dacă există un circuit de valoare pozitivă atunci există drumuri de valoare oricât de mare şi mulţimea valorilor drumurilor este nemărginită superior. Obs. mulţimea valorilor drumurilor este nemărginită inferior. există două mari categorii de probleme şi anume: . pentru noua mulţime A. Pasul 3. dacă există un circuit de valoare negativă înseamnă că există drumuri de valoare oricât de mică (cele care conţin acest circuit).determinarea drumurilor optime între toate perechile de vârfuri (surse multiple. destinaţii multiple) ∑ k! n -1 1 .5 "Dat un graf G=(X. Analog.determinarea drumurilor optime de sursă unică (sursă unică. deci drumul de valoare maximă (dacă există) va fi un drum elementar. Un număr finit de noduri n atrage după sine existenţa unui număr finit de arce (cel mult n2) şi a unui număr finit de drumuri elementare ( cel mult n× n!× k =1 ). Pasul 4. mărginită sau nu) devenind foarte greu de caracterizat cazurile când minimul dorit există. Dacă nu există nici un astfel de arc se trece la pasul 4. va avea majorant. noduri din care nu "pleacă" nici un arc). Cum mulţimea drumurilor elementare este finită (şi deci şi mulţimea valorilor lor) va avea minorant şi am lămurit problema compatibilităţii problemei. altfel spus. să se găsească. deci. dacă nu există circuite de valoare pozitivă atunci valoarea oricărui drum este mai mică sau egală cu a drumului elementar corespunzător. fiecare înmulţită cu numărul de parcurgeri ale circuitului respectiv. 1. În concluzie. destinaţii multiple) . Deoarece oricărui drum d îi corespunde un drum elementar de (obţinut prin eliminarea tuturor subcircuitelor lui d) putem calcula valoarea oricărui drum ca sumă între valoarea drumului elementar corespunzător şi valorile unor subcircuite ale sale. Pasul 2. obţinută prin parcurgerea acestuia de oricâte ori dorim) şi. ne vom limita în continuare doar la acestea. Deoarece totuşi majoritatea covârşitoare a problemelor economice se modelează prin grafuri cu număr finit de noduri. Dacă au rămas noduri în afara lui A atunci graful conţine circuite. Deasemeni. neexistând drum de valoare maximă. neexistând drum de valoare minimă. Dacă în graf nu există decât arce de valoare pozitivă atunci există drum de valoare minimă. Dacă A conţine mulţimea tuturor nodurilor atunci graful nu conţine circuite. Dacă în graf nu există decât arce de valoare negativă atunci există drum de valoare maximă. 1. Se găsesc 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). Obs. prima întrebare care se pune este dacă aceasta admite minim. drumul (drumurile) de valoare optimă (minimă sau/şi maximă) între cele două noduri şi valoarea acestuia (acestora)" Deoarece este vorba de găsirea minimului unei mulţimi de numere reale. Cum mulţimea drumurilor elementare este finită (şi deci şi mulţimea valorilor lor). Obs. Deoarece din cele de mai sus se sesizează importanţa existenţei circuitelor într-un graf vom da în continuare un algoritm de depistare a existenţei circuitelor într-un graf: Pasul 1. Se construieşte mulţimea A formată din nodurile pentru care toate arcele incidente sunt incidente spre interior ( noduri în care toate arcele "intră" sau.

lungimea celui mai scurt drum şi unul dintre drumurile minime de la x0 la xi.2. până când am determinat drumuri minime de la x0 către toate vârfurile pentru care există drum pornind din x0.xp). se vor obţine în ordine: D1=(1.2. considerând nodul de plecare 1. Următorul drum în ordinea lungimilor va fi dat fie de un alt arc cu extremitatea iniţială x0.5) de lungime 2 D3=(1.5.xj. o funcţie l:U R+ şi un nod x0.2. dar fără circuite de cost negativ determinarea drumurilor minime în grafuri orientate aciclice (din CLR) Floyd-Warhall: drumuri minime între oricare două noduri Bellman-Kalaba: drumuri maxime cu sursă unică şi număr fixat de paşi Floyd-Warshall: drumuri maxime între oricare două noduri în grafuri fără circuite Algoritmul lui Dijkstra Problemă: Fiind dat un graf orientat G=(X.xj) de lungime minimă. Exemplu: Pentru graful din figura alăturată.5.4) de lungime 5 De la 1 la 6 nu există drum.U).2) de lungime 1 D2=(1. Algoritmul utilizează metoda Greedy generând drumurile minime în ordinea crescătoare a lungimilor. Evident cel mai scurt drum de la x0 la unul din celelalte vârfuri ale grafului este dat de arcul (x0. Alegem în continuare drumuri în ordinea crescătoare a lungimilor. să se determine pentru toate vârfurile xi pentru care există drum de la x0 la xi. fie de un drum (x0.6 Algoritmii prezentaţi în continuare sunt: Dijkstra: determină drumurile minime cu sursă unică în grafuri cu costuri nenegative Dantzig: determină drumurile minime cu sursă unică în grafuri cu costuri nenegative Bellman-Ford: drumuri minime cu sursă unică în grafuri care admit costuri negative. Pentru aceasta se consideră S mulţimea vârfurilor xj∈ X pentru .3. 2 7 1 1 2 1 3 5 3 1 4 1 3 6 Se porneşte din x0.3) de lungime 4 D4=(1.

nod) S←∅ Q←X(G) . adică este linia x0 din matricea costurilor. caz în care dj dk+c(k.3} S={1. Iniţial: 0 dacă i=x0 sau C(x0.j)<dj.n..i)=∞ Ti= x0 dacă C(x0. vectorul d va conţine costurile (lungimile) drumurilor minime de la x0 la celelalte noduri..5.dn) astfel încât: lungimea drumului minim de la x0 la xi.i)≠∞ şi i≠ x0 La fiecare actualizare de forma dj Dk+c(k. Deci pentru xj∈ X-S.xk.. Pentru a alege nodul xk∈ X-S ce urmează a fi adăgat în S vom folosi un vector D=(d1. adăgăm în S acel nod xk∈ X-S cu proprietatea că drumul minim de la x0 la xk are cel mai mic cost dintre toate drumurile de la x0 la xp. dacă xi∉ S 7 Iniţial di=C(x0. În final. adăgăm în S nodul xk cu proprietatea că dk=min{dj | xj∈ X-S}. dj se modifică după adăugarea lui xk la S numai dacă dk+c(k.5. Pentru a reţine şi drumurile minime (nu numai lungimile lor) vom considera un vector numit T (tată) care reţine indicele precedentului fiecărui nod în drumul minim de la x0 la acel nod. dacă xi∈ S di= lungimea drumului minim de la x0 la xi ce foloseşte numai vârfuri din S.i). La un moment dat.d2.3.4} Se observă că drumul de la x0 la xk (nodul ce urmează să-l adăugăm în S la un moment dat) trece numai prin vârfuri din S (cu excepţia lui xk). ….2..c. Drumul minim de la x0 la xj ce foloseşte noduri din S (inclusiv xk) va fi de forma (x0. dacă pentru un nod xj nu există drum de la x0 la xj.nod) Initializeaza_Sursa_Unica(G. Pentru exemplul considerat S va avea pe rând următorul conţinut: S={1} S={1.5} S={1..j) vom avea şi o actualizare a vectorului T de forma Tj k. După adăgarea lui xk în S trebuie actualizate valorile lui d pentru elementele care nu sunt în S. deci fie când S=X (dacă există drumuri de la x0 la toate celelalte noduri).2} S={1. (∀ )i=1. La fiecare pas.care am găsit drum minim de la x0 la xj.xj). 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 adăgat. cu xp∈ X-S.2.2. fie când mulţimea X-S cuprinde numai noduri pentru care nu există drumuri pornind din x0 la ele (caz în care min{dj| xj∈ X-S}=∞ ). atunci dj=∞ .j). Pentru reprezentarea mulţimii S se poate folosi vectorul caracteristic S cu n componente definit astfel: 0 dacă xi∉ S Si= 1 dacă xi∈ S Algoritmul lui Dijkstra poate fi descris astfel: algoritm Dijkstra(G. Iniţial S={x0}. Algoritmul se încheie când S conţine toate nodurile xj pentru care există drum de la x0 la xj.

0.v) atunci D[v]←D[u]+c(u. T[5] 2 S[6]= ∞ . atunci se reactualizează D[v] şi T[v]. poate fi îmbunătăţit pe baza vârfului u. ∞ . ∞ ) T=(0. D[2]+C[2. 0) 1) determinăm min(D[i]| S[i]=0)=> min=1. D[2]+C[2. D[5]=3>D[2]+C[2. 1. respectiv pentru toate nodurile v∈ S avem D[v] egal cu distanţa mnimă. 8. 0.nod) pentru fiecare v∈ X(G) executa D[v]← ∞ T[v]← 0 sfarsit pentru D[nod]←0 8 După iniţializare avem T[v]=0 pentru orice nod v∈ U.v) se verifică dacă drumul minim la v . În algoritmul de mai sus Q este o coadă conţinând toate nodurile din X-S indexate prin valorile D corespunzătoare.c) sfarsit pentru sfarsit cat timp unde: Initializeaza_Sursa_Unica(G. k=2 => S[2] 1 Actualizăm distanţele la nodurile neselectate încă. 1. Relaxeaza(u.v.v).4]=1+∞ .6]=1+∞ . 0) S=(1. S[3]=0. şi dacă da. Exemplu: Considerăm graful anterior şi punctul x0=1. determinat până în acel moment. T[3] 2 S[4]=0. ∞ . 0. 3.c) daca D[v]>D[u]+c(u. 0. D[4]=∞ .cat timp Q≠0 executa u←Extrage_Minim(Q) S←S ∪ {u} pentru fiecare v vecin cu u executa Relaxeaza(u. nu se poate actualiza S[5]=0. D[6]= ∞ . Pseudocodul următor realizează un pas de relaxare pe arcul (u. Un pas de relaxare poate determina descreşterea valorii estimării drumului minim D[v] şi reactualizarea câmpului T[v] ce conţine predecesorul nodului v pe drumul minim. introduce u în S şi relaxează arcele divergente din u.v. 1. Algoritmul iterează selectarea unui nod u∈ X-S pentru care estimarea drumului minim este minimă. 0. D[v]=0 pentru v=nod (nodul sursă) şi D[v]=∞ pentru v∈ U-{ nod} . 2. ∞ .v) T[v]←u sfarsit daca Algoritmul Dijkstra operează prin menţinerea unei mulţimi S de noduri pentru care costurile finale corespunzătoare drumurilor minime de la sursa s au fost deja determinate. ∞ ) T=(0. 2. Cei trei vectori au valorile iniţiale: D=(0.5]=1+1=2 => D[5] 2. nu se poate actualiza După primul pas configuraţia celor trei vectori este: D=(0. 2. În procesul de relaxare al unui arc (u. 0. 0) . 1.3]=1+7=8 => D[3] 8. 1. D[3]=∞ >D[2]+C[2. 0.

xp. T[4] 3 S[6]=0. 3.j++) c[i][j]=INF. 0. S[6]=0. 1. 1.D[N]. 0. k=3 => S[3] 1 Actualizăm distanţele la nodurile neselectate încă. 0) determinăm min(D[i]| S[i]=0)=> min=∞ şi algoritmul se încheie pentru că nu există nici-un drum de la nodul 1 la nodul 6 9 Programul care implementează algoritmul lui Dijkstra este următorul: #include<iostream.i.h> #include<fstream. k=5 => S[5] 1 Actualizăm distanţele la nodurile neselectate încă. 4. 0) determinăm min(D[i]| S[i]=0)=> min=5.in”). while(!feof(f)) { f>. 0) S=(1. 0) determinăm min(D[i]| S[i]=0) => min=2. 1. 1. D[5]+C[5. 2. 1. } void minim(int& q) { int m. 0.h> #define N 50 #define INF 1<<15 int c[N][N]. for(i=1. ∞ ) T=(0. 5. 1. 0) S=(1. nu se poate actualiza S[6]=0.i++) for(j=1. D[6]=∞ . 0. m=2*INF. void citire() { ifstream f(„graf. ∞ ) T=(0.x. //numarul de noduri si nodul de plecare //initializam matricea costurilor for(i=1.i++) c[i][i]=0. 2. 0) determinăm min(D[i]| S[i]=0)=> min=4. 5. 1. 1.4]=4+1=5 => D[4] 5.3]=2+2=4 => D[3] 4. 2.z. 4. //arcul si costul sau c[x][y]=z. 5. 0) S=(1. D[5]+C[5. 1.y.close(). D[6]=∞ . 1. 1. 5. T[3] 5 S[4]=0. ∞ . f>>n>>xp. . 0. 0. D[4]=∞ .x>>y>>z.j. 1. nu se poate actualiza După al doilea pas configuraţia celor trei vectori este: D=(0.6]=2+∞ . int T[N]. int i. nu se poate actualiza După al patrulea pas configuraţia celor trei vectori este: D=(0. 3. k=4 => S[4] 1 Actualizăm distanţele la nodurile neselectate încă. S[4]=0. nu se poate actualiza După al treilea pas configuraţia celor trei vectori este: D=(0. 4.i<=n. D[6]= ∞ .4]=2+∞ . D[4]+C[4.2) 3) 4) 5) S=(1. D[3]+C[3. 1.i<=n. 0. D[4]=∞ > D[3]+C[3. 2.n. S[3]=0. D[3]=8>D[5]+C[5.6]=5+∞ . 1.S[N]. 5. ∞ ) T=(0. 1.j<=n.6]=4+∞ . 2. 2. 1. } f.

T si D S[k]=1. cout<<"\n". q=i. } } void afisare_drumuri() { int i. for(j=1.k. //nu mai pot construi drumuri minime else { //actualizam vectorii S. } void drum(int i) { if(i) { drum(T[i]). } } }while(ok).j++) if(!S[j]&&D[j]>D[k]+c[k][j]) { D[j]=D[k]+c[k][j].ok.i++) if(i!=xp) if(D[i]==INF) cout<<"\nNu exista drum de la :”<<xp<<”la “<<i<endl. //determina nodul k aflat la distanta minima x++. T[xp]=0.} 10 Algoritmul lui Dantzig . else { cout<<"\nDrumul minim de la “<<xp<<”la “<<i<<”: ". afisare_drumuri().i++) { D[i]=c[xp][i]. if(D[k]==INF||x==n) ok=0.x. drum(i).i<=n. do{ minim(k). if(c[xp][i]<INF) T[i]=xp. ok=1. } } void main() { citire(). } S[xp]=1. cout<<"\tLungimea drumului este "<<D[i]<<endl. } } void determinare_drumuri() { int i.i<=n. for(i=1. cout<<i<<” ”. //initializari for(i=1. S[i]=0.i++) if(!S[i]&&D[i]<m) { m=D[i]. T[j]=k. determinare_drumuri(). x=0.j<=n.j.for(i=1.i<=n. else T[i]=0.

Urmează procesul de identificare a arcelor care se vor adăuga grafului parţial.au extremitatea iniţială într-un nod i deja selectat . d[2]=2 şi se adaugă la graful parţial arcul (1. adică selectarea nodurilor se face în ordinea apropierii lor de nodul p de plecare. arce care nu participă la construirea nici unui drum de cost minim cu plecare din p.k]=min (unde d[i] este distanţa minimă de la p la i şi cost[i. În fapt. deoarece acest proces conduce la obţinerea unor drumuri de la p la k cu costul minim determinat. pe parcursul algoritmului se elimină arce ale grafului G. la fiecare pas al parcurgerii se identifică un nou nod. Considerăm ca exemplu graful următor: 2 2 4 2 2 1 4 1 1 6 2 1 3 5 7 Pasul 1: se selectează nodul 2.U) în care orice drum care pleacă din p este minim.k] este cosul arcului de la i la k) Algoritmul se opreşte când fie au fost selectate toate nodurile.d[i]+cost[i. cel mai apropiat nod faţă de nodul p de plecare.11 Enunţ: Se consideră un graf orientat G=(X. Strategia algoritmului se bazează pe o parcurgere în lăţime a grafului G. Pentru orice vârf i∈ X se cere determinarea tuturor drumurilor de la p la i care au costul minim. Aceste arce respectă simultan condiţiile: . Notăm cu k acest nod. Pentru rezolvarea acestei probleme sa va folosi algoritmul lui Dantzig.2) .Se desemnează un vârf de plecare p. fie când nu mai există drumuri de la p către vârfurile neselectate.au extremitatea finală în nodul k .U) şi o funcţie cost:U R+. Pe baza costurilor minime determinate succesiv. Acest algoritm obţine un graf parţial al lui G=(X.

5) 2 4 2 1 6 1 2 1 3 5 Pasul 4: se selectează nodul 4.12 2 4 2 6 1 3 5 Pasul 2: se selectează nodul 3. d[5]=3. se adaugă la graful parţial arcele (2.4) şi (5.4) .3) 2 4 2 6 1 2 3 5 Pasul 3: se selectează nodul 5. d[3]=3 şi se adaugă la graful parţial arcul (1. se adaugă la graful parţial arcele (2. d[4]=4.5) şi (3.

d[6]=6.d[N].k.y.j++) { a[i][j]=INF.j. f>>n. void citire() { ifstream f(„graf.6) 2 2 2 2 1 1 1 6 4 2 1 3 5 Pentru exemplul considerat.h> #include<fstream.i<=n. int i. Complexitatea este O(n2).z.j<=n.h> #define N 50 #define INF 1<<15 int a[N][N]. //numarul de noduri //initializam matricea costurilor si matricea grafului partial for(i=1.st[N].13 2 2 4 2 1 1 1 6 2 1 3 5 Pasul 5: se selectează nodul 6.x. b[i][j]=INF.min. int sel[N].n. se adaugă la graful parţial arcul (4. costul minim de la 1 la 6 este 6 şi se obţine prin drumurile: 1 2 4 6 1 2 5 4 6 1 3 5 4 6 Programul următor implementează algoritmul lui Dantzig. } .sos.b[N][N].in”). #include<iostream.i++) for(j=1.p.

i<=n. }} for(i=1.sos++) if(p!=sos) { for(i=1.close().i<=n-1. cout<<"plecare: ". b[i][i]=0.i++) { a[i][i]=0.i<=n.//arcul si costul sau a[x][y]=z.j.i<=n.j. sel[i]=0. dantzig().j++) if(sel[j]&&(d[j]+a[j][k]==min)) b[j][k]=a[j][k].sos<=n. st[k]=i.j++) cout<<st[j]. d[p]=0. } while(!feof(f)) { f>>x>>y>z. cout<<sos<<" de cost “<<d[sos]<<”\n". citire().i++) if(b[st[k-1]][i]<INF && !sel[i]) { if(i!=sos) { sel[i]=1. } } } void drum(int k) { int i.j<k.i++) sel[i]=0.i++) for(j=1. for(i=1. } f.j<=n. min=INF. for(sos=1. cin>>p. if(min!=INF) { sel[k]=1.j.j<=n.i++) { minim(). } } void dantzig() { int i. drum(k+1). } } } void main() { int i. for(i=1. d[k]=min. for(j=1. k=j.i<=n. } else { for(j=1. sel[p]=1. for(i=1. 14 . st[1]=p. sel[p]=1.j++) if(sel[i]&&!sel[j]) if(min>d[i]+a[i][j]) { min=d[i]+a[i][j].} void minim() { int i. drum(2).

s) pentru i=1. Algoritmul efectuează n-1 treceri complete prin mulţimea arcelor grafului (orice drum elementar poate fi format din maxim n-1 arce). Algoritmul pseudocod poate fi descris astfel: algoritm Bellman_Ford(G.c.15 Algoritmul Bellman-Ford Enunţ: Se consideră un graf orientat G=(X. 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 situaţia în care nodul de plecare nu face parte dintr-un circuit de cost negativ.s) Initializeaza_Sursa_Unica(G.j) este ca d[j]> d[i]+c[i. În acest caz algoritmul evidenţiază prezenţa circuitului de cost negativ ce cuprinde nodul sursă. Se desemnează un nod p de plecare. existenţa unui circuit negativ face ca la o nouă trecere prin mulţimea arcelor să fie în continuare posibilă minimizarea costului unui drum. În final.v.j] p d[i] i Condiţia ca distanţa de la p la j să poată fi minimizată prin arcul (i. Mulţimea X conţine n vârfuri. Se vor detecta situaţiile în care există circuite de cost negativ care includ nodul p. j d[j] c[i.j)∈ U să participe la minimizarea distanţei de la p la j.c) sfarsit pentru pentru fiecare arc (u. Strategia algoritmului este aceea de minimizare succesivă a costului drumului minim de la p la orice nod j din graf (d[j]) până la obţinerea costului minim.n-1 executa Relaxeaza(u.v)∈U(G) executa daca D[v]>D[u]+c(u.j] Notăm cu n numărul de noduri din graf. Această operaţie se face prin verificarea posibilităţii ca fiecare arc (i.U) şi o funcţie de cost c:U→ R. Operaţia se face cu o trecere completă prin toate arcele grafului. Pentru orice nod j∈ X se cere determinarea drumului de cost minim de la p la j.v) atunci returneaza FALS sfarsit daca sfarsit pentru returneaza ADEVARAT .

n. lc[i]->leg=0.in”). f>>n. //adresa urmatorului vecin }LST. } int minimizeaza() { int ok=0. LST *lc[N]. În situaţia în care graful este memorat în liste de adiacenţă complexitatea algoritmului este O(n*m). t[p->inf]=j. #include<iostream. int i. for(i=1. Ca şi algoritmul lui Dijkstra. Algoritmul returnează ADEVARAT dacă şi numai dacă graful nu conţine circuite de cost negativ accesibile din sursa s.h> #define N 50 #define INF 32000 typedef struct nod{ int inf. . x->leg=lc[i]->leg.j. } p=p->leg.start.co.j.close(). lc[i]->leg=x. void citire() { ifstream f(„graf.j<=n. x->inf=j.j) { p=lc[j]->leg. } while(!feof(f)) { f>>i>>j>>co. funcţia minimizeaza() efectuează o trecere completă prin toate arcele grafului în vederea descreşterii costurilor drumurilor cu plecare din nodul sursă desemnat prin start.16 Funcţiile Initializeaza_Sursa_Unica() şi Relaxeaza() sunt cele prezentate la algoritmul lui Dijkstra. while(p) { if(d[p->inf]>d[j]>c) { ok=1. //adaug pe j in lista vecinilor lui i } f. d[p->inf]=d[j]>c.h> #include<stdlib.d[N]. LST *p. algoritmul Bellman_Ford utilizează tehnica de relaxare. x=new LST.i) //aloca adrese pentru listele de vecini { lc[i]=new LST. În programul următor. x->c=co. //costul muchiei nod *leg. LST *x. //listele de adiacenta (vecini) int t[N]. procedând la descreşterea estimării D[v] a drumului minim de la sursa s la fiecare nod v∈ X până când este obţinut costul adevărat corespunzător unui drum minim. for(j=1. //vecinul int c.i<=n.h> #include<fstream.

citire(). } } void drum(int x) { if(x) { drum(t[x]). sunt relaxate toate muchiile divergente din acel vârf. Algoritmul începe prin sortarea topologică a grafului pentru impunerea unei ordini liniare a nodurilor. ok=minimizeaza(). for(i=1. drum(i).i) { t[i]=0. În continuare. cin>start.i) if(i!=start) { cout<<"cost “<<d[i]<<” prin ". . cout<<"\n". chiar dacă graful conţine arce de cost negativ. d[i]=INF.i<=n-1.i) ok=minimizeaza(). Dacă există drum de la nodul u la nodul v atunci u precede v în ordinea topologică. putem determina drumurile minime de sursă unică în timp O(n+m). exit(0).} } return ok. //a n-a trecere va negativ if(ok) { cout<<"\nCircuit de cost negativ\n". } } void main() { int i. for(i=1.U) cu n noduri şi m arce. } } 17 depista circuite de cost Drumuri minime în grafuri orientate aciclice Prin relaxarea muchiilor unui graf orientat aciclic ponderat G=(X. cout<<x.i. //initializari } d[start]=0. se efectuează o singură trecere peste nodurile sortate topologic şi pe măsură ce fiecare vârf este procesat. } void bellman_ford() { int ok. cout<<"plecare: ". for(i=1.i<=n.i<=n. bellman_ford(). Drumurile minime sunt totdeauna bine definite într-un astfel de graf datorită faptului că acesta nu poate avea cicluri negativ.

for(i=1.in”). c[i][i]=0. x=new AD.d[N]. ordinea topologică este dată de valorile din vectorul m.j<=n. L[i]->leg=x. AD *L[N].j++) c[i][j]=INF. .v.m[N].co.T[N].s) sorteaza in ordine topologica varfurile din G Initializeaza_Sursa_Unica(G. void citire() { int i. }AD. T este vectorul predecesorilor.n. } for(i=1.s) pentru fiecare nod u in ordinea data de sortarea topologica executa pentru fiecare nod v vecin cu u executa Relaxaza(u. algoritm GOA_drumuri_minim(G. c[i][j]=co.close().h> #define N 50 #define INF 32000 typedef struct nod{ int inf. //listele vecinilor int c[N][N].j. L[i]->leg=0.//adaug j in lista vecinilor lui i } f. d este vectorul distanţelor minime determinate.v. x->leg=L[i]->leg.c) sfarsit pentru sfarsit pentru Sortarea topologică se realizează cu ajutorul parcurgerii în adâncime DF. Programul următor implementează algoritmul descris mai sus: #include<iostream.k. } while(!feof(f)) { f>>i>>j>>co.s. Funcţiile Initializeaza_Sursa_Unica() şi Relaxaza(u.i++) //initializare liste vecini { L[i]=new AD. nod *leg. x->inf=j. în ordinea inversă a timpilor de încheiere a explorării unui nod.i<=n. Lista este realizată în vectorul m care va fi completat de la sfârşit spre început.c) au aceeaşi semnificaţie ca la algoritmul lui Dijkstra.h> #include<fstream.viz[N]. el este introdus la începutul unei liste. } void Init() { int i.i<=n. În momentul în care explorarea unui nod i s-a încheiat. ifstream f(„graf. f>>n.i++) //initializare matrice costuri { for(j=1. graful are n noduri şi este reprezentat prin matricea costurilor cnxn.c. În continuare. AD *x.18 În următorul algoritm s este nodul sursă.

} //i a fost complet explorat.x->inf). //pentru nodul sursa } void Relaxeaza(int u.i<=n.i++) if(!viz[i]) DF(i).int v) //relaxeaza arcul (u. } } void GOA_drum(int s) { int i. //nu s-a determinat nici-un drum de la s la v. Parcurge(). //parcurg lista vecinilor lui m[i] while(x) { Relaxeaza(m[i]. AD *x. il adaug la inceputul listei m m[k]=i. cost infinit T[i]=0.for(i=1. //nodul tata pe drumul minim de la s la v } d[s]=0. for(i=1. } k=n. T[v]=u. x=x->leg. 19 . } void Initializare(int s) //nodul sursa { int i.i<=n. m[i]=0.i++) //pentru fiecare nod in ordinea data de sortarea topologica { x=L[m[i]]->leg. //lista vecinilor lui i viz[i]=1. //realizeaza sortarea topologica in vectorul m Initializare(s). for(i=1.i<=n.i<=n. for(i=1.i++) { viz[i]=0. } void DF(int i) { AD *x=L[i]->leg.i++) //pentru fiecare nod din graf { d[i]=INF. //m contine nodurile in ordine topologica } void Parcurge() { int i. k--. Init(). while(x) { if(!viz[x->inf]) DF(x->inf).v) { if(d[v]>d[u]+c[u][v]) { d[v]=d[u]+c[u][v].

x2.. pot fi reţinute şi nodurile care formează drumurile.U) cu X={x1.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). } 20 Algoritmul Floyd-Warshall Enunt: Fiind dat un graf orientat G=(X. cout<<"\n". } else cout<<"nu exista drum de la “<<s<<” la ”<<m[i]. } void main() { int i. drum(m[i]). } else cout<<"%d ". Se porneşte de la matricea costurilor ataşată grafului. Elementul .n pentru i=1.s. cin>.s). } } } void drum(int i) { if(i!=s) { drum(T[i]). Vom folosi o matrice D=(dij)nxn ale cărei elemente dij sunt mulţimi de noduri.xn} şi o funcţie de cost l:U R+ să se determine pentru fiecare pereche de noduri xi.n pentru j=1. cout<<"nodul sursa: ".cik+ckj) sf_pentru sf_pentru sf_pentru Simultan cu determinarea lungimilor minime ale drumurilor. for(i=1. Algoritmul este : pentru k=1. GOA_drum(s).i<=n.i).n cij=min(cij. citire(). Algoritmul Floyd-Warhall determină lungimile minime ale drumurilor între oricare două noduri ale grafului într-o matrice C=(cij)nxn unde : cij= lungimea drumului minim de la xi la xj dacă există drum de la xi la xj 0 dacă i=j ∞ dacă nu există drum de la xi la xj Determinarea matricii C este asemănătoare algoritmului Roy-Warshall pentru obţinerea matricii drumurilor.x=x->leg. cout<<"%d ".….i++) if(m[i]!=s) if(d[i]<INF) { cout<<"de la “<<s<<”la “<<m[i]<<” cost “<<d[m[i]]<<” drum: “.

j<=n. //initial matricea costurilor. Programul următor implementează algoritmul în varianta de mai sus. C[i][i]=0.j.dacă cij<cik+ckj .i<=n. ifstream f(„graf.in”). //pe linia d[i.n pentru j=1.dacă cij=cik+ckj (înseamnă că am găsit noi posibilităţi de construire a drumului minim de la xi la xj folosind nodul k) se adaugă la dij nodurile din dkj . for(i=1.n daca cij>cik+ckj atunci cij=cik+ckj dij=k sf_daca sf_pentru sf_pentru sf_pentru Complexitatea algoritmului este de ordinul O(n3). Observaţie: Dacă ne interesează doar câte un drum pentru fiecare pereche de noduri xi.xj vom considera în locul matricii D o matrice D’ tot nxn astfel încât d’ij să reţină un nod ce-l poate precede pe xj în drumul minim de la xi la xj. Odată cu iniţializarea matricii C cu matricea costurilor.i++) { for(j=1.m.x2. void INITC() //initializeaza matricea costurilor C { int i.j++) C[i][j]=INF.dacă cij>cik+ckj se iniţializează dij cu dkj În final reconstituirea drumurilor minime între oricare două vârfuri xi. } . f>>n>>m. vom iniţializa şi matricea D astfel: {xi} dacă cij<∞ ∅ şi i≠ j dij= dacă cij=∞ sau i=j Pe măsură ce se actualizează matricea C vom actualiza şi matricea D după cum urmează: . Procedeul continuă până ajungem la nodul xi. algoritmul Floyd-Warshall este următorul: pentru k=1.apoi a costurilor drumurilor int D[N][N].x1.j]=nodul care poate precede nodul j // pe drumul minim de la nodul i la nodul j int n.21 dij va reprezenta în final mulţimea nodurilor ce pot precede pe xj în drumul minim de la xi la xj.n pentru i=1.h> #include<fstream. precedentul acestuia va fi orice nod din mulţimea diq. #include<iostream.xj se face pornind din xj astfel: precedentul lui xj îl alegem din mulţimea dij având unul din aceste noduri fixat (să-l numim xq).h> #define N 50 #define INF 1<<15 int C[N][N].cost. atunci dij rămâne neschimbat . În acest caz.

j. } } } void main() { int i.j).int j) //afiseaza un drum de la i la j pornind de la nodul j { if(j) { Drum(i.i<=n. C[x1][x2]=cost.i++) for(j=1. 22 Algoritmul Floyd-Warshall pentru drumuri de cost maxim . } AFISARE(). for(i=1.j++) { if(C[i][j]==INF) cout<<"\nnu exista drum intre “<<i<<” si “<<j<<endl.i++) for(j=1. } for(i=1.D[i][j]). cout<<"\tsuccesiunea de varfuri este: ".j<=n. INITC().i<=m. cout<<i<<” ”. cout<<j<<” ”. Drum(i.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. } f.close().k++) for(i=1.i<=n.k.k<=n.j.i++) { f>>x1>>x2>>cost.j<=n. else if(i!=j) { cout<<"\ncost drum de la “<<i<<” la “<<j<<”este ”<<C[i][j].} void Drum(int i. for(k=1. } } void AFISARE() //afisarea rezultatelor { int i.

x2.n (k≠ j) daca cij<cik+ckj atunci cij cik+ckj dij dkj altfel daca cij=cik+ckj atunci dij dij∪ dkj sf_daca sf_daca sf_pentru sf_pentru sf_pentru În matricea C vom avea în final lungimile drumurilor maxime între oricare două noduri. Programul pentru determinarea tuturor drumurilor maxime între oricare două vârfuri ale unui graf G=(X.xj (i≠ j).23 Fie G=(X. Desemnându-se un nod x. n (k≠ i) pentru j=1.xn) şi l:U R+ funcţia de cost ataşată grafului. Ne punem problema determinării drumurilor de lungime maximă în acest graf.xj) cij= 0 -∞ dacă dacă dacă (xi.xj)∈ U i=j (xi.U) este analog celui de la problema anterioară.xj)∉ U Algoritmul Floyd-Warshall de determinare a drumurilor minime poate fi adaptat cu mici modificări pentru determinarea drumurilor maxime între oricare două noduri xi. .adaptat noilor cerinţe.n pentru i=1. se cere determinarea drumurilor de cost maxim de la toate celelalte noduri la el. Pentru a reţine nodurile prin care trec aceste drumuri maxime vom folosi matricea D=(dij)nxn în care: {xi} dacă cij>-∞ şi i≠ j ∅ dacă cij=-∞ sau i=j dij= Matricile C şi D se modifică conform următorului algoritm: pentru k=1. fără circuite şi o funcţie de cost a:U→ R..U) un graf orientat fără circuite cu X=(x1. se foloseşte acelaşi algoritm . iar în dij vom avea mulţimea nodurilor ce pot precede pe xj într-un drum maxim de la xi la xj.... Algoritmul Bellman-Kalaba Enunţ: Se consideră un graf orientat G=(X.U) conex. drumuri care includ un număr impus de arce (paşi). adică prin matricea C=(cij)nxn cu l(xi. Pentru rezolvarea acestei probleme vom considera graful reprezentat prin matricea costurilor în forma b).

.k-1] x Generalizând.j]│j∈ X). matricea c va conţine pe prima coloană costul arcelor spre nodul x. Complexitatea algoritmului este O(n3). i a[i. Iniţial.24 Pe baza matricii costurilor grafului considerat. Numerele de pe fiecare linie a matricei c vor forma un şir monoton. algoritmul construieşte o nouă matrice c în care pe coloana j (j≤n) se vor afla costurile maxime ale drumurilor ce ajung în x folosind j arce. Astfel c[i.j] j c[i. . A doua iteraţie va determina costurile maxime ale drumurilor ce ajung în x şi conţin două arce. deci drumuri de lungime 1. Se va identifica valoarea maximă dintre acestea: c[i.2]=max(c[j.2] vom adăuga pe rând arcele ce pleacă din i către drumurile de lungime 1 anterior determinate. Ele se vor memora în a doua coloană şi se vor determina pe baza valorilor din coloana anterioară. pentru a determina costul maxim al drumului de la i la x folosind k arce se calculează: max(c[j.1]+a[i. Algoritmul se încheie în momenul în care valorile situate pe două coloane consecutive sunt identice.j] reprezintă costul maxim al unui drum de la i la x şi trece prin j arce.k-1]+a[i. Astfel. pentru determinarea valorii c[i. Exemplu: Considerăm graful din figură şi determinăm drumurile maxime ce ajung în nodul x=6. deci când nici un drum nu se poate maximiza.j]│j∈ X). Faptul că nu există circuite în graf ne asigură că numărul maxim de iteraţii ar putea fi egal cu n-1 (n numărul de noduri).

in”). } f.c[N][N].i<=n.n.j.close().j++) a[i][j]=-1. cin>>n.y.h> #include<fstream.j.j<=n.x.i++) for(j=1. unsigned char t[N][N]. //pentru arcele lipsa for(i=1.i++) a[i][i]=0.//arcul si costul sau a[x][y]=z. void citire() { ifstream f(„graf. Algoritmul se încheie la iteraţia 5 deoarece c(4)=c(5).i<=n. . c(1) -1 -1 -1 2 7 0 c(2) -1 8 8 2 7 0 c(3) 10 12 8 2 7 0 c(4) 14 12 8 2 7 0 c(5) 14 12 8 2 7 0 #include<iostream. while(!feof(f)) { f>>x>>y>z.x.z. //numarul de noduri //initializam matricea costurilor si matricea grafului partial for(i=1. int pasi.p1. int i.25 2 2 2 2 1 1 4 1 6 4 2 7 3 1 5 Notăm cu c(i) coloana i din matricea c a drumurilor maxime ce ajung în x=6 folosind i arce. } void initializari() { int i.h> #define N 50 int a[N][N].

cin>.j<=n. cin>.pasi). scrie(). cout<<"costul “<<c[p1][pasi]<<endl. if(y!=1) drum(t[x][y].x. } 26 .j++) { c[j][1]=a[j][x]. } void main() { citire(). kalaba(). for(i=1. t[i][k]=j. } ok=1.ok.j. cout<<"numar pasi: ". initializari(). drum(p1.i++) for(j=0. }while(!ok). cout<<"drum maxim “<<p1<<” -> “<<x<<” prin “<<pasi<<”arce\n". break. } for(j=1. cout<<x<<endl. } } void kalaba() { int i. do{ for(i=1.for(i=0.p1. } void drum(int x. cout<<"sosire: ".k. t[i][j]=0. } else cout<<"nu exista\n". cout<<"plecare: ".y-1).i++) for(j=1.i<=n.j<=n.i<=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]. } void scrie() { if(c[p1][pasi]!=-1) { while(c[p1][pasi]==c[p1][pasi-1]) pasi--. } k++.int y) { cout<<x<<” ”. k=2.j<=n.i<=n.j++) { c[i][j]=-1. cin>>pasi. if(c[j][1]!=-1) t[j][1]=j.i++) if(c[i][k-1]!=c[i][k]) { ok=0.

Dându-se o cameră de plecare (xi.cum avem n*n camere. Caracterul „o” semnifică o cameră liberă..j<n. adică * 1. int i.k]+A[k.yi) şi o cameră de sosire (xf. Matricea este dată în fişierul lab. deasupra sau dedesubtul camerei curente.nod_f]=A[nod_i.reciproc. dacă avem cameră ocupată. atunci nodul k se află pe drumul optim căutat.se iniţializează matricea A cu o valoare foarte mari (infinit) .2. #include<iostream. dacă avem cameră liberă. } .y=(i-1)%n+1) din labirint (relaţii obţinute prin liniarizarea matricii L) .20.. void citire() { ifstream f("lab.i++) { fgets(s.A[N*N][N*N].j]= 0.. atunci în graf cele două noduri vor fi adiacente. la generarea matricii costurilor ne vom referi la configuraţia labiruntului astfel: nodul i din graf corespunde camerei (x=(i-1)/n+1. Dacă două camere sunt libere şi învecinate. iar caracterul „ *” o cameră ocupată. Pentru a afişa efectiv camerele de pe drumul minim determinat de la nodul sursa nod_i la destinaţia nod_f vom folosi strategia Divide et Impera.h> #define N 15 #define INF 10000 short int L[N][N].in".j.nod_f).nod_f].k) şi (k. Complexitatea acestei metode este dată de complexitatea algoritmului Floyd-Warhall adică este O((n*n)3) .j++) if(s[j]=='*') L[i][j+1]=0. deci vom genera drumurile (nod_i. să se determine cel mai scurt drum de la camera de plecare până la camera de sosire ştiind că dintr-o cameră liberă ne putem deplasa într-o altă cameră vecină liberă aflată la stânga.h> #include<fstream. nodul reprezentat de (x. considerând că dacă avem un nod intermediar k şi A[nod_i. Vom memora graful prin matricea costurilor A în care costul unei muchii este 1. for(j=0. inacceptabilă pentru valori n chiar relativ mici. f>>n.f). evident neocupate. Pentru a determina cel mai scurt drum între camera de plecare şi cea de sosire vom folosi algoritmul Floyd-Warshall.i<=n.y) este elementul p=n*(x-1)+y din matricea L. ele fiind numerotate în ordine 1. for(i=1. Citirea labirintului din fişier se face prin crearea unei matrici L astfel: L[i. fgetc(f).in.n*n. Programul următor implementează metoda descrisă mai sus. prima linie a acestuia conţinând dimensiunea n a labirintului.yf). la dreapta. char s[20]. else L[i][j+1]=1. În acest fel se verifică toate camerele libere vecine şi se completează matricea costurilor.27 Drumuri minime în labirint Enunţ: Se consideră un labirint codificat sub forma unei matrici de n*n caractere reprezentând camerele labirintului. Pe baza matricii L şi a regulilor de vecinătate din enunţ vom construi matricea costurilor A de dimensiune n*n astfel: .n. adică o Vom ataşa acestei probleme un graf neorientat cu n*n noduri reprezentând camerele labiruntului.

while(k<=n*n && !gata) { if(x!=k && y!=k && A[x][y]==A[x][k]+A[k][y]) { drum(x. cout<<"\n". for(k=1. } if(!gata) cout<<y<<” ”. } A[i][i]=0. } k++.i++) { x=(i-1)/n+1. gata=1.j. for(i=0.x.int y) { int k=1.j++) A[i][j]=INF. } void afisare_drum(int x.k++) for(i=1. } void drum(int x.i<=n*n. } } void Floyd_Warhall() { int i.j<=n*n. } 28 .j++) if(A[i][j]>A[i][k]+A[k][j]) A[i][j]=A[i][k]+A[k][j].j++) cout<<L[i][j]<<”\t”. drum(k. for(i=1.k).} void construieste_graf() { int i.gata=0.k<=n*n.i<=n+1.i++) for(j=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.i++) //bordam matricea L cu 0 L[0][i]=L[i][0]=L[i][n+1]=L[n+1][i]=0.j<=n*n.i<=n.y.int y) { if(A[x][y]<INF) { f.j<=n. if(L[x][y+1]==1) //la dreapta A[n*(x-1)+y+1][i]=A[i][n*(x-1)+y+1]=1.i++) { for(j=1.k.j. for(i=1.i++) //initializam matricea costurilor for(j=1.y). 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.i<=n*n. y=(i-1)%n+1.close(). for(i=1.i<=n*n.

C=new short int [n*n+1]. #include<iostream.i<=n*n. int i. for(i=1.A[x][y].DMIN { int i.f).*DMIN. short int *C.xf. short int L[N][N]. nod_f=n*(xf-1)+yf. cout<<"punctul de plecare (x.nod_f.VIZ.getc(f). cout<<"\tLabirintul este:\n". cin>>xf>>yf. nod *leg. for(j=0. AD *LA[N*N].j++) if(s[j]=='*') L[i][j+1]=0. puts(s). cout<<"\n". } Observaţie: Complexitatea algoritmul devine O(n2) dacă în loc de algoritmul Floyd-Warshall folosim o parcurgere BF a grafului reprezentat prin liste de adiacenşă şi ataşăm nodurilor un vector de marcare (algoritmul lui Lee). //initializam vectorul de vizitare } else cout<<"\nNu exista drum\n".i++) { f.close(). citire(). construieste_graf().yf. char s[20].y)=". Tablourile utilizate de parcurgerea BF sunt deasemenea alocate dinamic în heap pentru a mări spaţiul de memorie disponibil. } void Init() //aloca spatiu pentru tablourile C.nod_i.n.i++) VIZ[i]=0. }AD. cin>>n.*T. T=new short int [n*n+1].20. f.T. cout<<"punctul de sosire (x.i<=n. cout<<"prin celulele: “x. nod_i=n*(xi-1)+yi. for(i=1. Floyd_Warhall().y).y)=").j<n. else L[i][j+1]=1.j. 29 . VIZ=new short int [n*n+1].yi.in"). cout<<"\nlungime=”<.gets(s. Programul următor determină drumul minim între camera de plecare nod_i şi camera de sosire nod_f folosind pentru graful asociat listele de adiacenţă alocate dinamic.h> #include<fstream. } f.} void main() { int xi. drum(x. //tablouri alocate dinamic in heap void citire() { ifstream f(“lab. afisare_drum(nod_i.*VIZ.nod_f). cin>>xi>>yi.h> #define N 50 typedef struct nod{ short int inf.

} } } } void BF(int nod. for(i=1. p->leg=LA[i]->leg. T[nod]=0. p->inf=n*(x-1)+y+1. LA[n*x+y]->leg=p. } if(L[x][y+1]==1) //vecin la dreapta { p=new AD. p->inf=n*(x-1)+y-1. LA[i]->leg=p. p->leg=LA[i]->leg. if(L[x][y]==1) { if(L[x][y-1]==1) //vecin la stanga { p=new AD. p->leg=LA[n*(x-2)+y]->leg.int s) { AD *x. p->leg=LA[i]->leg.x. p=new AD. p=new AD. p=new AD. p->inf=i. VIZ[nod]=1. p->leg=LA[n*(x-1)+y+1]->leg. p->leg=LA[n*x+y]->leg. p->leg=LA[n*(x-1)+y-1]->leg. LA[n*(x-1)+y-1]->leg=p.DMIN=new short int [n*n+1]. DMIN[nod]=0. p->inf=n*(x-2)+y. LA[i]->leg=p.i<=n*n.i++) { LA[i]=new AD. int prim. } if(L[x-1][y]==1) //vecin deasupra { p=new AD. LA[i]->leg=p.i++) { x=(i-1)/n+1. } for(i=0. C[prim]=nod. prim=ultim=1. LA[i]->leg=0.ultim. p->inf=i. p->inf=i. LA[n*(x-1)+y+1]->leg=p. p->inf=i. } void construieste_graf() { int i. //extrag nodul din varful cozii 30 .i++) //bordam matricea L cu 0 L[0][i]=L[i][0]=L[i][n+1]=L[n+1][i]=0. for(i=1. p->leg=LA[i]->leg. LA[n*(x-2)+y]->leg=p.i<=n*n. while(prim<=ultim) { nod=C[prim]. p=new AD. AD *p. p->inf=n*x+y. LA[i]->leg=p.i<=n+1. y=(i-1)%n+1. } if(L[x+1][y]==1) //vecin dedesubt { p=new AD.y.

nod_i=n*(xi-1)+yi.int nod) { if(i!=nod) drum(T[i]. construieste_graf(). if(x->inf==s) return.. BF(nod_i.nod). cin>>xf>>yf. citire(). //am ajuns in punctul de sosire. cout<<"punctul de sosire (x. } void main() { int xi.xf.y)=". //parcurg BF graful din nodul de plecare if(DMIN[nod_f]) { cout<<"\nlungime=”<< DMIN[nod_f]<<”este ”<<drum(nod_f.y)=". //adaug x->inf in coada } x=x->leg. nod_f=n*(xf-1)+yf. Init(). cout<<"\n".nod_f). //avansez la urmatorul nod din coada 31 .} } void drum(int i.yi.nod_i)<<” \ndrum prin nodurile: ". cout<<i<<” ”. VIZ[x->inf]=1. //lista vecinilor lui nod while(x) { if(!VIZ[x->inf]) { T[x->inf]=nod. DMIN[x->inf]=DMIN[nod]+1. opresc parcurgerea C[++ultim]=x->inf. cout<<"punctul de plecare (x. } else cout<<"\nNu exista drum intre camera “<<nod_i<<“ si camera “<<nod_f<<endl. } prim++.nod_i. cin>>xi>>yi.yf. } x=LA[nod]->leg.nod_f.

Liana Bejan Ienulescu. Leiserson. Rivest. Viorica Patrascu.Culegere de probleme pentru clasele IX-XI. Cormen. Radu Boriga. Dan Pracsiu. Charles E. Informatica . 2001 3.Manual pentru clasa a XI-a. 2000 2. Carpen Manuela. Ronald R. Editura Agora. Thomas H. Dana Lica.32 Bibliografie 1. manuscris . Informatica Manual pentru clasa a XI-a. Editura Niculescu 2003 4. Fundamentele programarii. Editura L&S Soft 2002 5. Suport de curs si lucrari de laborator . Doru Popescu Anastasiu. Daniela Oprescu. Pavel Florin Moraru. Introducere in algoritmi. George Daniel Mateescu. Editura Niculescu.

Sign up to vote on this title
UsefulNot useful