Sunteți pe pagina 1din 7

Programming Techniques Backtracking

Stoica Spahiu Cosmin

Lucrarea 5. Backtracking.
Prezentarea Tehnicii Backtracking

Aceast tehnic poate fi utilizat pentru rezolvarea problemelor de cutare. Cutarea efectuat este exhaustiv, putnd fi folosit pentru generarea tuturor soluiilor posibile. Se folosete n rezolvarea problemelor care ndeplinesc simultan urmtoarele condiii: Soluia lor poate fi pus sub forma unui vector V = x1x2x3 . xn, cu x1A1 . Xn An Mulimile A1, A2, An sunt mulimi finite, iar elementele lor se afl ntr-o ordine bine stabilit Nu se dispune de o metod de rezolvare mai rapid La ntlnirea unei astfel de probleme suntem tentai s generm toate elementele produsului cartezian A1 A2 . An. Aceasta nu este totui o rezolvare foarte rezonabil. Timpul de execuie este att de mare, nct poate fi considerat infinit. Dac de exemplu dorim s generm toate permutrile unei mulimi, genernd produsul cartezian, am putea obine 1,1,1,.1, pentru ca apoi s constatm c nu am obinut o permutare (cifrele nu sunt distincte), dei chiar de la a 2-a cifr se poate observa c cifrele nu sunt distincte. Tehnica Backtracking are la baz un principiu extrem de simplu: se construiete soluia pas cu pas. Dac se constat c pentru o anumit valoare nu se poate ajunge la soluie, se renun la acea valoare i se reia cutarea din punctul unde am rmas.

Pentru uurarea nelegerii metodei vom prezenta o rutin unic, aplicabil oricrei probleme, rutin care utilizeaz noiunea de stiv. se alege primul element x1, ce aparine lui A1; presupunnd generate elementele x1xk, vom avea 2 posibiliti: o nu s-a gsit un astfel de element, caz n care se reia cutarea, considernd generate elementele x1 xk-1, iar aceasta se reia de la urmtorul element Ak, rmas netestat. o a fost gsit, caz n care se testeaz dac acesta ndeplinete anumite condiii (rspunsul poate fi verificat de o funcie Validare care returneaz true sau false). o dac le ndeplinete, se testeaz dac s-a ajuns la soluie (se utilizeaz funcia Soluie, care returneaz true dac s-a ajuns la soluie) dac s-a ajuns la soluie, se tiprete i se continu algoritmul pentru gsirea unei alte soluii, considerndu-se generate x1xk, iar elementul xk+1 se caut printre elementele mulimii Ak+1 rmase netestate dac nu s-a ajuns la soluie se reia algoritmul considerndu-se generate elementele x1, xk, xk+1 o dac nu le ndeplinete se reia algoritmul considernd generate x1xk, iar elementul xk+1 se caut printre elementele mulimii Ak+1 rmase netestate

Prezentarea algoritmului

Programming Techniques Backtracking

Stoica Spahiu Cosmin

O s exemplificm tehnica cutrii cu reveniri cu ajutorul unei probleme clasice: cum putem amplasa N regine pe o tabl de ah cu dimensiuni N x N astfel nct acestea s nu se atace dou cte dou. Este evident c nu putem amplasa dect o singur regin pe linie sau pe coloan i n final fiecare linie va avea amplasat cte o regin. De aceea vom ncerca amplasarea linie cu linie, ncepnd cu prima linie. Atunci cnd ncercm s amplasm o regin pe o linie ncepem cu prima coloan i continum cu urmtoarele, pn cnd gsim o poziie valid. n momentul n care nu mai gsim nici o poziie valid pe o linie vom reveni la linia anterioar, unde vom cuta urmtoarea poziie. S rezolvm, de exemplu, problema pentru N = 4. ncepem cu prima linie i prima coloan:

Problema Reginelor.

Pe cea de-a doua linie nu putem amplasa nici o dam pn pe cea de-a treia coloan:

Pe linia a treia nu putem amplasa nici o dam, de aceea vom reveni la linia anterioar pentru urmtoarea opiune:

Programming Techniques Backtracking Pe linia a treia prima opiune valid este pe coloana a doua:

Stoica Spahiu Cosmin

Dar acum nu mai avem nici o opiune valid pentru linia a patra. Revenim la linia a treia. Dar nici aici nu mai avem nici o opiune valid. De aceea trebuie s revenim la linia a doua. Este evident c aici nu mai avem ce face aa c se revine pn la linia a I-a:

Acum putem trece la linia a II-a:

Pe linia a treia putem amplasa o regin chiar pe prima coloan:

Programming Techniques Backtracking n fine, pe ultima linie avem urmtoarea opiune valid:

Stoica Spahiu Cosmin

Backtracking-ul se poate implementa mai uor recursiv. Programul complet pentru varianta recursiv este urmtorul:
#include "stdio.h" #include "conio.h" #include "math.h"
//aceasta funcie testeaz daca regina de pe linie este atacata de reginele poziionate anterior. Este de fapt funcia de Validare

int ataca(int linie, int memorie[])


