Documente Academic
Documente Profesional
Documente Cultură
Cuprins
Backtracking iterativ 1) Definiie 2) Probleme prototip 3) Tipul de probleme la care se aplic metoda 4) Mecanism general 5) Rutina 6) Problema permutarilor 7) Submultimile unei multimi 8) Problema permutarilor literelor unui cuvant Backtracking recursiv
DEFINIIE Metoda Backtracking este o metod sau o tehnic de programare care construiete progresiv soluia final ncepnd de la soluia nul sau vid i alegnd pentru fiecare component a sa o valoare din mulimea soluiilor posibile. Metoda se traduce prin revenire sau cale ntoars sau drum napoi deoarece de fiecare dat cnd nu se poate nainta ne ntoarcem cu un pas sau mai muli napoi . Problema 1 prototip:
Se dau dou mulimi: S1={A, B, C} i S2={M, N}. Determinai toi vectorii formai din dou componente x1 , x2 aparinnd mulimii S1S2 astfel nct dac x1 este A sau B, x2 nu poate fi M.
Soluii: soluii rezultat: x{(A, M), (A, N), (B, M), (B, N), (C, M), (C, N)} soluii rezultat: x{(A, N), (B, N), (C, M), (C, N)}
Mecanismul general
Metoda urmrete s evite generarea tuturor soluiilor posibile. n acest scop elementele vectorului x=(x1, x2, , xn) primesc pe rnd valori n sensul c lui xk i se atribuie o valoare numai dac au fost deja atribuite valori pentru componentele x1, x2, , xk-1. Odat stabilit o valoare pentru xk-1 nu se trece direct la atribuirea unei valori pentru xk; nti se verific condiia de continuare referitoare la componentele x1, x2, , xk-1. Dac au fost ndeplinite condiiile de continuare atunci se alege o valoare pentru xk iar nendeplinirea lor exprim faptul c oricum am alege componentele xk, xk+1, , xn nu vom putea ajunge la o soluie de tip rezultat. Asta nseamn c acele condiii de continuare sunt strict necesare pentru obinerea unei soluii. n cazul nendeplinirii condiiilor de continuare va trebui s se fac o nou alegere pentru xk sau dac mulimea Sk a fost epuizat se va ncerca o nou alegere pentru componenta xk-1 dup care se face o nou alegere pentru xk. Aceast revenire la componenta anterioar prin micorarea lui k d numele metodei. n unele probleme vom ntlni att condiii de continuare ct i condiii interne ntre ele existnd o strns legtur. n alte tipuri de probleme condiiile interne vor fi aceleai cu condiiile de continuare i atunci cnd pasul k va atinge valoarea n condiiile de continuare sunt chiar condiiile interne. Prin aceast metod orice vector este construit progresiv ncepnd cu prima component i mergnd ctre ultima cu eventualele reveniri asupra valorilor atribuite anterior (cu unul sau mai muli pai napoi att timp ct este posibil revenirea).
Prin atribuiri sau ncercri de atribuiri euate din cauza nerespectrii condiiilor de continuare, anumite valori sunt consumate sau testate i vom nota cu Ck mulimea valorilor consumate (testate) fiecare mulime Ck fiind inclus cu posibilitate de egal n Sk.
Pn la xk toate componentele au primit valorile v1, v2, , vk-1 n ncercarea de a construi un vector soluie. Aceste valori satisfac condiiile de continuare pentru componenta xk. Urmeaz s se fac o atribuire din Sk-Ck, unde Ci reprezint mulimea valorilor consumate (testate). Aplicarea metodei Backtracking ncepe n situaia n care nici o valoare nu a fost consumat i se ncearc atribuirea asupra primei componente (acest lucru presupune c mulimile Ck sunt vide).
Rutina
Atunci cnd mulimile de valori posibile sunt distincte S1SSSn se citesc mulimile S1,S2,,Sn se iniializeaz C1, C2, , Cn cu mulimea vid k = 1; //se iniializeaz pasul de lucru cu 1 while (k > 0) do //configuraia nu e final if (k == n+1 ) atunci //s-a depit dimensiunea vectorului se tiprete soluia se scade cu o unitate pasul de lucru else dac Ck Sk atunci //mai exist valori netestate nc se alege o valoare vk Sk-Ck se verific pentru vectorul (v1, v2, , vk) condiiile de continuare dac acestea sunt ndeplinite atunci xk = vk i k = k+1 else Ck k=k-1
Atunci cnd mulimile de valori posibile coincid S1=SS==Sn { void back() int k, i, s; s = 3; k = 1; for (i = 1; i <= n; i++) x[i] = 0; while (k > 0) { if (k == n + 1) { retsol(); k--; } else if (x[k] < s) { x[k]++; if (continuare(k)) k++; } else { x[k] = 0; k--; } }
PROBLEMA PERMUTRILOR
S se genereze toate permutrile unei mulimi de n numere (1, 2, , n). Explicaii: Funcia retsol este funcia de afiare a soluiilor. Cu o variabil nr contorizm numrul soluiilor iar cu ajutorul contorului i de la 1 la n afim soluiile. n funcia continuare nu avem dect o singur condiie, ca x[i] s fie diferit de x[k] (condiia combinrilor) pentru a nu se repeta soluiile. Funcia care stabilete soluiile rezultat i le reine n vectorul x[i] este back. Att timp ct se mai pot alege soluii posibile se verific dac nu s-a depit dimensiunea vectorului x[i] i se tiprete soluia, scznd i pasul de lucru cu o unitate. n caz contrar se verific dac mai sunt valori netestate nc i pentru acestea se verific condiiile problemei, apelnd funcia continuare. Dac condiiile sunt respectate valoarea devine soluie a problemei i se tiprete folosind apelul funciei retsol. n funcia principal se citete n care reprezint numrul de elemente ale mulimii iar la sfrit se apeleaz funcia back pentru a tipri soluiile. Exemplu: Fie n=3 adic mulimea {1, 2, 3}. Vor exista 6 variante de permutri : 123, 132, 213, 231, 312, 321
#include<iostream.h> #include<conio.h> #include<stdio.h> int x[10],n,nr = 1; void retsol() { int i ; printf("\nSolutia cu nr. %d este:", nr); for (i = 1; i <= n; i++) printf("%d", x[i]); nr++; } int cont(int k) { int i,t = 1; for (i = 1; i <= k - 1; i++) if (x[i] == x[k]) t = 0; return t; }
void back() { int k, i, s; k = 1;s = n; for (i = 1; i <= n; i++) x[i] = 0; while (k != 0) if (k == n + 1) { retsol(); k--; } else if (x[k] < s) { x[k]++; if (cont(k) == 1) k++; } else { x[k] = 0; k--; } } int main() { printf("n="); scanf("%d", &n); back(); return 0; }
n funcia principal citim n - numrul de elemente ale mulimii. Cu ajutorul variabilei m, care pornete de la 0 la n, vom apela funcia back pentru a tipri soluiile rezultat.
Exemplu: Pentru mulimea {1, 2, 3} avem 8 submulimi: , {1}, {2}, {3}, {1,2}, {1, 3}, {2, 3}, {1, 2, 3}
#include<iostream.h> #include<stdio.h> #include<conio.h> int x[100],n,m,nr = 1; void retsol() { printf("\nSolutia cu nr %d : ", nr); if (m == 0) printf("0"); for (int i = 1; i <= m; i++) printf("%d ", x[i]); nr++; } int cont(int k) { int i,test = 1; for (i = 1; i <= k - 1; i++) if (x[i] >= x[k]) test = 0; return test; }
void back() { int i, k, s; k = 1; s = n; while (k != 0) if (k == m + 1) { retsol(); k--; } else if (x[k] < s) { x[k]++; if (cont(k) == 1) k++; } else { x[k] = 0; k--; } }
int main() {
printf("n="); scanf("%d", &n); for (m = 0; m <= n; m++) back(); return 0 ; }
Exemplu: Vom considera un cuvnt format din patru litere: ALEX. n acest caz vom avea 24 de soluii: ALEX, ALXE, AELX, AEXL, AXLE, AXEL, LAEX, LAXE, LEAX, LEXA, LXAE, LXEA, EALX, EAXL, ELAX, ALXA, EXAL, EXLA, XALE, XAEL, XLAE, XLEA, XEAL, XELA.
#include<iostream.h> #include<stdio.h> #include<conio.h> #include<string.h> int n,x[15],nr = 1; char cuv[15]; void retsol() { printf("Solutia cu nr %d : ", nr); for (int i = 1; i <= n; i++) printf("%c ", cuv[x[i] - 1]); nr++; printf("\n");
} int cont(int k) { int i,test = 1; for (i = 1; i <= k - 1; i++) if (x[i] == x[k]) test = 0; return test; }
void back() { int k; k = 1; while (k > 0) if (k == n + 1) { retsol(); k--; } else if (x[k] < n) { x[k]++; if (cont(k) == 1) k++; } else { x[k] = 0; k--; } } int main() { printf("Cuvantul : "); scanf("%s", &cuv); n = strlen(cuv); back(); return 0; }
Rutina
n cazul variantei recursive rutina back se definete ca o funcie recursiv cu parametrul k (nivelul curent din stiv). Urcarea in stiv se face prin autoapelul funciei back cu o nou valoare pentru k. Generarea potenialilor candidai la soluie se face print-o instruciune for care parcurge valorile posibile de la limita inferioar la limita superioar. Iat soluia pentru problema permutarilor:
Implementarea codului
#include<iostream.h> #include<conio.h> int st[20],n; void tipar() { for(int i=1;i<=n;i++) cout<<st[i]<<' '; cout<<endl; } int valid ( int k ) { for ( int i=1;i<=k-1;i++) if (st[i]==st[k]) return 0; return 1; } void back ( int k ) { for ( int i=1;i<=n;i++ ) { st[k]=i; if ( valid ( k ) ) if ( k==n ) tipar(); else back(k+1); } } int main() { cout<<"n="; cin>>n; back(1); return 0; }
atta timp ct este posibil s utilizm n plat nc o moned din tipul respectiv ( nu se depete suma care trebuie pltit ) :
se incrementeaz nivelul ( se folosete o nou moned de tipul k ); suma pltit se majoreaz cu valoarea acelei monede; n cazul cnd a fost obinut o soluie ( s0 = S ), aceasta se tiprete, contrar se apeleaz funcia pentru nivelul urmtor, cu noua valoare s0.
# include <iostream.h> # include <conio.h> int sol[10],a[10],b[10],n,i,s; void tipar ( int k ){ cout <<endl<<" SOLUTIE : "<<endl; for ( i=1;i<=k;i++ ) if ( sol[i] ) cout<<sol[i]<<" bancnote de "<<a[i]<<endl; } void plata ( int k,int s0 ) { while ( (sol[k]<b[k] )&&( s0+a[k]<=s ) ) { sol[k]=sol[k]+1; if(sol[k]>0) s0+=a[k]; if(s0==s) tipar(k); else if(k<n)plata(k+1,s0); } sol[k]=-1; } int main(){ cout<<"Cate tipuri de bancnote avem ? ";cin>>n; cout<<"Suma ce trebuie platita : ";cin>>s;
for(i=1;i<=n;i++){ cout<<"Valoarea monedei de tipul "<<i<<" : ";cin>>a [i]; b[i]=s/a[i]; sol[i]=-1; } plata(1,0); }
return 0;
Problema calului
Fiind dat o tabl de ah de dimensiunea nxn i un cal n colul stnga sus al acesteia, se cere s se afieze toate posibilitile de mutare a calului astfel nct s treac o singur dat prin fiecare ptrat al tablei.
#include<iostream.h> #include<fstream.h> long st[100][100],vec[20][20],i,n; int valid(int k) { if((st[k][1]<1) || (st[k][1]>n) || (st[k][2]<1) || (st[k][2]>n)) return 0; for(i=1;i<k;i++) if ((st[i][1]==st[k][1])&&(st[i][2]==st[k][2])) return 0; return 1;} int solutie(int k) {return (k==n*n);} void tipar(int k) {for(i=1;i<=k;i++) cout<<st[i][1]<<" "<<st[i][2]<<" "; cout<<endl<<endl;}
void bkt(int k) { int val; for(val=1;val<=8;val++) {st[k][1]=st[k-1][1]+vec[val][1]; st[k][2]=st[k-1][2]+vec[val][2]; if(valid(k)) {if(solutie(k)) tipar(k); else bkt(k+1); } } } int main() {int j; fstream f ("datee.in",ios::in); for(i=1;i<=8;i++) for(j=1;j<=2;j++) f>>vec[i][j]; cin>>n; st[1][1]=1; st[1][2]=1; bkt(2); return 0; }