Sunteți pe pagina 1din 16

Informatică clasa a XI-a Teorie 1

Cap.6 Metoda Greedy

6.1 Mecanism general

Este o metodă generală de elaborare a algoritmilor. În esenţă ea se aplică problemelor în care se dă o


mulţime A conţinând n date de intrare cerându-se să se determine o submulţime B a sa care să îndeplinească
anumite condiţii pentru a fi acceptată. Cum în general există mai multe astfel de mulţimi, se dă şi un criteriu
conform căruia, dintre submulţimile acceptabile (numite soluţii posibile), se alege una singură (numită soluţie
optimă) ca rezultat final.
Soluţiile posibile au următoarea proprietate: dacă submulţimea B  A este o soluţie posibilă şi C  B,
atunci şi C este o soluţie posibilă. Vom presupune că mulţimea vidă este întotdeauna o soluţie posibilă.
Metoda Greedy tratează acest tip de probleme în două moduri care urmează aceeaşi metodă dar diferă
doar prin ordinea de efectuare a unor operaţii:

a) Se pleacă de la soluţia vidă. Se alege pe rând, într-un anumit fel, un element din A neales la paşii
precedenţi. Dacă adăugarea lui la soluţia parţială anterior construită conduce la o soluţie posibilă,
construim noua soluţie prin adăugarea elementului ales. Secvenţa de algoritm care descrie această
variantă este:

algoritm Greedy1(A,n,B)
BØ
pentru i1.n executa
Alege(A,i,x)
Posibil(B,x,v)
daca v=1 atunci Adauga(B,x)
sf_daca
sf_pentru
sf_algoritm

Funcţia Alege furnizează în mod dinamic elementul x=aj  {ai,.......an}şi efectuează interschimbarea
elementelor ai şi aj. Funcţia Posibil furnizează v=1 dacă B  {x} este o soluţie posibilă şi v=0 în caz
contrar. Funcţia Adauga înlocuieşte pe B cu B  {x}.

b) Lucrurile decurg ca mai sus cu excepţia faptului că se stabileşte de la început (şi nu în mod dinamic)
ordinea în care trebuie considerate elementele mulţimii A. Algoritmul corespunzător este:

algoritm Greedy2(A,n,B)
BØ
Prel)A)
pentru i1.n executa
Posibil(B,ai,v)
daca v=1 atunci Adauga(B,ai)
sf_daca
sf_pentru
sf_algoritm

Funcţia Prel efectuează o prelucrare a elementelor mulţimii A conform unui anumit criteriu.

Metoda Greedy nu caută să determine toate soluţiile posibile şi să aleagă pe cea optimă conform
criteriului de optimizare dat, ci constă în a alege pe rând câte un element urmând să-l introducă eventual în
soluţia optimă; de aici provine numele Greedy (lacom).
Un exemplu tipic îl constituie problemele de optimizare. Astfel, dacă trebuie determinat maximul unei
funcţii de cost depinzând de parametrii a1,.....,an ideea generală a metodei este de a alege la fiecare pas
acel element care să facă să crească cât mai mult valoarea acestei funcţii.
Exemplu: Se dă o mulţime X={x1,x2,.....,xn} cu elemente reale. Să se determine submulţimea cu suma
elementelor maximă.
Se observă uşor că dacă o submulţime Y conţine un element y0≤0, atunci suma submulţimii Y-{y0} ≥
decât suma elementelor din submulţimea Y. Deci soluţia optimă va avea toate elemetele pozitive. Funcţia
Alege furnizează x=ai, funcţia Posibil returnează v=1 dacă ai>0 şi v=0 în caz contrar. Algoritmul este:
Informatică clasa a XI-a Teorie 2

algoritm SUBM(X,Y,n,k)
float X[n],Y[n]
k0
pentru i1,n executa
daca xi>0 atunci kk+1; ykxi
sf_daca
sf_pentru

Observaţie: Metoda Greedy nu furnizează soluţia optimă pentru orice problemă. În acest caz trebuie să
cunoaştem măcar în ce măsură suntem apropiaţi de soluţia optimă.

