Professional Documents
Culture Documents
Metoda Greedy................................................................................................ 2
1.1 Consideraii teoretice................................................................................ 2
Explicarea numelui.......................................................................................... 2
Aplicabilitatea algoritmilor de tip Greedy........................................................2
Modul de lucru al algoritmilor de tip Greedy...................................................2
1.2 Implementare............................................................................................ 2
1.3 Exemple.................................................................................................... 3
Problema comis-voiajorului.............................................................................. 3
2. Metoda Divide and Conquer............................................................................ 7
2.1 Consideraii teoretice................................................................................ 7
Aplicabilitatea algoritmilor de tip Divide and Conquer....................................7
Modul de lucru al algoritmilor de tip Divide and Conquer................................7
2.2 Implementare............................................................................................ 7
2.3 Exemple.................................................................................................... 8
Turnurile din Hanoi.......................................................................................... 8
Determinarea minimului i maximului dintr-un ir de numere.........................9
3 Probleme propuse.......................................................................................... 12
3.1 Conectarea oraelor cu cost minim.........................................................12
3.2 Cele mai apropiate puncte de pe o dreapt............................................15
1. Metoda Greedy
1.2 Implementare
1.3 Exemple
Problema comis-voiajorului
Enun Se condider n orae. Se cunosc distanele dintre oricare dou orae. Un
comis-voiajor trebuie s treac prin toate cele n orae. Se cere s se determine
un drum care pornete dintr-un ora, trece exact o dat prin fiecare din celelalte
orae i apoi revine la primul ora, astfel nct lungimea drumului s fie minim.
Rezolvare Pentru gsirea unei soluii optime la aceast problem este nevoie de
algoritmi cu timp de rulare foarte mare (de ordin exponenial O(2 n)). n situaiile
practice asemenea algoritmi cu timp foarte mare de rulare nu sunt acceptabili.
Ca urmare se face un compromis i se accept algoritmi care nu gsesc soluia
optim ci doar o soluie aproape de optim, dar au n schimb un timp de rulare
mic. Propunem n continuare o soluie greedy la aceast problem. Ideea este
urmtoarea. Se pornete dintr-un ora oarecare. Se caut drumul cel mai scurt
care pleac din oraul respectiv ctre orae nevizitate nc. Se parcurge acel
drum i se ajunge ntr-un alt ora. Aici din nou se caut cel mai scurt drum ctre
oraele nevizitate nc. Se parcurge i acest drum, ajungndu-se ntr-un nou
ora. Repetnd aceti pai se parcurg toate oraele. La final se parcurge drumul
care duce napoi spre primul ora.
S considerm exemplul din figura 1. Avem 4 orae cu distanele reprezentate n
figur.
}
}
*j_min = j;
int main(void)
{
FILE *fin;
int i, j;
int count, cost, min, j_min;
/* Deschidem fisierul pentru citire in mod text. */
fin = fopen("comis.in", "rt");
if (!fin)
{
printf("Eroare: nu pot deschide fisierul.\n");
return -1;
}
/* Citim datele din fisier. */
fscanf(fin, "%d", &n);
for (i=0; i<n; i++)
for (j=0; j<n; j++)
fscanf(fin, "%d", &(d[i][j]));
/* Afisam pe ecran datele preluate din fisier. */
printf("Avem %d orase.\n", n);
printf("Distantele dintre orase sunt:\n");
for (i=0; i<n; i++)
{
for (j=0; j<n; j++)
printf("%d ", d[i][j]);
printf("\n");
}
printf("\n");
/* Initial nici un oras nu este vizitat. */
for (i=0; i<n; i++)
vizitat[i] = 0;
/* Primul oras vizitat este cel cu numarul "0".
Costul total este zero deocamdata. */
drum[0] = 0;
vizitat[0] = 1;
count = 1;
cost = 0;
/* Parcurgem restul de n-1 orase. */
for (i=0; i<n-1; i++)
{
/* Alegem urmatorul oras care sa fie vizitat. */
alege(drum[count-1], &min, &j_min);
/* Parcurgem drumul minim gasit si vizitam
un nou oras. */
printf("Am ales drumul (%d, %d) de cost %d.\n",
drum[count-1], j_min, min);
drum[count] = j_min;
vizitat[j_min] = 1;
count++;
cost += min;
}
/* Parcurgem drumul de la ultimul oras vizitat
catre primul oras si actualizam costul
total. */
cost += d[drum[n-1]][0];
/* Afisam drumul parcurs. */
printf("\nDrumul are costul %d si este:\n", cost);
for (i=0; i<n; i++)
printf("%d ", drum[i]);
printf("0\n");
return 0;
Fiierul cu date de intrare pentru reeaua de orae din figura 1 este urmtorul:
4
0
4
2
7
4
0
2
1
2
2
0
1
7
1
1
0
2.2 Implementare
n general implementarea metodei Divide and Conquer se face prin funcii
recursive. De regul vom avea o singur funcie care primete ca parametri
informaiile necesare pentru a rezolva o subproblem i returneaz rezultatele
pentru subproblema respectiv.
Funcia va determina dac subproblema este una trivial, caz n care va calcula
direct soluia pentru ea. Dac subproblema nu este una trivial, atunci funcia va
mpri subproblema n subsubprobleme i se va auto-apela n mod recursiv
pentru fiecare din ele. Pe urm va combina rezultate obinute pentru
subsubprobleme i va gsi soluia pentru subproblem.
Prezentm schema general de lucru a unei asemena funcii, n pseudocod:
function divide(* parametri care definesc o subproblema)
{
if (* subproblema este una triviala)
{
* rezolva subproblema in mod direct
* returneaza rezultatele
}
else
{
* imparte subproblema in subsubprobleme
* pentru fiecare subsubproblema
* apeleaza divide(subsubproblema)
2.3 Exemple
Turnurile din Hanoi
Enun Fie trei tije notate cu a, b i c. Pe tija a se afl n discuri de dimensiuni
diferite, aezate n ordinea descresctoare a diametrelor, astfel nct discul cu
diametrul cel mai mare se afl cel mai jos, iar discul cu diametrul cel mai mic se
afl n vrful stivei. S se gseasc o modalitate de a muta toate cele n discuri
de pe tija a pe tija b, folosind tija intermediar c, atfel nct n final discurile s fie
ordonate tot descresctor. n timpul operaiilor care se fac, este interzis
plasarea unui disc mai mare peste un disc mai mic.
Rezolvare Problema noastr iniial este s mutm n discuri de pe tija a pe tija
b, folosind tija intermediar c. O putem codifica n felul urmtor: (n,a,b,c). Dac
am gsi o modalitate de a muta n-1 discuri de pe tija a pe tija intermediar c,
atunci am putea s mutm discul cel mai mare de pe tija a pe tija b. Pe urm ar
/* Afisam rezultatele. */
printf("Minimul este %d.\n", min);
printf("Maximul este %d.\n", max);
return 0;
Dac analizm metoda de mai sus, vom vedea c ea face comparaii inutile,
deoarece orice element care este candidat pentru minim nu poate fi n acelai
timp candidat pentru maxim, i invers. Deci este redundant s testm fiecare
element n parte att pentru minim ct i pentru maxim.
Putem aplica tehnica Divide and Conquer, mprind irul de numere n dou
pri. Determinm minimul i maximul pentru fiecare din cele dou pri, iar pe
urm determinm maximul global prin compararea celor dou maxime pariale,
iar minimul global prin compararea celor dou minime pariale.
Implementare Pentru implementare vom defini o funcie recursiv ce va cuta
minimul i maximul ntr-o secven a irului. Iniial vom apela aceast funcie
pentru ntregul ir. Funcia se va apela pe ea nsi, recursiv, pentru jumtarea
stng i pentru jumtatea dreapt a secvenei.
Cod surs Prezentm n continuare codul surs n limbajul C pentru rezolvarea
problemei.
#include <stdio.h>
/* Declaram sirul de numere direct din cod. Alternativ
el poate fi citit de la tastatura sau din fisier. */
#define N 10
int x[] = {10, 5, 23, -11, 4, 2, 0, -6, 66, 40};
/* Numaram cate comparatii se fac in total. */
int comp = 0;
/* Functie care determina minimul si maximul dintr-o
secventa a sirului de numere. Secventa este
delimitata de indicii "st" si "dr". Valorile minime
si maxime gasite vor fi returnate prin pointerii
"min" si "max" primiti ca si parametru. */
void minmax(int st, int dr, int *min, int *max)
{
int mijloc, min_st, max_st, min_dr, max_dr;
printf("Caut in secventa [%d..%d].\n", st, dr);
/* Daca secventa contine un singur numar, atunci
el este atat minim cat si maxim. */
if (st == dr)
{
*min = x[st];
*max = x[st];
}
/* Daca secventa contine doua numere, atunci
facem o comparatie pentru a gasi minimul si
maximul. */
else if (st == dr - 1)
{
comp++;
if (x[st] < x[dr])
{
*min = x[st];
*max = x[dr];
}
else
{
*min = x[dr];
*max = x[st];
}
}
/* Daca avem mai multe numere, atunci divizam
problema in subprobleme. */
else
{
/* Divizare. */
mijloc = (st + dr) / 2;
minmax(st, mijloc, &min_st, &max_st);
minmax(mijloc+1, dr, &min_dr, &max_dr);
}
int main(void)
{
int min, max;
int i;
/* Afisam sirul de numere. */
printf("Avem %d numere.\n", N);
for (i=0; i<N; i++)
printf("%d ", x[i]);
printf("\n\n");
/* Apelam functia recursiva. */
minmax(0, N-1, &min, &max);
/* Afisam rezultatele. */
printf("\n");
printf("Minimul este %d.\n", min);
printf("Maximul este %d.\n", max);
printf("Comparatii facute: %d.\n", comp);
}
return 0;
Dac rulm n paralel cele dou programe, vom vedea c ntradevr pentru
acelai ir de numere metoda Divide and Conquer face mai puine comparaii
dect metoda clasic.
3 Probleme propuse
La urmtorul pas s-a ales muchia de cost minim care pleac din nodurile 0, 2 sau
1 ctre nodurile nc neintroduse n arbore. Muchia aleas a fost (1,5) de cost 3.
Nodul 5 a fost adugat n arbore.
Urmtoarea muchie aleas a fost (5,4) de cost 2. Nodul 4 a fost adugat n
arbore. Apoi a fost aleas muchia (4,6) de cost 2 i nodul 6 a fost adugat i el n
arbore.
Pe urm a fost aleas muchia (1,3) de cost 4 i nodul 3 a fost introdus n arbore.
n acest moment algoritmul s-a ncheiat deoarece toate oraele au fost conectate
la reea. Costul total al conectrii a fost 10 + 9 + 3 + 2 + 2 + 4 = 30.
Implementare Matricea costurilor, C, se reine ntr-un tablou bidimensional.
Pentru perechile de orae ntre care nu se poate face legtur se va trece n
matricea costurilor valoarea 0. n termeni Greedy, mulimea noastr iniial de
elemente A este muimea tuturor perechilor de orae ntre care se poate stabili
legtur direct. Adic A={(i, j) | ci,j>0}. Pentru graful din figura 1, mulimea A
va conine elementele {(0,1), (0,2), (1,2), (1,3), (1,5), (2,3), (4,5), (4,6), (5,6)}.
Submulimea B pe care o cutm va conine o parte din perechile aflate n
mulimea A. Se poate demonstra c soluia optim B* conine n-1 perechi atunci
cnd numrul de orae este n (presupunem c graful este conex, adic se poate
construi o reea care s conecteze toate oraele).
Pentru construirea mulimii B, vom selecta oraele rnd pe rnd pentru a le
aduga la reea. Vom spune c un ora este selectat atunci cnd el a fost
conectat la reeaua de orae printr-o muchie care face parte din mulimea B.
n implementare nu vom lucra cu mulimea A sub forma explicit de perechi, ci
vom folosi matricea costurilor C. Vom eticheta liniile i coloanele matricei
costurilor dup cum urmeaz. Atunci cnd un ora oi este selectat, linia i din
matrice se marcheaz, iar coloana i din matrice se terge.
Pentru a alege urmtorul element din mulimea A care s fie prelucrat, cutm
cel mai mic cost din matricea costurilor din liniile marcate i coloanele care nu
sunt terse. S zicem c cel mai mic cost a fost gsit ca fiind elementul c i_min,j_min.
Atunci urmtorul element din mulimea A care va fi prelucrat este perechea
(i_min,j_min).
tergerea coloanelor din matricea costurilor nu va nsemna o tergere fizic, ci
doar una logic. Vom folosi doi vectori prin care vom memora care linii sunt
marcate i care coloane sunt terse.
O schema de cod surs pentru rezolvarea acestei probleme arat astfel:
...
/* Numarul maxim de noduri din graf. */
#define N_MAX 30
/* Numarul de orase. */
int n;
/* Matricea costurilor de conectare a oraselor. */
int c[N_MAX][N_MAX];
...
/* Marcam linia noului nod si stergem
coloana lui. */
marcat[j_min] = 1;
sters[j_min] = 1;
/* Adaugam muchia la arbore. */
arbore[count][0] = i_min;
arbore[count][1] = j_min;
count++;
}
...
return 0;