Sunteți pe pagina 1din 44

Tehnici de Programare

Backtracking

14.08.2017 Cezar PLESCA Backtracking 1


Problema celor 8 dame
Dispunerea a N (ex. N=8) dame pe o tabla de sah de
dimensiuni NxN astfel incat oricare 2 sa nu se atace.
O dama ataca pe orizontala,
verticala si diagonala.
Fiecare dama trebuie sa fie
plasata pe o linie distincta!
Solutia este un vector
= 1 , 2 , [1; ]
Spatiul solutiilor este imens!
1; 1; 1; = 1;

14.08.2017 Cezar PLESCA Backtracking 2


Idee de rezolvare
Constructie graduala a solutiei: dispunem prima dama
pe prima pozitie de pe prima linie; incercam apoi
variante pentru a doua dama pe a doua linie; daca
damele nu se ataca, cautam pozitii pentru a 3-a dama,
s.a.m.d.

Ce se intampla daca la un moment dat nu gasim solutii


pe o linie data ? Ne intoarcem pe linia anterioara si vom
considera urmatoarea pozitie pentru dama respectiva

14.08.2017 Cezar PLESCA Backtracking 3


Exemplu de rezolvare

14.08.2017 Cezar PLESCA Backtracking 4


Arborele de solutii ale problemei
Simplificare : tura in loc de dama. O solutie este un
drum dinspre radacina catre o frunza respectand
constrangerile problemei.

14.08.2017 Cezar PLESCA Backtracking 5


Parcurgerea arborelui de solutii
Drum = solutie subdrumul respecta constrangerile.
Parcurgere in adancime; daca subdrumul este
incorect, revenim pe nivelul superior si coboram iarasi!

14.08.2017 Cezar PLESCA Backtracking 6


Problema labirintului
Labirint codat printr-o matrice binara. Punct de
plecare, puncte de iesire; in fiecare moment exista 4
posibilitati de deplasare : stanga, dreapta, sus, jos
Solutia depinde de
sirul de mutari a carui
lungime poate fi oricat ...
Constrangeri : mutare
valida + drumul sa nu
treaca de doua ori prin
aceeasi celula !
14.08.2017 Cezar PLESCA Backtracking 7
Backtracking. Notiuni generale
Trebuie luate o serie de decizii in cadrul unei solutii:
= 1 , 2 , (finita)
Exista o serie de constrangeri pe care trebuie sa le
indeplineasca componentele ale solutiei!
Backtracking : construirea progresiva a solutiei prin
extinderea unei solutii partiale = 1 , 2
Pentru fiecare +1 din +1 verificam daca respecta
constrangerile problemei si mergem mai departe
Cand nu mai putem avansa, ne intoarcem pe urmele
solutiei pe care am venit si incercam altceva
14.08.2017 Cezar PLESCA Backtracking 8
Cautarea progresiva a unei solutii

dead end
?
dead end
dead end

?
start ? ?
dead end

dead end

success!

14.08.2017 Cezar PLESCA Backtracking 9


Pseudocod pentru backtracking
Pseudocod pentru determinarea tuturor solutiilor:
void backtracking(1 , 2 , )
daca (1 , 2 , ) este solutie
return
pentru fiecare +1 din +1
daca (1 , 2 , +1 ) este ok
backtracking(1 , 2 , +1 )
end
end

14.08.2017 Cezar PLESCA Backtracking 10


Pseudocod pentru backtracking
Pseudocod pentru determinarea primei solutii:
bool backtracking(1 , 2 , )
daca (1 , 2 , ) este solutie
return true;
pentru fiecare +1 din +1
daca (1 , 2 , +1 ) este ok
bool sol=backtracking(1 , 2 , +1 )
daca sol=true
return true;
end
return false;
end
14.08.2017 Cezar PLESCA Backtracking 11
Backtracking nerecursiv o solutie
bool backtracking() // elementele memorate pe o stiva
bool forward=TRUE;
stack* S=NULL; k=0;
executa{
daca forward este TRUE
daca S este solutie // + alte variabile
return true
x=primul element din
altfel
x=pop(S); k--;
x=urmatorul element din dupa x
end

14.08.2017 Cezar PLESCA Backtracking 12


Backtracking nerecursiv o solutie
forward=FALSE
pentru fiecare din incepand cu x
daca S { } este ok
push(S, ); k++
forward=TRUE
break
end
end
} cat timp SNULL // sau k==0 (conditii echivalente)
return false
end