6.2 Exemple demonstrative

1. Problema continuă a rucsacului

Enunţ: O persoană are un rucsac cu care poate transporta o greutate maximă G. Persoana are la dispoziţie n
obiecte şi cunoaşte pentru fiecare obiect greutatea şi câştigul care se obţine în urma transportării lui la
destinaţie. Obiectele pot fi tăiate în bucăţi. Să se precizeze ce obiecte şi în ce proporţie trebuie transportate
astfel încât câştigul total obţinut să fie maxim.

Rezolvare: Algoritmul bazat pe metoda Greedy este următorul:


 se calculează pentru fiecare obiect în parte eficienţa de transport (câştigul obţinut prin transportul
unei unităţi de greutate din obiectul respectiv).
 obiectele se sortează în ordinea descrescătoare a eficienţei de transport şi se preiau în calcul în
această ordine
 câştigul iniţial va fi 0, iar greutatea rămasă de încărcat va fi greutatea G a rucsacului
 atât timp cât nu a fost completată greutatea maximă a rucsacului şi nu au fost luate în considerare
toate obiectele se procedează astfel: dintre obiectele neîncărcate se selectează acele cu cea mai
mare eficienţă de transport şi avem două posibilităţi:
a) acesta încape în totalitate în rucsac, deci se scade din greutatea de încărcat greutatea
obiectului, la câştig se accumuleazăcâştigul obţinut prin transportarea acelui obiect şi se
tipăreşte 1, în sensul că întregul obiect a fost încărcat
b) obiectul nu încape în totalitate în rucsac, caz în care se calculează ce parte din el se poate
transporta, se accumulează câştigul obţinut, se tipăreşte procentul care s-a încărcat din obiect,
greutatea rămasă de încărcat devine 0 şi algoritmul se încheie
Exemplu: greutatea rucsacului este G=3 şi avem la dispoziţie 3 obiecte:

Câştig 2 4 6
Greutate 2 1 3
Eficienţă(câştig/greutate) 1 4 2

Se sortează obiectele descrescător după eficienţă şi se obţine ordinea 2 2 1.


Obiectul 2 se încarcă în întregime: G3-1=2, castig0+4=4
Din obiectul 3 se încarcă 2/3: G2-(2/3)*3=0 (şi algoritmul se va încheia), castig4+(2/3)*6=8
Câştigul total este 8.

Observăm că metoda Greedy se remarcă în alegerea obiectului care va fi transportat (în ordinea
descrescătoare a eficienţei de transport), alegere asupra căreia nu se revine.

2. Arborele parţial de cost minim

Definiţie: Se numeşte arbore un graf neorientat, conex şi fără cicluri.

În figura alăturată este desenat un arbore. Se observă că oricum am elimina o muchie, graful îşi pierde
proprietatea de conexitate, şi oriunde am adăuga o muchie, apare un ciclu. Acest lucru este valabil în orice
arbore.
Informatică clasa a XI-a Teorie 3

Teoremă: Fie un graf neorientat G=(X,U).


Următoarele afirmaţii sunt echivalente:
1) G este un arbore
2) G este un graf conex, minimal cu această
proprietate (eliminând o muchie oarecare
se obţine un graf neconex)
3) G este un graf fără cicluri, maximal cu
această proprietate (dacă se adaugă o
muchie se obţine un graf care are măcar
un ciclu)

Definiţie: Fie graful G=(X,U) cu n2 noduri, m muchii şi p componente conexe. Numărul (G)=m-n+p se
numeşte numărul ciclomatic al grafului G.

Observaţii:
- se poate arăta că (G) furnizează numărul de cicluri elementare din graf
- numărul (G) reprezintă numărul de muchii ce trebuie înlăturate din graf astfel încât acesta să nu
conţină cicluri

Definiţie: Fie G un graf neorientat. Un graf parţial H al lui G, cu proprietatea că H este arbore, se numeşte
arbore parţial al lui G.

Corolar: Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex.

