Sunteți pe pagina 1din 7

Algoritmi divide et impera

Divide et impera este o tehnic de elaborare a algoritmilor care const n:


- descompunerea problemei ce trebuie rezolvat ntr-un numr de
subprobleme, mai mici, ale aceleiai probleme, ce pot fi rezovate
separat;
- rezolvarea succesiv i independent a fiecreia din aceste
subprobleme;
- recompunerea subsoluiilor astfel obinute pentru a gsi soluia
problemei iniiale.
Prin tehnica divide et impera se proceedeaz astfel: separm
vectorul V n dou pri de mrimi ct mai apropiate, sortm aceste pri
prin apeluri recursive, apoi interclasam soluiile pentru fiecare parte,
avnd grij s pstrm ordonarea crescatoare a elementelor.
Turnurile din Hanoi
Vom prezenta, n continuare, o funcie recursiv folosit pentru
rezolvarea unei probleme clasice aprut la sfritul secolului al XIX-lea, n
Europa, cunoscut sub numele de "Turnurile din Hanoi".
n esen
problema este urmtoarea: se dau trei tije, denumite stnga (sursa),
mijloc (aux) i dreapta (dest) i 64 de discuri, de dimensiuni (diametre)
diferite, aezate unul peste altul n ordine descresctoare (a diametrelor),
pe tija din stnga, ce formeaz un "turn". Se cere s se deplaseze turnul
format din cele n discuri (n=64) din poziia stnga n poziia dreapta, prin
intermediul tijei mijloc, respectnd urmtoarele reguli:
- se mut cte un singur disc;
- nu se poate aeza un disc mai mare, ca diametru, peste unul mai
mic.
Problema mutrii celor n discuri poate fi descris recursiv astfel:
- dac n=1 , se mut un singur disc;
- dac n >1 se mut primele n-1 discuri n poziia mijloc, prin
intermediul poziiei dreapta; se mut apoi discul rmas n poziia
dreapta, dup care se mut cele n-1 discuri din poziia mijloc n
poziia dreapta, prin intermediul poziiei stnga.
De exemplu deplasarea celor n discuri din poziia stnga n poziia
dreapta, muta (n, stanga, dreapta) este echivalent cu urmtoarea
secven de sub-probleme :
muta ( n-1 , stanga , mijloc );
deplasez un disc din poziia "stnga" n "dreapta";
muta ( n-1 , mijloc , dreapta );

/*t_hanoi.c - Problema 'Turnurilor din Hanoi', rezolvata recursiv */


#include <stdio.h>
#include <conio.h>
enum pozitie { stanga , mijloc , dreapta};
void tipareste_pozitie ( enum pozitie poz )
{
switch ( poz )
{
case 0 : printf ( "stanga" );/* se poate case stanga */
break;
case 1 : printf ( "mijloc" );
break;
case 2 : printf ( "dreapta" );
}
}
void deplasare( enum pozitie sursa , enum pozitie dest )
{
printf ( "muta un disc din " );
tipareste_pozitie ( sursa );
printf ( " in " );
tipareste_pozitie ( dest );
printf ( "\n" );
return;
}
void muta ( int n, enum pozitie sursa, enum pozitie inter, enum
pozitie dest )
{
if ( n > 0 )
{
muta ( n - 1 , sursa , dest , inter );
deplasare ( sursa , dest );
muta ( n - 1 , inter, sursa , dest );
}
}
void main (void)
{
int i , nd;
clrscr();
printf ( "Nr. de discuri :" );
scanf ("%d", &nd);
while ( getchar() != '\n' );
printf ("Mutarile pt. deplasarea unui turn cu %d discuri sunt :\n",
nd);
muta ( nd , stanga , mijloc , dreapta );
printf("\n\n");
}

Problema tieturilor

Se consider o form dreptunghiular cu lungimea l i limea h,