14.08.2017 Cezar PLESCA Backtracking 13


Implementarea damelor recursiv
#define N 8
int D[N]; // dama i se afla pe linia i si coloana D[i]
bool dame(int k){
if(k==N){ // afisarea solutiei descoperite
for(int i=0;i<N;i++) printf("%d\t",D[i]);
return true;
}
for(int i=0;i<N;i++){
D[k]=i; // alegerea unei pozitii pentru dama k
if(verifica(k))
if(dame(k+1)) return true;
}
return false;
}

14.08.2017 Cezar PLESCA Backtracking 14


Implementarea damelor recursiv
bool verifica(int k){ // verifica D[k] cu damele anterioare
for(int i=0;i<k;i++){ // atac pe verticala sau diagonala
if(D[i]==D[k] || abs(D[i]-D[k])==abs(i-k))
return false;
}
return true;
}

void main(){
dame(0);
}

14.08.2017 Cezar PLESCA Backtracking 15


Implementarea damelor nerecursiv
bool damestack(){ // elementele momemorate pe o stiva
bool forward=true; stack* S=NULL; int k=0,x;
do{
if(forward){
if(k==N) // solutie +afisare
return true;
x=0; // primul element de pe linia k
}
else{
k--; x=pop(S); // intoarcere 1 nivel
x++; // urmatorul element de pe linia k
}
14.08.2017 Cezar PLESCA Backtracking 16
Implementarea damelor nerecursiv
forward=false;
for(int xk=x;xk<N;xk++){
D[k]=xk;
if(verifica(k)){
push(S,xk);k++;
forward=true; break;
}
}
}while(S!=NULL); // sau k==0 (conditii echivalente)
printf("Nici o solutie!\n");
return false;
}
14.08.2017 Cezar PLESCA Backtracking 17
Aranjamente, Combinari

14.08.2017 Cezar PLESCA Backtracking 18


Generarea aranjamentelor (n,k)
Aranjamente de 4 luate cate 2 : (1,2), (1,3), (1,4),
(2,1), (2,3), (2,4), (3,1), (3,2), (3,4), (4,1), (4,2), (4,3)

Aranjamente de n luate cate k : toate submultimile de


k elemente : 1 , 2 , , , {1,2 }

Problema adaptata pentru backtracking: construirea


progresiva a unui aranjament (1, 2, k elemente).

Constrangere : la fiecare adaugare numerele sa fie !


14.08.2017 Cezar PLESCA Backtracking 19
Implementare aranjamentelor (n,k)
void aranjamente(int n,int k,int* x,int i){ // i pozitia curenta
if(i==k){//solutie, am generat deja k elemente
for(int j=0;j<k;j++)
printf("%d\t",x[j]);
printf("\n"); return;
}
for(int j=1;j<=n;j++){ // multimea de valori posibile
x[i]=j; // adaugarea unui nou element
if(verifica(x,i))
aranjamente(n,k,x,i+1); // avansare
}
}
14.08.2017 Cezar PLESCA Backtracking 20
Implementare aranjamentelor (n,k)
bool verifica(int* x,int i){ // elementul nou sa fie distinct
for(int j=0;j<i;j++){
if(x[j]==x[i])
return false;
}
return true;
}
void main(){
int n=4,k=2;
int* X=(int*)malloc(k*sizeof(int));
aranjamente(n,k,X,0); // plecare de pe pozitia 0
}
14.08.2017 Cezar PLESCA Backtracking 21
Generarea combinarilor (n,k)
Combinari de 4 luate cate 2 :
(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)

Combinari de n luate cate k : toate submultimile de


k elemente : 1 , 2 , , < , {1,2 }

Problema adaptata pentru backtracking: construirea


progresiva a unei combinari (1, 2, k elemente).

Constrangere : la fiecare adaugare, multime ordonata!


14.08.2017 Cezar PLESCA Backtracking 22
Implementarea combinarilor (n,k)
void combinari(int n,int k,int* x,int i){
if(i==k){//solutie
for(int j=0;j<k;j++)
printf("%d\t",x[j]);
printf("\n"); return;
}
int start=1;
if(i>0) start=x[i-1]+1; // stim de unde sa incepem !
for(int j=start;j<=n;j++){
x[i]=j; combinari(n,k,x,i+1);
}
}
14.08.2017 Cezar PLESCA Backtracking 23
Generarea permutarilor unei multimi
Permutarile multimii {1,2,3}:
{1,2,3}, {1,3,2}, {2,1,3}, {2,3,1}, {3,1,2}, {3,2,1}