Propoziţii:
1) Un arbore cu n2 vârfuri conţine cel puţin două vârfuri terminale.
2) Orice arbore cu n vârfuri are n-1 muchii.
3) Un graf G cu n vârfuri şi m muchii este arbore dacă este aciclic şi m=n-1.
4) Un graf G cu n vârfuri şi m muchii este arbore dacă este conex şi m=n-1.
5) Un graf cu n vârfuri şi cel puţin n muchii conţine cel puţin un ciclu.
6) Numerele d1d2d3.......dn1 sunt gradele nodurilor unui arbore cu n2 noduri dacă şi numai
dacă d1+d2+....+dn=2(n-1).

Definiţie: Un graf neorientat care nu conţine cicluri se numeşte pădure.

În practică se întâlnesc foarte des probleme de tipul următor: se doreşte conectarea unor consumatori
la o sursă de energie electrică astfel încât costul branşării să fie minim. Transpunând problema în termenii
teoriei grafurilor, se cere de fapt determinarea unui arbore parţial de cost minim, adică un arbore care are
proprietatea că suma costurilor muchiilor sale să fie minimă.

Definiţie: Suma costurilor muchiilor unui graf se numeşte costul grafului. Dacă se defineşte funcţia c:UR+
care asociază fiecărei muchii un număr real numit cost, costul grafului este

c(G)   c(u )
uU
Funcţia c se numeşte funcţia cost.
Fie G=(X,U) un graf conex, reprezentat prin matricea costurilor. Se ştie că prin eliminarea uneia sau a
mai multor muchii se obţine un graf parţial. Dacă graful parţial al unui graf G conex este arbore, acesta se
numeşte arbore parţial al lui G. Dacă dintr-un graf conex G=(X,U) se elimină muchii astfel încât să se
obţină un arbore parţial al cărui cost să fie minim, acesta se numeşte arbore parţial de cost
minim(APM).

Proprietate: Pentru graful G conex, cu funcţia de cost c, există un graf parţial H conex şi de cost minim, care
este şi arbore.
Pentru determinarea APM sunt cunoscuţi mai mulţi algoritmi, cei mai utilizaţi fiind algoritmul lui Kruskal
(1956) şi algoritmul lui Prim, ambii bazaţi pe o strategie Greedy.
Informatică clasa a XI-a Teorie 4

Algoritmul lui Kruskal

Se pleacă iniţial de la n arbori disjuncţi, fiecare fiind format dintr-un nod al grafului : H1, H2,…….,Hn. La
fiecare pas vom încerca să unificăm doi dintre arborii existenţi, alegerea celor doi arbori ce vor fi unificaţi
făcându-se astfel: dintre toate muchiile nealese încă, se selectează muchia de cost minim care are o
extremitate într-un arbore şi cealaltă extremitate în alt arbore. Prin adăugarea acestei muchii la arborele care se
construieşte, se vor unifica cei doi arbori disjuncţi într-unul singur. După n-1 paşi se obţine APM care conţine n-
1 muchii.
Algoritmul pseudocod corespunzator este:

algoritm Kruskal(G,U)
A
pentru fiecare varf v X(G) executa
Formeaza_Multime(v)
sfarsit pentru
sorteaza muchiile din u in ordinea crescatoare a costului c
pentru fiecare muchie (u,v)U(G),in ordinea crescatoare a costului
executa
daca Gaseste_Multime(u)Gaseste_Multime(v) atunci
AA  {(u,v)}
Uneste(u,v)
sfarsit daca
sfarsit pentru
returneaza A

Algoritmul foloseşte o structură de date pentru mulţimi disjuncte pentru reprezentarea mai multor mulţimi de
elemente disjuncte. Fiecare mulţime conţine vârfurile unui arbore din pădurea curentă. Funcţia
Gaseste_Multime(u) returnează un element reprezentativ din mulţimea care îl conţine pe u. Astfel, putem
determina dacă două vârfuri u şi v aparţin aceluiaşi arbore testând dacă Gaseste_Multime(u) este egal cu
Gaseste_Multime(v). Combinarea arborilor este realizată de funcţia Uneste(u,v).