care are pe suprafaa ei n guri, ce au coordonate ntregi. Se cere s se
separe (taie) din aceast form dreptunghiular o bucat, tot de form
dreptunghiular, de arie maxim care nu prezint guri.
Vom considrea c poziiile gurilor sunt reinute n doi vectori xg i
yg. Dreptunghiurile iniiale, precum i cele obinute n procesul tierii sunt
memorate prin coordonatele colului din stnga jos i cele dou dimensiuni
(l i h). Pentru un dreptunghi dat, la un moment dat, se caut dac are
vreo gaur.; dac are, atunci problema se descompune n alte patru
subprobleme, de acelai tip. n cazul cnd bucata respectiv nu are guri,
atunci se compar aria ei cu aria bucii maxime, gsite pn n acel
moment. Dreptunghiul de arie maxim este memorat tot prin coordonatele
stnga jos i dimensiuni, transmise de la o etap la alta. Dac
dreptunghiul iniial avea n guri, am descompus problema n alte patru
subprobleme, cu cel mult n-1 guri.
Pentru ca o gaur de coordonate xg(i) i yg(i) s se afle n interiorul
dreptunghiului trebuie ca:
xg(i) > x; xg(i) < x+l; yg(i) > y; yg(i) < y+h;
Printr-o tietur vertical se obin dou dreptunghiuri de coordonate
i dimensiuni:
(x, y) i dimensiuni xg(i)-x, h;
(xg(i), y) i dimensiuni l+x-xg(i), h;
iar pentru tietura pe orizontal se obin alte dou dreptunghiuri:
(x, y) i dimensiuni l, yg(i)-x;
(x, yg(i)) i dimensiuni l, h+y-yg(i);
n continuare este prezentat programul pentru rezolvarea acestei
probleme.
/*taieturi.c - Problema taieturilor, exemplu de algoritm "divide et impera"*/
#include <stdio.h>
#include <conio.h>
#define MAXG 5
int n=4;
void dreptunghi (int x, int y, int l, int h, int *px, int *py, int *pl, int
*ph, int xg[], int yg[]){
int gasit, i;
i=0; gasit=0;
while (i<n && !gasit) // exista gaura in dreptunghiul ((x,y), l, h) ?
if (xg[i] > x && xg[i] < x+l && yg[i] > y && yg[i] < y+h)
gasit=1;
else
i++;

if (gasit) // daca da, avem 2 taieturi -> 4 dreptunghiuri


{dreptunghi(x, y, xg[i]-x, h, px, py, pl, ph, xg, yg);
dreptunghi(xg[i], y, l+x-xg[i], h, px, py, pl, ph, xg, yg);
dreptunghi(x, y, l, yg[i]-y, px, py, pl, ph, xg, yg);
dreptunghi(x, yg[i], l, h+y-yg[i], px, py, pl, ph, xg, yg);
}
else if (l*h > *pl* *ph) // se compara aria drept. cu cea a drept.
taiat
{*px=x; *py=y; // daca acesta nu mai contine gauri (!gasit)
*pl=l; *ph=h;
}
}
void main (void)
{
int xg[MAXG]={1, 2, 3, 4}, yg[MAXG]={1, 2, 3, 4};
int l=5, h=5;
int *px, *py, lmax=0, hmax=0;
clrscr();
dreptunghi(0, 0, l, h, px, py, &lmax, &hmax, xg, yg);
printf("\nDreptunghiul maxim este: (x,y) stanga-jos = (%d,
%d)\n\t\t\t"
"lung (dx) = %d, latime (dy sau h) = %d\n", *px, *py, lmax,
hmax);
printf("\nApasati o tasta pentru a termina programul !!!\n");
getch();
}

Problema labirintului
Se consider un labirint, specificat printr-o matrice cu n linii i m
coloane, n care fiecare element al matricei reprezint o camer din
labirint. Pornind dintr-o camer oarecare, de coordonate i, j se cere s se
gseasc toate ieirile din labirint.
Un element al matricii L[i][j], ce are patru vecini (direciile spre
partea de sus, dreapta, jos, stnga ale poziiei curente, considerate n
aceast ordine), poate lua valori ntre 0 i 15, dac interpretm valoarea
asociat camerei (elementului din matrice) n zecimal, codificnd
(memornd) pentru fiecare direcie cu ieire valoarea 1, iar n caz contrar
cu valoarea 0. irul format cu aceste patru cifre binare este transformat
(interpretat) n zecimal i este memorat n elementul L[i][j]. De exemplu
dac camera (i, j) are ieiri spre partea de sus i spre stnga, se obine
valoarea binar 1001, adic 9 n zecimal, valoare ce va fi memorat n L[i]
[j].
Ieirile vor fi codificate cu valoarea 16, motiv pentru care vom ataa
dou linii, sus i jos, i dou coloane, la stnga i respectiv dreapta, ce vor
conine aceast valoare.

Drumul parcurs spre ieire se va memora ntr-o matrice, d, cu dou