Permutarile unei multimi de n elemente : multimile de


forma : 1 , 2 , , , {1,2 }

Problema adaptata pentru backtracking: construirea


progresiva a unei permutari (1, 2, n elemente).

Constrangere : la fiecare adaugare, elemente distincte!


14.08.2017 Cezar PLESCA Backtracking 24
Implementarea permutarilor
void permutari(int n,int* x,int i){ // i pozitia curenta
if(i==n){//solutie
for(int j=0;j<n;j++)
printf("%d\t",x[j]);
printf("\n"); return;
}
for(int j=1;j<=n;j++){
x[i]=j;
if(verifica(x,i)) // aceeasi functie de verificare
permutari(n,x,i+1);
}
}
14.08.2017 Cezar PLESCA Backtracking 25
Generarea tuturor submultimilor
Submultimile multimii {1,2,3}:
, {1}, {2}, {3}, {1,2}, {1,3}, {1,2,3}

Submultimile unei multimi de n elemente : multimile

1 , 2 , , < , 1,2 , 0

Problema adaptata pentru backtracking: construirea


progresiva a unei submultimi (1, 2, k elemente).
Constrangere : la fiecare adaugare, elemente ordonate!

14.08.2017 Cezar PLESCA Backtracking 26


Implementarea submultimilor
void submultimi(int n,int* x,int i){
if(i<=n){//solutie
for(int j=0;j<i;j++) printf("%d\t",x[j]);
printf("\n"); // fara return!
}
int start=1;
if(i>0) start=x[i-1]+1;
for(int j=start;j<=n;j++){
x[i]=j;
submultimi(n,x,i+1);
}
}
14.08.2017 Cezar PLESCA Backtracking 27
Probleme Backtracking

14.08.2017 Cezar PLESCA Backtracking 28


Puzzle cu numere
Obiectiv : aducerea numerelor in ordine crescatoare
de sus in jos prin deplasari ale celulelor din
vecinatatea celulei libere!
Set de deplasari cunoscute :
stanga, dreapta, sus, jos.

Constrangeri :nerepetarea
aceleasi configuratii de joc!
Problema de cautare in
spatiul configuratiilor!

14.08.2017 Cezar PLESCA Backtracking 29


Problema labirintului
Labirint codat printr-o matrice binara. Punct de
plecare, puncte de iesire; in fiecare moment exista 4
posibilitati de deplasare : stanga, dreapta, sus, jos
Solutia depinde de
sirul de mutari a carui
lungime poate fi oricat ...
Constrangeri : mutare
valida + drumul sa nu
treaca de doua ori prin
aceeasi celula !
14.08.2017 Cezar PLESCA Backtracking 30
Iesirea dintr-un labirint - I
#define N 5
// definirea labirintului
int A[N][N]={
{0,0,0,1,0},
{1,0,1,1,0},
{0,0,1,0,0},
{0,0,0,0,1},
{0,0,1,0,0}
};
int M[4][2]={
{0,1},{-1,0},{0,-1},{1,0}
}; // codarea deplasarilor pe x si y : 0-DREAPTA, 1-SUS, etc.
14.08.2017 Cezar PLESCA Backtracking 31
Iesirea dintr-un labirint - II
int V[N][N]; // matricea de celule deja vizitate
int xend=N-1, yend=N-1; // punctul de iesire
bool labirint(int* path,int l,int x,int y){
// testarea iesirii din labirint
if(x==xend && y==yend){
for(int i=0;i<l;i++)
printf("%d\t",path[i]);
return true;
}
V[x][y]=true;

}
14.08.2017 Cezar PLESCA Backtracking 32
Iesirea dintr-un labirint - III
bool labirint(int* path,int l,int x,int y){

for(int move=0;move<4;move++){
path[l]=move;
if(verify(x,y,move)){
if(labirint(path,l+1,
x+M[move][0],y+M[move][1]))
return true;
}
}
return false;
}
14.08.2017 Cezar PLESCA Backtracking 33
Iesirea dintr-un labirint - IV
bool verify(int x,int y,int move){
int xn=x+M[move][0], yn=y+M[move][1];
if(0<=xn && xn<N && 0<=yn && yn<N){
if(A[xn][yn]==0 && V[xn][yn]==false)
return true;
}
return false;
}
void main(){
int path[N*N], l=0;
if(!labirint(path,0,0,0)) printf("Nu exista solutie!\n");
}
14.08.2017 Cezar PLESCA Backtracking 34
Drum sau ciclu hamiltonian
Fiind dat un graf si doua noduri u si v (ex. 1 si 5), gasiti
un drum hamiltonian intre u si v. Intr-un drum
hamiltonian fiecare nod este vizitat o singura data.