Pentru a implementa algoritmul lui Kruskal vom reprezenta graful prin vectorul muchiilor, fiecare muchie
având ataşat şi costul. Pentru a specifica la fiecare pas din ce arbore face parte un nod i, vom folosi vectorul L.
Iniţial, pornim cu n arbori disjuncţi, deci L[i]=i , pentru i{1,2,….n}. Pentru a eficientiza alegerea
muchiilor, vectorul muchiilor u se va ordona crescător după costul muchiilor. Pentru a alege cele n-1 muchii ale
APM se parcurg elementele vectorului u astfel:
Fie v=[u[i].x,u[i].y] muchia ce urmează a fi analizată (plecăm cu i=1). Dacă în vectorul L
extremităţile muchiilor au valori egale, adică L[u[i].x]=L[u[i].y], atunci cele două extremităţi aparţin
aceluiaşi subarbore, deci alegerea acestei muchii ar duce la formarea unui ciclu. În caz contrar, înseamnă că
extremităţile muchiei fac parte din subarbori diferiţi, fie aceaştia Hx şi Hy, care pot fi unificaţi, operaţie care
presupune că pentru toate vârfurile celor doi arbori trebuie să apară în vectorul L aceeaşi valoare (provenită din
primul sau din al doilea arbore). Acest pas se repetă până am reuşit să alegem cele n-1 muchii ale APM.

Exemplu: Fie graful din figura următoare:


Informatică clasa a XI-a Teorie 5

Pasii algoritmului sunt:

Configuratia initiala:

După ordonarea crescătoare a muchiilor


după costul c ataşat, avem:
u=([5,4],[3,4],[3,5],[1,2],
[2,5],[2,3],[6,5],[2,6],[2,7],
[1,7],[7,6])
c=(1,1,2,2,2,3,3,3,3,4,5)
L=(1,2,3,4,5,6,7) , fiecare nod
făcând parte dintr-un arbore.

Pasul 1:

Se alege muchia de cost minim [5,4] şi L


devine: (1,2,3,5,5,6,7) deoarece am
unificat arborii H4 şi H5 şi am obţinut
subarborele H5=(4,5). Ceilalţi subarbori
rămân nemodificaţi.

Pasul 2:
Se alege muchia [3,4] de
cost minim, unificăm H3 şi H5 şi obţinem
L=(1,2,3,3,3,6,7) şi H3=(3,4,5).
Informatică clasa a XI-a Teorie 6

Pasul 3:
Se verifică muchia [3,5] care nu poate fi
aleasă deoarece L[3]=L[5] (vârfurile 3 şi 5 aparţin
deja aceluiaşi subarbore H3) şi alegerea ei ar provoca
apariţia unui ciclu.
Se alege muchia [1,2], se unifică H1 şi H2 şi
obţinem: L=(1,1,3,3,3,6,7) şi H1=(1,2).

Pasul 4:
Se alege muchia [2,5], se unifică H1 şi H3 şi
obţinem: L=(1,1,1,1,1,6,7) şi
H1=(1,2,3,4,5).

Pasul 5:

Muchia [2,3] nu poate fi aleasă pentru că ar crea un


ciclu.
Se alege muchia [6,5] şi se obţine subarborele
H1=(1,2,3,4,5,6).
Informatică clasa a XI-a Teorie 7

Pasul 6:
Se alege muchia [2,7] si L devine
(2,2,2,2,2,2,2), ceea ce corespunde unui
arbore parţial de cost minim H=([1,2], [2,5],
[3,4], [5,4], [6,5], [2,7]), costul total
fiind 12.

Nu are importanţă care subarbore se alege pentru a transfera vârfurile celuilalt subarbore.

Observaţie: Dacă există mai multe muchii cu acelaşi cost, pentru un graf conex G pot exista mai multe
posibilităţi de alegere a unei muchii de cost minim, deci pot exista mai mulţi APM. Aceşti arbori se vor deosebi
prin muchiile ce vor fi luate în consideraţie, şi nu prin costul asociat, deoarece aceast cost va fi acelaşi pentru
toţi arborii, şi anume ei vor avea cost minim.