linii i un numr de coloane egal cu dimensiunea matricii (n*m), care va
memora n cele dou linii ale unei coloane k, linia i coloana n care s-a
ajuns n drumul spre ieire.
Funcia iesire, care determin drumul spre ieire din labirint, va
realiza urmtoarele operaii:
- testeaz dac nu s-a ieit din labirint (L[i][j]==16);
- dac s-a ajuns la ieire se tiprete drumul gsit;
- dac nu, se rein n matricea d[0][k]=i, d[1][k]=j), k fiind numrul
curent al camerei vizitate;
- se testeaz dac aceast camer a mai fost vizitat, iar dac da se
iese din funcie;
- se testeaz pe rnd cele patru direcii (sus, dreapta, jos, stnga) i
acolo unde este gsit o ieire se apeleaz funcia cu noile
coordonate, i se decremeteaz valoarea k.
ntregul program este prezentat n continuare.
/* ieslabir.c - problema "labirintului": iesirea dintr-un labirint
specificat printr-o matrice L, in care se memoreaza valorile
zecimale ale codificarilor binare ale drumului spre iesire, si
anume: fiecare directie care duce spre iesire e codificata cu 1, iar
restul cu 0; deci pentru o casuta oarecare se considera directiile:
sus, dreapta, jos si stanga, in aceasta ordine; de exemplu daca o
camera va avea directii spre iesire sus si stanga, adica 1001, in
binar, se va memora valoarea 9; matricea va fi completata cu 2
linii si 2 coloane care vor contine valorile 16 (iesire din labirint).
Drumul spre iesire va fi memorat in matricea d[2][n*m], pe prima
linie i, iar pe cea de-a doua j, pentru drumul catre iesire din
pozitia k.*/
#include <stdio.h>
#include <conio.h>
#define N 10
#define M 15
#define DIM (N-1)*(M-1)
// N, M - dimensiuni matrice labirint, 2 linii si 2 coloane atasate;
// pentru a delimita labirintul
int L[N][M] = {{16, 16, 16, 16, 16, 16, 16},
{ 16, 0, 2, 1, 1, 0, 16},
{ 16, 0, 2, 0, 8, 0, 16},
{ 16, 0, 2, 4, 8, 0, 16},
{ 16, 1, 7, 4, 2, 0, 16},
{ 16, 0, 2, 0, 2, 0, 16},
{ 16, 16, 16, 16, 16, 16, 16},
};
int d[2][DIM];
int i, j, n=5, m=5, nr_sol=0, nse=4;
void tiparire (int k, int d[2][DIM])
{int h;
nr_sol++; // contorizare numar de solutii
printf("\nSolutia %d:\n", nr_sol);

for (h=1; h<=k; h++){


printf("[%d,%d]-> ", d[0][h], d[1][h]);
if (h%8==0)
printf("\n");
}
printf("\n");
if (nr_sol % nse == 0) // continua afisarea urmatorului ecran de
solutii
{printf("\n");
// dupa afisarea a 'nse' solutii, apasa tasta
'Enter'
printf("Apasati ENTER pentru a continua afisarea");
getch();
clrscr();
}
}
void iesire (int k, int i, int j, int L[N][M], int d[2][DIM]){
int gasit, h;
if (L[i][j] == 16)
tiparire(k, d);
else
{k++;
d[0][k]=i;
d[1][k]=j;
gasit=0;
for (h=1; h<=k-1; h++)
if (d[0][h] == d[0][k] && d[1][h] == d[1][k])
gasit=1;
if (!gasit)
for (h=1; h<=4; h++)
switch (h){
case 1: if (L[i][j] & 8)
iesire(k, i-1, j, L, d);
break;
case 2: if (L[i][j] & 4)
iesire(k, i, j+1, L, d);
break;
case 3: if (L[i][j] & 2)
iesire(k, i+1, j, L, d);
break;
case 4: if (L[i][j] & 1)
iesire(k, i, j-1, L, d);
}
k--;
}
}
void main (void) {
int il, ic;
clrscr();
/*

printf("Dimensiuni labirint (matrice), linii, coloane: ");


scanf("%d%d", &n, &m);
for (il=1; il<=n; il++)
for (ic=1; ic<=m; ic++){
printf("L[%d, %d]=", il, ic);
scanf("%d", &L[il][ic])
}
*/
printf("Punctul de plecare din labirint (i, j): ");
scanf("%d%d", &i, &j);
// matricea L, impreuna cu cele 2 linii si 2 coloane, care
// delimiteaza labirintul, si specifica iesirea din labirint
// are dimensiunile (n+2)*(m+2)
for (ic=1; ic<=m; ic++){
L[0][ic]=16;
L[n+1][ic]=16;
}
for (il=1; il<=m; il++){
L[il][0]=16;
L[il][m+1]=16;
}
printf("\nLabirintul are urmatoarea configuratie:\n\n");
for (il=0; il<n+2; il++){
for (ic=0; ic<m+2; ic++)
if (il==i && ic==j)
printf(" x");
else
printf("%3d", L[il][ic]);
printf("\n\n");
}
printf("Coordonate punct plecare L[%d,%d]=%d\n", i, j, L[i][j]);
iesire(0, i, j, L, d);
printf("\nNumar total de solutii: %i\n", nr_sol);
printf("Apasati ENTER pentru a termina programul !");
getch();}