14.08.2017 Cezar PLESCA Backtracking 35


Colorarea unei harti cu k culori
State vecine culori distincte. Modelarea hartii sub
forma unui graf (state vecine noduri adiacente)

14.08.2017 Cezar PLESCA Backtracking 36


Colorarea grafului cu k-culori
Fiind dat un graf si k culori, sa
se coloreze nodurile asa incat
sa nu existe 2 noduri vecine de
aceeasi culoare.

La fiecare pas, set de culori


cunoscute! Constrangerile
sunt date de colorarea
nodurilor vecine !
Colorarea cu k culori: (2 ).

14.08.2017 Cezar PLESCA Backtracking 37


Colorarea unui graf cu k culori - I
#define N 5
// matricea de adiacenta a grafului
int A[N][N];
bool colorare(int* colors,int k,int n,int nbColors){
if(k==n){
for(int i=0;i<n;i++)
printf("%d\t",colors[i]);
return true;
}
.
}

14.08.2017 Cezar PLESCA Backtracking 38


Colorarea unui graf cu k culori - II
bool colorare(int* colors,int k,int n,int nbColors){

for(int i=0;i<nbColors;i++){
colors[k]=i;
if(verify(colors,k)){
bool ok=colorare(colors,k+1,n,nbColors);
if(ok)
return true;
}
}
return false;
}
14.08.2017 Cezar PLESCA Backtracking 39
Colorarea unui graf cu k culori - III
bool verify(int* colors,int k){
for(int i=0;i<k;i++){
if(A[i][k]==1 && colors[i]==colors[k])
return false;
}
return true;
}
void main(){
int C[N], nbColors=5;
if(!colorare(C,0,N,nbColors))
printf("Nu exista solutie de colorare!\n");
}
14.08.2017 Cezar PLESCA Backtracking 40
Submultime de suma data
Fiind data o multime S de valori naturale 1 , 2 si
un numar t, sa se gaseasca submultimea de suma t.

Exemplu : daca S={8, 11, 26, 29, 37} si t=37.


Solutii posibile sunt: {8, 29}, {11, 26}, {37}.

La fiecare pas, set de alegeri cunoscute (unul din


elementele ramase) !
Constrangerile sunt date de elemente diferite in cadrul
multimii (posibil ordonate) si nedepasirea sumei date!

14.08.2017 Cezar PLESCA Backtracking 41


Submultime de suma data - I
int sum=37, S[5]={8,11,26,29,37};
void subsuma(int n,int* index,int i,int sumcrt){
if(sumcrt==sum){//solutie
for(int j=0;j<i;j++)
printf("%d\t",S[index[j]]);
printf("%d\n);
return;
}
int start=0;
if(i>0) start=index[i-1]+1;
.
}
14.08.2017 Cezar PLESCA Backtracking 42
Submultime de suma data - II
void subsuma(int n,int* index,int i,int sumcrt){

for(int j=start;j<n;j++){
index[i]=j;
if(sumcrt+S[j]<=sum)
subsuma(n,index,i+1,sumcrt+S[j]);
}
}
void main(){
int sol[5]; //indecsii elementelor alese
subsuma(5,sol,0,0);
}
14.08.2017 Cezar PLESCA Backtracking 43
Problema comis-voiajorului
Fiind dat un graf de orase interconectate intre ele,
impreuna cu distantele intre acestea, in ce ordine
trebuiesc vizitate pentru a se parcurge drumul minim ?

La fiecare pas, set de


alegeri cunoscute
(orasele ramase) !

Constrangerile :
orase diferite si
nedepasirea minimului!
14.08.2017 Cezar PLESCA Backtracking 44

S-ar putea să vă placă și