Implementarea algoritmului lui Kruskal este:

#include<iostream>
#include<fstream>
#define N 30
#define M 60
typedef struct {
int x,y;
float cost;
}MUCHIE;
void citire_muchii(MUCHIE u[M],int &n,int &m)
{
int k;
ifstream f(“graf.in”);
f>>n>>m;
for(k=1;k<=m;k++)
f>>u[k].x>>u[k].y>>u[k].cost;
f.close();
}
void sortare(MUCHIE u[M],int m)
{
MUCHIE aux; int i,j;
for(i=1;i<=m-1;i++)
for(j=i+1;j<=m;j++)
if(u[i].cost>u[j].cost)
{
aux=u[i]; u[i]=u[j]; u[j]=aux;
}
}
void creare_apm(MUCHIE u[M],int n,int m)
{
int L[N],k,i,v,w,j;
float ct=0; //cost total apm
for(i=1;i<=n;i++) L[i]=i; //n subarbori disjuncti
i=1; k=0;
cout<<"\n\nAPM: ";
while(k<n-1)
Informatică clasa a XI-a Teorie 8

{
if(L[u[i].x]!=L[u[i].y])
//extremitatile muchiei sunt in subarbori diferiti
{
k++;
ct+=u[i].cost; //adaugam costul muchiei i
cout<<"[“<<u[i].x<<”][”<<u[i].y<<”]=”;
v=L[u[i].y]; w=L[u[i].x];
for(j=1;j<=n;j++)
if(L[j]==v) L[j]=w; //reunim cei doi arbori
}
i++; //trecem la urmatoarea muchie
}
Cout<<"\n\ncostul total este “<<ct<<endl;
}
void main()
{
MUCHIE u[M]; int n,m;
citire_muchii(u,n,m); sortare(u,m); creare_apm(u,n,m);
}

Algoritmul lui Prim

Graful conex este dat prin matricea costurilor, forma 1. La fiecare pas k>0 obţinem un arbore H1 cu k+1
vârfuri şi n-(k+1) arbori cu câte un singur vârf. La fiecare pas se alege muchia de cost minim care are o
extremitate în arborele H1 deja creat şi cealaltă extremitate liberă. Lucrăm cu vectorul T în care vom avea,
pentru fiecare vârf neales încă, vârful din H1 de care acesta se leagă printr-o muchie de cost minim (legătură de
tip tată). În componenta corespunzătoare din vectorul Q vom avea costul muchiei adăugate. Cum la început
avem în H1 doar vârful 1, în toate componentele lui T avem 1, cu excepţia lui T[1], iar în Q, valorile
corespunzătoare din linia 1 a matricei costurilor c. La pasul k, după alegerea unui nou vârf w care se adaugă la
H1, se vor actualiza componentele din T şi Q pentru vârfurile nealese încă deoarece adăugarea vârfului w poate
modifica aceste valori. Algoritmul are ordinul de complexitate (n2).
Algoritmul pseudocod corespunzător este:

algoritm Prim(G,c,rad)
QX(G)
pentru fiecare uQ executa
D[u]∞
sfarsit pentru
D[rad]0
T[rad]0
cat timp Q0 executa
uExtrage_Minim(Q)
pentru fiecare vcinilor lui u executa
daca vQ şi c(u,v)D[v] atunci
T[v]u
D[v]c(u,v)
sfarsit daca
sfarsit pentru
sfarsit cat timp

Se iniţializează vectorul Q astfel încât acesta să conţină toate vârfurile grafului şi se iniţializează câmpul D al
fiecărui vârf cu ∞, excepţie făcând rădăcina rad, al cărei câmp D este iniţializat cu 0. Tatăl rădăcinii se
iniţializează cu 0, deoarece rad nu are nici un părinte. Pe parcursul algoritmului, mulţimea X-Q conţine vârfurile
arborelui curent. Se identifică un vârf uQ incident unei muchii de cost minim care traversează tăietura (X-
Q,X) (cu excepţia primei iteraţii în care u=rad deoarece are cheia D[rad]=0). Eliminarea lui u din mulţimea Q
îl adaugă pe acesta mulţimii U-Q a vârfurilor din arbore. În liniile următoare se actualizează câmpurile D şi T ale
Informatică clasa a XI-a Teorie 9