{ int i;

//luam pe rnd toate damele poziionate anterior //verificam daca cele 2 dame sunt pe aceeai coloana sau pe diagonal

for(i=0;i<linie;i++)

if((memorie[linie]==memorie[i]) || (abs(memorie[i]-memorie[linie])==linie-i)) // se ataca, deci nu sunt corect poziionate. return 1; return 0; //daca am ajuns aici, nseamn c nu se atac }

//cile de afiare sunt infinite

void afiseaza(int dim,int memorie[])


{ int l,c; for(l=0;l<=dim;l++) printf("_"); for(l=0;l<dim;l++) { printf("\n|"); for(c=0;c<dim;c++) if(memorie[l]==c) printf("%c",6); else if((l+c)&1) printf(" "); else printf("%c",219); printf("|"); } printf("\n"); for(l=0;l<=dim;l++)

Programming Techniques Backtracking


printf("-"); printf("\n"); printf("o tasta, va rog!\n"); getch();

Stoica Spahiu Cosmin

// // // //

aici este funcia care face efectiv backtracking-ul (suntem la linia(nivelul) linie. dim ne spune ct de mare-i tabla memorie tine minte damele deja poziionate. Ea este stiva

void dame(int dim,int linie, int memorie[])

if(linie==dim)// daca am terminat afiseaza(dim,memorie);//afim else //altfel avem de lucru for(memorie[linie]=0;memorie[linie]<dim;memorie[linie]++) //ncercam toate coloanele de la stnga la dreapta if(!ataca(linie,memorie)) //daca e o poziie valida trecem la nivelul urmtor dame(dim,linie+1,memorie); //cnd ne ntoarcem din apelul recursiv nseamn c am revenit // la acest nivel i ncercm urmtoarea coloana } void main() { int n,memorie[100]; printf("dimensiunea tablei de joc="); scanf("%d",&n); dame(n,0,memorie);// prima linie are numrul zero }

n continuare prezentm i o implementare iterativ:


#include "stdio.h" #include "conio.h" #include "math.h"
//aceleai explicaii ca mai sus, dar nu de alta dar e aceeai funcie

int ataca(int linie, int memorie[])

int i; for(i=0;i<linie;i++) if((memorie[linie]==memorie[i]) || (abs(memorie[i]-memorie[linie])==linie-i)) return 1; return 0;

//la fel ca mai sus

void afiseaza(int dim,int memorie[]) { int l,c; for(l=0;l<=dim;l++) printf("_"); for(l=0;l<dim;l++) {

Programming Techniques Backtracking

Stoica Spahiu Cosmin


printf("\n|"); for(c=0;c<dim;c++) if(memorie[l]==c) printf("%c",6); else if((l+c)&1) printf(" "); else printf("%c",219); printf("|");

} printf("\n"); for(l=0;l<=dim;l++) printf("-"); printf("\n"); printf("o tasta, va rog!\n"); getch();

void main() { int n,memorie[100],nivel; printf("dimensiunea tablei de joc="); scanf("%d",&n); nivel=0; //datorit C-ului primul nivel are numrul zero //punem prima dam s se nclzeasc pe marginea tablei memorie[nivel]=-1; //ct timp nu ncercm s poziionm dama de linia -1 while(nivel>=0) { //avem o soluie if(nivel==n) { afiseaza(nivel,memorie); //ne ntoarcem la ultima linie nivel--; } //ncercm urmtoarea poziie de pe linia curent memorie[nivel]++; // dac dama a epuizat coloanele if(memorie[nivel]==n) //revenim la linia anterioar nivel--; else // altfel //dac este o poziie valid if(!ataca(nivel,memorie)) memorie[++nivel]=-1; //trecem la urmtorul nivel i scoatem nc o dam la nclzire } }

Aceast tehnic este relativ uor de folosit, dar fiind foarte lent i consumatoare de memorie este recomandabil pentru cazurile n care resursele nu sunt o problem.

Probleme propuse.
1. Se d un labirint cu N x M celule avnd forma unui ptrat, fiecare celul avnd maxim o u pe fiecare latur (n total maxim 4 ui). Uile pot funciona ntr-un 6

Programming Techniques Backtracking

Stoica Spahiu Cosmin

2. 3. 4. 5.

6.

singur sens. Se cere s se determine calea de la o celul oarecare ctre ieirea din labirint. Se d o hart cu n orae. Pe aceast sunt trasate m drumuri, care unesc oraele dou cte dou. S se determine calea care leag dou orae oarecare. S se determine toate mutrile pe care trebuie s le efectueze un cal pe o tabl de ah astfel nct s treac prin toate poziiile cel puin o dat, pornind dintr-o poziie oarecare. S se determine toate mutrile pe care trebuie s le efectueze un cal pe o tabl de ah astfel nct s treac prin toate poziiile o singur dat, pornind dintr-o poziie oarecare. Se dau x plci de gresie de dimensiuni 1x1, fiecare plac avnd fiecare margine vopsit ntr-o culoare. S se determine cum poate fi pavat o ncpere de dimensiuni N x M astfel nct marginile vecine ale plcuelor s aib aceeai culoare. Se dau x plci de gresie de dimensiuni 1x1, fiecare plac fiind vopsit ntr-o culoare. S se determine cum poate fi pavat o ncpere de dimensiuni N x M astfel nct dou plcue vecine s nu aib aceeai culoare.