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 = x 1 x 2 x 3 . x n , cu
x 1 A 1 . X n A n
Mulimile A 1 , A 2 , A n 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 A 1 A 2 . A n . 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.
Prezentarea algoritmului Pentru uurarea nelegerii metodei vom prezenta o rutin unic, aplicabil oricrei probleme, rutin care utilizeaz noiunea de stiv. se alege primul element x 1 , ce aparine lui A 1 ; presupunnd generate elementele x 1 x k , vom avea 2 posibiliti: o nu s-a gsit un astfel de element, caz n care se reia cutarea, considernd generate elementele x 1 x k-1 , iar aceasta se reia de la urmtorul element A k , 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 x 1 x k, iar elementul x k+1 se caut printre elementele mulimii A k+1 rmase netestate dac nu s-a ajuns la soluie se reia algoritmul considerndu-se generate elementele x 1 , x k , x k+1
o dac nu le ndeplinete se reia algoritmul considernd generate x 1 x k , iar elementul x k+1 se caut printre elementele mulimii A k+1 rmase netestate Programming Techniques Backtracking Stoica Spahiu Cosmin 2 Problema Reginelor. 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:
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 Stoica Spahiu Cosmin 3 Pe linia a treia prima opiune valid este pe coloana a doua:
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 Stoica Spahiu Cosmin 4 n fine, pe ultima linie avem urmtoarea opiune valid:
Backtracking-ul se poate implementa mai uor recursiv. Programul complet pentru varianta recursiv este urmtorul:
int ataca(int linie, int memorie[]) //aceasta funcie testeaz daca regina de pe linie este atacata de reginele poziionate anterior. Este de fapt funcia de Validare { int i; //luam pe rnd toate damele poziionate anterior for(i=0;i<linie;i++) //verificam daca cele 2 dame sunt pe aceeai coloana sau pe diagonal 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 }
void dame(int dim,int linie, int memorie[]) // 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 { 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 }
int ataca(int linie, int memorie[]) //aceleai explicaii ca mai sus, dar nu de alta dar e aceeai funcie { int i; for(i=0;i<linie;i++) if((memorie[linie]==memorie[i]) || (abs(memorie[i]-memorie[linie])==linie-i)) return 1; return 0; }
void afiseaza(int dim,int memorie[]) //la fel ca mai sus { int l,c; for(l=0;l<=dim;l++) printf("_"); for(l=0;l<dim;l++) { Programming Techniques Backtracking Stoica Spahiu Cosmin 6 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) { if(nivel==n) //avem o soluie { 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 Programming Techniques Backtracking Stoica Spahiu Cosmin 7 singur sens. Se cere s se determine calea de la o celul oarecare ctre ieirea din labirint. 2. 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. 3. 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. 4. 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. 5. 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. 6. 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.