fiecărui vârf v adiacent lui u, dar care nu se află în arbore. Actualizarea respectă condiţiile D[v]=c(v,T[v])
şi (v,T[v]) să fie o muchie de cost minim care îl uneşte pe v cu un vârf din arbore.

Fie graful din figura


următoare:

Configuraţia iniţială:

Pasul 1:

Pasul 2:
Informatică clasa a XI-a Teorie 10

Pasul 3:

Pasul 4:

Pasul 5:
Informatică clasa a XI-a Teorie 11

Pasul 6:

Implementarea algoritmului lui Prim este:

#include<iostream>
#include<fstream.h>
#define N 30
#define M 60
#define INF 1<<14
void citire_graf(int c[N][N],int &n)
{
int m,i,j,x,y,z;
ifstream f(“graf.in”);
f>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) c[i][j]=INF;
for(i=1;i<=n;i++) c[i][i]=0;
for(i=1;i<=m;i++)
{
f>>x>>y>>z;
c[x][y]=c[y][x]=z;
}
f.close();
}
void creare_apm(int c[N][N],int n)
{
int V[N],T[N],Q[N],i,j,min,w,ct=0; //costul total al arborelui
for(i=1;i<=n;i++) V[i]=i; //varfurile neincluse in arbore
V[1]=0;
for(j=2;j<=n;j++)
{
T[j]=1; Q[j]=c[1][j];
}
Cout<<"\n\nAPM: ";
while(1)
{
min=INF; //determinam muchia de cost minim
for(i=2;i<=n;i++)
if(V[i] && Q[i]<min)
{
min=Q[i]; w=i;
}
if(min==INF) break;
V[w]=0; //varful w a fost inclus in arbore
cout<<"[“<<T[w]<<”][”<<w<<”]=”; //muchia adaugata
Informatică clasa a XI-a Teorie 12

ct+=c[T[w]][w]; //actualizam costul arborelui


for(j=2;j<=n;j++)
if(V[j] && Q[j]>c[w][j])
{
T[j]=w; Q[j]=c[w][j];
}
}
Cout<<"\ncostul total este "<<ct<<endl;
}
void main()
{
int c[N][N],n;
citire_graf(c,n); creare_apm(c,n);
}

3. Drumuri minime în grafuri orientate. Algoritmul lui Dijkstra

Fie G=(X,U) şi l:UR+. Se pune problema determinării unor drumuri de lungime minimă în acest graf.
Rezolvarea ei are multiple aplicaţii practice. Considerând drept noduri diferite puncte dintr-un oraş, dacă
ponderea l(u), unde u=(xi,xj), reprezintă durata de trecere de la xi la xj, problema revine la determinarea
drumurilor de durată minimă; dacă l(u) reprezintă costul transportului de la xi la xj, problema revine la
determinarea drumurilor având costul de transport minim etc.
Pentru tratarea problemelor de minim vom asocia grafului G matricea costurilor în forma a), adică
C=(cij)nxn definită astfel:
l(xi,xj) dacă (xi,xj)U

cij= 0 dacă i=j

+ dacă (xi,xj)U

Problemă: Fiind dat un graf orientat G=(X,U), o funcţie l:UR+ şi un nod x0, să se determine pentru
toate vârfurile xi pentru care există drum de la x0 la xi, lungimea celui mai scurt drum şi unul dintre drumurile
minime de la x0 la xi.
Algoritmul utilizrează metoda Greedy generând
drumurile minime în ordinea crescătoare a lungimilor.
2 7 3
Exemplu: Pentru graful din figura alăturată,
1 1 2 1
considerând nodul de plecare 1, se vor obţine în ordine:
D1=(1,2) de lungime 1
D2=(1,2,5) de lungime 2
1 3 5 D3=(1,2,5,3) de lungime 4
4
D4=(1,2,5,3,4) de lungime 5
De la 1 la 6 nu există drum.

1 3

6
Se porneşte din x0. Evident cel mai scurt drum
de la x0 la unul din celelalte vârfuri ale grafului este dat de arcul (x0,xj) de lungime minimă. Următorul drum în
ordinea lungimilor va fi dat fie de un alt arc cu extremitatea iniţială x0, fie de un drum (x0,xj,xp). Alegem în
continuare drumuri în ordinea crescătoare a lungimilor, până când am determinat drumuri minime de la x0 către
toate vârfurile pentru care există drum pornind din x0. Pentru aceasta se consideră S mulţimea vârfurilor xjX
pentru care am găsit drum minim de la x0 la xj. Iniţial S={x0}. La fiecare pas, 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, cu xpX-S.
Pentru exemplul considerat S va avea pe rând următorul conţinut:
S={1}
S={1,2}
S={1,2,5}
Informatică clasa a XI-a Teorie 13

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 adăugăm în S la un moment dat) trece numai prin
vârfuri din S (cu excepţia lui xk). Pentru a alege nodul xkX-S ce urmează a fi adăgat în S vom folosi un vector
D=(d1,d2,...,dn) astfel încât:

lungimea drumului minim de la x0 la xi, dacă xiS


di=
lungimea drumului minim de la x0 la xi ce foloseşte numai vârfuri din
S, dacă xiS

Iniţial di=C(x0,i), ()i=1..n, adică este linia x0 din matricea costurilor. La un moment dat, adăgăm în S
nodul xk cu proprietatea că dk=min{dj | xjX-S}. După adăgarea 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 adăgat. Drumul minim de la x0 la xj ce
foloseşte noduri din S (inclusiv xk) va fi de forma (x0,…,xk,xj). Deci pentru xjX-S, dj se modifică după
adăugarea lui xk la S numai dacă dk+C(k,j)<dj, caz în care dj dk+C(k,j). În final, vectorul d va conţine
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=.
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.
Iniţial:
0 dacă i=x0 sau C(x0,i)=
Ti=
x0 dacă C(x0,i) şi ix0

La fiecare actualizare de forma dj dk+C(k,j) vom avea şi o actualizare a vectorului T de forma Tjk.
Algoritmul se încheie când S conţine toate nodurile xj pentru care există drum de la x0 la xj, deci fie când S=X
(dacă există drumuri de la x0 la toate celelalte noduri), 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}=). 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

Exemplu: Considerăm graful anterior şi punctul x0=1. Cei trei vectori au valorile iniţiale:
D=(0, 1, , , 3, )
T=(0, 1, 0, 0, 1, 0)
S=(1, 0, 0, 0, 0, 0)

1) determinăm min(D[i] | S[i]=0) => min=1,k=2 => S[2]1


Actualizăm distanţele 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 configuraţia celor trei vectori este:
D=(0, 1, 8, , 2, )
T=(0, 1, 2, 0, 2, 0)
S=(1, 1, 0, 0, 0, 0)

2) determinăm min(D[i] | S[i]=0) => min=2, k=5 => S[5]1


Actualizăm distanţele 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 configuraţia celor trei vectori este:
D=(0, 1, 4, , 2, )
T=(0, 1, 5, 0, 2, 0)
S=(1, 1, 0, 0, 1, 0)
Informatică clasa a XI-a Teorie 14

3) determinăm min(D[i] | S[i]=0) => min=4, k=3 => S[3]1


Actualizăm distanţele 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 configuraţia celor trei vectori este:
D=(0, 1, 4, 5, 2, )
T=(0, 1, 5, 3, 2, 0)
S=(1, 1, 1, 0, 1, 0)

4) determinăm min(D[i] | S[i]=0) => min=5, k=4 => S[4]1


Actualizăm distanţele la nodurile neselectate încă.
S[6]=0; D[6]=  D[4]+C[4,6]=5+, nu se poate actualiza
După al patrulea pas configuraţia celor trei vectori este:
D=(0, 1, 4, 5, 2, )
T=(0, 1, 5, 3, 2, 0)
S=(1, 1, 1, 1, 1, 0)

5) 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

Programul care implementează algoritmul lui Dijkstra este următorul:

#include<fstream.h>
#include<iostream>
#define N 20
#define INF 1<<14
void citire(int c[N][N],int &n,int &xp)
{
int i,j,x,y,z;
ifstream f(“graf.txt”);
f>>n>>xp; //numarul de noduri si nodul de plecare
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
c[i][j]=INF;
for(i=1;i<=n;i++) c[i][i]=0;
while(!feof(f))
{
f>>x>>y>>z; //arcul si costul sau
c[x][y]=z;
}
f.close();
}
void minim(int S[N],int D[N],int n,int &q)
{
int i; long m;
m=2*INF;
for(i=1;i<=n;i++)
if(!S[i]&&D[i]<m)
{
m=D[i]; q=i;
}
}
void determinare_drumuri(int c[N][N],int D[N],int T[N],int n)
{
int S[N],i,x,j,k,ok;
//initializari
for(i=1;i<=n;i++)
{
D[i]=c[xp][i]; S[i]=0;
if(c[xp][i]<INF) T[i]=xp;
else T[i]=0;
}
S[xp]=1; T[xp]=0; ok=1; x=0;
Informatică clasa a XI-a Teorie 15

do{
minim(S,T,n,k); //determina nodul k aflat la distanta minima
x++;
if(D[k]==INF||x==n)
ok=0; //nu mai pot fi construite drumuri minime
else
{
//actualizam vectorii S,T si D
S[k]=1;
for(j=1;j<=n;j++)
if(!S[j]&&D[j]>D[k]+c[k][j])
{
D[j]=D[k]+c[k][j];
T[j]=k;
}
}
}while(ok);
}
void drum(int D[N],int T[N],int i)
{
if(i)
{
drum(D,T,T[i]);
cout<<i<<” ”;
}
}
void afisare_drumuri(int D[N],int T[N],int n)
{
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(D,T,i); cout<<"\n";
cout<<"\tLungimea drumului este “<<D[i]<<endl;
}
}
void main()
{
int c[N][N],D[N],T[N],n,xp;
citire(c,n,xp); determinare_drumuri(c,D,T,n);
afisare_drumuri(D,T,n);
}

Datorită selectării minimelor, complexitatea algoritmului este O(n2).

4. Ciclul hamiltonian de cost minim (problema comis-voiajorului)

Fie G=(X,U) un graf neorientat complet ponderat. Să se determine un ciclu hamiltonian care să aibă costul
total minim.

Pentru această problemă se cunosc algoritmi care necesită un timp exponenţial (exemplu metoda
backtracking). Din acest motiv se renunţă la condiţia de optimalitate şi se caută un ciclu care să treacă prin
toate oraşele cu un cost pe cât posibil mai mic.

Paşii algoritmului:
1. Se citeşte matricea costurilor A şi nodul de pornire v1
2. Dacă v1,v2,........,vk este un lanţ deja ales, după caz se procedează astfel:
Informatică clasa a XI-a Teorie 16

a) dacă k=n se alege muchia (vk,v1)


b) dacă k≠n, se alege muchia de cost minim care are o extremitate în vk iar cealaltă extremitate
în mulţimea nodurilor neselectate încă
Odată cu selectarea unei muchii se selectează un nou nod şi nu se revine asupra acestei alegeri. Din acest
motiv algoritmul se încadrează în strategia Greedy.

Exemplu: Fie graful de mai jos şi nodul de plecare v=1.

Conform algoritmului descris anterior se vor alege


pe rând următoarele muchii:
(1,2), (2,3), (3,5), (5,4), (4,1)
şi se obţine un ciclu hamiltonian de cost total 18.
Se observă că acesta nu este ciclul de cost minim

Concluzie: Metoda Greedy nu furnizează soluţia


optimă pentru orice tip de problemă. Dacă se
renunţă la criteriul de optimalitate se obţine o soluţie
apropiată de soluţia optimă. În funcţie de problemă
se decide dacă aceasta este acceptabilă sau se
foloseşte un algoritm exponenţial.

S-ar putea să vă placă și