Sunteți pe pagina 1din 14

Metoda Backtracking

Metoda backtracking se aplic algoritmilor pentru rezolvarea urmtoarelor tipuri de


probleme:
Fiind date n mulimi S1, S2, ... Sn, fiecare avnd un numr nrsi de elemente, se cere gsirea
elementelor vectorului X =(x1, x2, ... xn) S=S1xS2xSn, astfel nct s fie ndeplinit o anumit
relaie (x1, x2, ,xn) ntre elementele sale.
Relaia (x1, x2, ,xn) se numete relaie intern (condiie intern), mulimea S=S1xS2xSn
se numete spaiul soluiilor posibile, iar vectorul X se numete soluia rezultat.
Metoda backtracking determin toate soluiile rezultat ale problemei. Dintre acestea se
poate alege una care ndeplinete n plus o alt condiie.
Aceast metod se folosete n rezolvarea problemelor care ndeplinesc simultan
urmtoarele condiii:
- mulimile S1, S2, ... Sn sunt mulimi finite, iar elementele lor se consider c se afl ntr-o
relaie de ordine bine stabilit (de regul sunt termenii unei progresii aritmetice);
- nu se dispune de o alt metod de rezolvare, mai rapid;
- x1, x2, , xn pot fi la rndul lor vectori;
- S1, S2, ... Sn pot fi identice.
n

Metoda backtracking elimin generarea tuturor celor

nr s
i 1

posibiliti din spaiul soluiilor

posibile (adic a produsului cartezian al celor n mulimi). n acest scop la generarea vectorului X,
se respect urmtoarele condiii:
a)

xk primete valori numai dac x1, x2, ... ,xk-1 au primit deja valori;

b)

dup ce se atribuie o valoare lui x k, se verific relaia (condiia) numit de continuare

(x1, x2, ,xk) care stabilete situaia n care are sens s se treac la calculul lui x k+1.
Nendeplinirea condiiei ` exprim faptul c oricum am alege x k+1, xk+2, ... ,xn nu se ajunge
la soluia rezultat.
n caz de nendeplinire a condiiei `(x1, x2, ,xk), se alege o nou valoare pentru xk Sk
dintre cele nealese, i se reia verificarea condiiei `.
Dac mulimea de valori xk s-a epuizat, se revine la alegerea altei valori pentru x k-1 dintre
cele nealese .a.m.d. Aceast micorare a lui k d numele metodei, ilustrnd faptul c atunci cnd
nu se poate avansa se urmrete napoi secvena curent din soluia posibil. ntre condiia intern
i cea de continuare exist o strns legtur. Generarea soluiilor se termin dup ce au fost
testate toate valorile din S1.

Stabilirea optim a condiiilor de continuare reduce mult numrul de calcule.


Observaie: metoda Backtracking are ca rezultat obinerea tuturor soluiilor problemei. n
cazul n care se cere o sigur soluie se poate fora oprirea, atunci cnd a fost gsit.
orice soluie se genereaz sub form de vector.
Vom considera c generarea soluiilor se face intr-o stiv. Astfel, x1 S1 se va gsi pe
primul nivel al stivei, x2 S2 se va gsi pe al doilea nivel al stivei,... x k Sk se va gsi pe nivelul k al
stivei:
Stiva

...

1. pentru generarea permutrilor mulimii {1,2.....n}, orice nivel al


stivei va lua valori de la 1 la n.

xk Sk

2. odat ales un element, se verific condiiile de continuare (altfel


spus, se verific dac elementul este valid).

3. dac k=n+1, atunci s-a obinut o soluie posibil care poate fi si

x2 S2
x1 S1

soluie rezultat n funcie de condiiile problemei.


4. Observaie: Problemele rezolvate cu aceast metod necesit un
timp ndelungat. Din acest motiv, este bine s utilizm metoda
numai atunci cnd nu avem la dispoziie un alt algoritm mai
eficient.

Sub form recursiv, algoritmul backtracking poate fi redat astfel:


#define nmax ...//numrul maxim de mulimi
..../* se consider declarate global vectorii care mulimile Si i numrul lor de elemente nrsi */
int x[nmax],n,k,nrs[nmax];
void citire(){.}
void afisare(){.}
int valid(int k){ return ((x[1], x[2], ..., x[k])==1);}
int soluie(int k){.}//de exemplu {return k==n+1}
void backtracking_recursiv(int k)
{if(soluie(k)) afisare(x,n); /* afiarea sau eventual prelucrarea soluiei rezultat */
else { int i;
for (i=1;i<=nrs[k];i++)
{x[k]=Sk[i];
/* al i-lea element din mullimea Sk */
if (valid(k))backtracking_recursiv(k+1);
}
}
}

int main(){ citire(); backtracking_recursiv(1); return 0;}

Problemele care se rezolv prin metoda backtracking pot fi mprite n mai multe grupuri
de probleme cu rezolvri asemntoare, in funcie de modificrile pe care le vom face n algoritm.
Principalele grupuri de probleme sunt:
G1) probleme n care vectorul soluie are lungime fix i fiecare element apare o singur dat n
soluie;
G2) probleme n care vectorul soluie are lungime variabil i fiecare element poate s apar de
mai multe ori n soluie;
G3) probleme n plan, atunci cnd spaiul n care ne deplasm este un tablou bidimensional
(backtracking generalizat).

Cele mai cunoscute probleme din G1 sunt:


1.

Generarea produsului cartezian a n mulimi

Se consider n mulimi finite S1, S2, ... Sn, de forma {1,2..,sn}. S se genereze produsul cartezian
al acestor mulimi.
Indicaii de rezolvare:
Am considerat mulimile de forma {1,2.., sn} pentru a simplifica problema, n special la partea de
citire si afiare, algoritmul de generare rmnnd nemodificat.
Identificm urmtoarele particulariti i condiii:

vectorul soluie: X=(x1, x2, ... xn) S1xS2xSn,


Fiecare element Xk Sk
Nu exist condiii interne pentru vectorul soluie. Nu are funcie de validare.
Obinem soluia cnd s-au generat n valori.
Avem soluie dac k=n+1

//generare prod cartezian


#include <iostream>
#include <fstream>
using namespace std;
int x[50], n,k,s[50][100], card[50];
ifstream f("fis.in");
void citeste()
{ int i,j; f>>n;
for(i=1;i<=n;i++)
{
f>>card[i];
for(j=1;j<=card[i];j++) f>>s[i][j];}}
void scrie()
{
int i;cout<<endl;
for(i=1;i<=n;i++) cout<<s[i][x[i]]<<" ";}
int solutie(int k) { return(k==n+1);}
void back(int k)
{
if(solutie(k))scrie();
else
for(int i=1;i<=card[k];i++)
{x[k]=i;
// if(valid(k)) nu este cazul
back(k+1);}
}
int main()
{citeste(); back(1); return 0;}

2. Generarea submulimilor unei mulimi


Generarea submulimilor unei mulimi A cu n elemente se poate face cu ajutorul algoritmului de
generare a combinrilor, apelndu-l repetat cu valorile 1, 2, ..., n pentru a genera submulimile cu
un element, apoi cele cu dou elemente, apoi cu 3 elemente etc.
Aceast modalitate de rezolvare este i mai complicat i mai puin eficient dect urmtoarea,
care se bazeaz pe generarea produsului cartezian {0,1}x{0,1}x{0,1} de n ori. Aceast a doua
metod este eficient deoarece genereaz 2 n soluii (=nr. de submulimi ale unei mulimi cu n
elemente). Fiecare element al produsului cartezian reprezint cte un vector caracteristic al unei
submulimi din A.
Aadar, generm toi vectorii caracteristici x cu n elemente, cu valorile 0 i 1. Pentru fiecare vector
soluie parcurgem soluia i afim elementele din mulimea A crora le corespund valorile 1 n x.
Astfel, pentru combinaia 001011 vom afia elementele de pe poziiile 3, 5 i 6 din mulimea
iniial.

3. Generarea permutrilor unei mulimi


Se d o mulime cu n elemente A={a1,a2,,an}. Se cere s se genereze si s se afieze toate
permutrile ei. Altfel spus, se cere s se afieze toate modurile n care se pot aeza elementele
mulimii A.
Folosim pentru generare mulimea S={1,2,,n}. Spaiul soluiilor posibile este S n. Un vector XSn
este o solutie rezultat dac x[i]x[j] si x[i]{1,2,...,n} (condiile interne). La pasul k:

x[k] {1,2,,n};
valid (k) : x[k]x[1],x[2],...,x[k-1]
solutie(k) : k=n+1.

Se pot identifica mai multe modaliti de a verifica dac elementul x[k] a fost plasat deja n
vectorul soluie. Cele mai importante dou sunt:

parcurgerea elementelor deja generate pentru a verifica daca x[k] apare sau nu ntre ele;
folosirea unui vector cu n elemente n care vom avea valori 0 sau 1 corespunztoare
elementelor mulimii iniiale. Valoarea 1 va preciza faptul c elementul de pe poziia
corespunztoare a fost plasat anterior n vectorul soluie, iar valoarea 0 c nu.

Corespunztor acestor dou moduri de a verifica dac un element a mai fost sau nu plasat n
vectorul soluie, avem 2 moduri de generare a permutrilor.
4. Generarea aranjamentelor
Generm aranjamentele unei mulimi atunci cnd ni se cer toate modurile de a alege m elemente
distincte dintre cele n ale mulimii (m<n).
Aceast problem se rezolv foarte uor folosind metodele de generarea permutrilor. Singurele
modificri presupun citirea numrului m, modificarea condiiei de soluie, care va fi k=m n loc de
k=n i a numrului de elemente afiate.
Folosim pentru generare mulimea S={1,2,,n}. Spaiul soluiilor posibile este S m. Un vector XSm
este o solutie rezultat dac x[i]x[j] si x[i]{1,2,...,n} (condiile interne). La pasul k:

x[k] {1,2,,n};
valid (k) : x[k]x[1],x[2],...,x[k-1]
soluie(k) : k=m+1.

5. Generarea submulimilor cu m elemente ale unei mulimi (combinri)


Generm combinrilor unei mulimi presupune o condiie suplimentar fa de permutri sau
aranjamente. Acest lucru se datoreaz faptului c generarea combinrilor presupune alegerea n
ordine
strict
cresctoare
a
elementelor
care
compun
vectorul
soluie.
Astfel, condiia de continuare, sau de validare a unui element este aceea c el trebuie s fie strict
mai mare dect cel plasat anterior. n acest mod asigurm faptul c elementele nu se vor repeta i
c vor fi generate n ordine strict cresctoare. Trebuie, ns, s avem grij s nu punem aceast
condiie si asupra primului element din vectorul soluie, deoarece el nu are cu cine s fie comparat
sau s iniializm X[0]=0.
O optimizare a algoritmului de generare a combinrilor se poate obine pornind instruciunea for
pentru plasarea unui element de la valoare urmtoare valorii generate anterior.Astfel nu mai
trebuie s verificm dac elementul Xk este mai mare ca Xk-1.
Folosim pentru generare mulimea S={1,2,,n}. Spaiul soluiilor posibile este S m. Un vector XSm
este o solutie rezultat dac x[i]x[j] si x[i]{1,2,...,n} (condiiile interne). La pasul k:

x[0]=0
x[k] { x[k-1]+1,..,n }; //optim x[k] { x[k-1]+1,..,n-m+k}
valid (k) : fiecare valoare aleas x[k] este valid; nu necesita validare
soluie(k) : k=m+1.

6. Aranjarea a n regine pe o tabl de ah de dimensiune nxn fr ca ele s se atace.


Dndu-se o tabl de ah de dimensiune nxn (n>3) s se aranjeze pe ea n regine fr ca ele s se
atace. Reamintim c o regin atac linia, coloana i cele 2 diagonale pe care se afl. n figura de
mai jos celulele colorare mai nchis sunt atacate de regina poziionat unde indic litera D.

Se plaseaza cte o regin pe fiecare linie.


Condiia de a putea plasa o regin pe poziia k presupune verificarea ca s nu se atace cu nici una
dintre celelalte k-1 regine deja plasate pe tabla. Dac pe poziia k din vectorul X punem o valoare
ea va reprezenta coloana pe care se plaseaz pe tabl regina k.
x[k] { 1,2,..,n };

Validare(k): x[i]x[k] i |k-i||x[k]-x[i]| cu i=2,...,k-1.

Soluie: k=n+1

7. Generarea tuturor secvenelor de n (par) paranteze care se nchid corect.


S se genereze toate irurile de n parateze rotunde nchise corect.
Exemplu:

Pt n=4: (()) ; ()()


Pt n=6: ((())) ; ()(()) ; ()()() ; (()()) ; (())()

Notam cu 1 paranteza stnga si cu 2 paranteza dreapt. Un vectorul soluie va fi de forma:


x=(1,1,2,2), adic (()).
Vom retine in variabila ps numrul de paranteze stngi folosite i n variabila pd numrul de
parateze drepte folosite. Identificm urmtoarele particulariti i condiii:

S={1,2}
vectorul soluie: X=(x1, x2, ... xn) Sn
x[1]=1; x[n]=2
Fiecare element Xk {1,2}
valid(k): ps<=n/2 i ps>=pd
Obinem soluia dac k=n+1 i ps=pd

8. Generarea partitiilor unei mulimi.


Se consider multimea {1,2,,n}. Se cer toate partiiile acestei mulimi. Submulimile A1,A2,,Ak
ale mulimii A constituie o partiie a acesteia dac sunt disjuncte ntre ele (nu au elemente comune)
i mulimea rezultat n urma reuniunii lor este A.
Exemplu:
Pentru A={1,2,3} avem:
{1}, {2,3}
{1,2}, {3}
{1}, {2}, {3}
5 partitii
{1,2,3}
{1,3}, {2}
O partitie a mulimii {1,2,3,,n} se poate reprezenta sub forma unui vector x cu n
componente x[i]=k are semnificatia ca elementul i al mulimii considerate aparine submulimii k a
partiiei.
O solutie este x={1,2,3} care reprezint partiia {1}, {2,3} format din submulimile A1={1} i
A2={2,3}.

X[1]=1 semnifica faptul ca 1 A1


X[2]=2 semnifica faptul ca 2 A2
X[3]=2 semnifica faptul ca 3 A2

Submultimile unei partitii se numeroteaza cu numere consecutive. Pentru orice i din {1,2,,n} trebuie
sa existe un j din aceeasi multime astfel incat |x[i]-x[j]|<=1. Nu putem avea ca solutie x(1,1,1,3) pentru
ca partiia obinut nu are 3 submultimi, insa putem avea x=(1,2,1,3).

O partitie a unei multimi cu n elemente este format din cel mult n multimi distincte (de ex
{1},{2},{3} partitie a lui {1,2,3}) => S={1,2,3,,n}
vectorul soluie: X=(x1, x2, ... xn) Sn
Pentru a evita repetitia partitiilor (de ex. {1}, {2,3} cu {2,3}, {1}) , facem conventia ca x[k] sa ia
numai valori din multimea 1,2,3,,max=maxim(x[1],x[2],,x[k-1])+1. Deci Xk {1,2,...,max}
orice valoare x[k] este valid
Obinem soluia dac k=n+1

//partitiile unei multimi


#include <iostream>
#include <fstream>
using namespace std;
int x[50],a[50], n, nrsol;
void citeste()
{ifstream f("fis.in");
f>>n;
int i;
for(i=1;i<=n;i++)f>>a[i];
}
int maxim(int k)
{int i,z=0;
for(i=1;i<k;i++)
z=max(x[i],z);
return z;
}
void scrie()
{ int i,z,j;
nrsol++;
cout<<endl<<"-------------------\n";
cout<<endl<<"Solutia "<<nrsol<<endl;
cout<<endl;
z=maxim(n+1);
for(i=1;i<=z;i++)
{cout<<"{ ";
for(j=1;j<=n;j++)
if(x[j]==i)cout<<a[j]<<" ";
cout<<"} ";
}}
void back(int k)
{ if(k==n+1)scrie();
else
for(int i=1; i<=maxim(k)+1;i++)
{ x[k]=i;
back(k+1);}
}
int main()
{ citeste();
x[1]=1;
back(2);
return 0;}

9. Colorarea rilor de pe o hart astfel nct oricare dou ri vecine s aib culori diferite
Fiind dat o hart cu n ri, se cer toate soluiile de colorare a hrii, utiliznd cel mult 4 culori,
astfel nct dou tri cu frontiera comun s fie colorate diferit. Este demonstrat faptul c sunt
suficiente numai 4 culori pentru ca orice hart s poat fi colorat.
Harta este furnizat programului cu ajutorul unei matrice A cu n linii i n coloane .
( 1, daca ara i se nvecineaz cu ara j;
A(i,j) =(
( 0, in caz contrar.
Matricea A este simetric. Pentru rezolvarea problemei se utilizeaza vectorul stiv x, unde
nivelul k al acestuia simbolizeaz ara k, iar x[k] culoarea ataat rii k. Stiva are nlimea n i pe
fiecare nivel ia valori ntre 1 si 4

S={1,2,3,4}
vectorul soluie: X=(x1, x2, ... xn) Sn
fiecare element x[k] {1,2,3,4}
valid(k): A[i][k]=1 i x[i]x[k], i=1,2,...,k-1
Obinem soluia dac k=n+1

//colorarea hartilor
#include <iostream>
#include <fstream>
using namespace std;
int x[50],a[50][50];
int n, nrsol;
void citeste()
{ifstream f("fis.in");
f>>n;
int i,j;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
f>>a[i][j];
}
void scrie()
{ int i;
nrsol++;
cout<<endl<<"-------------------\n";
cout<<endl<<"Solutia "<<nrsol<<endl;
for(i=1;i<=n;i++)
cout<<i<<" ";
cout<<endl;

0
1
0
1
0

1
0
1
1
1

for(i=1;i<=n;i++)
cout<<x[i]<<" ";
}
int valid(int k)
{
int i;
for(i=1;i<k;i++)
if(a[i][k]==1 &&
x[i]==x[k])return 0;
return 1;
}
void back(int k)
{ if(k==n+1)scrie();
else
for(int i=1;i<=4;i++)
{ x[k]=i;
if(valid(k))back(k+1);}
}
int main()
{ citeste(); back(1);
return 0;
}

0
1
0
0
1

1
1
0
0
1

0
1
1
1
0

10. Problema comisului voiajor


Un comis voiajor trebuie s viziteze un numr n de orae. Iniial, el se afl ntr-unul dintre
ele, notat 1. Comisul voiajor dorete s nu treac de dou ori prin acelai ora, iar la ntoarcere s
revin n oraul 1. Cunoscnd legturile existente ntre orae, se cere s se afieze toate
variantele de deplasare posibile pe care le poate urma comisul voiajor.
Exemplu:
In figura de mai jos sunt simbolizate cele 6 orase, precum si drumurile existente intre ele.
Comisul voiajor are urmatoarele posibilitati de parcurgere:

1, 2, 3, 4, 5, 6, 1;
1, 2, 5, 4, 3, 6, 1;
1, 6, 3, 4, 5, 2, 1;
1, 6, 5, 4, 3, 2, 1;

Legturile existente ntre orae sunt date n matricea A cu n linii i n coloane. Elementele
matricei A pot fi 0 sau 1 (matricea este binar).
1, daca exista drum intre orasele i si j
A(i,j) =
0, in caz contrar.
Se observ ca A[i,j] = A[j,i] oricare ar fi i,j{1, 2, 3, , n} matricea este simetric.
Pentru rezolvarea problemei folosim stiva X. La baza stivei (nivelul 1) se incarc numrul 1, deci
x[1]=1. Problema se reduce la a genera toi vectorii X=(x1,x2,...,xn) cu prop:
x[1]=1
x[i]{2,3,4,,n}
x[i]x[j], pentru ij
a[x1][x2]=1, a[x2][x3]=1, a[xn-1][xn]=1, a[xn][1]=1
Vom utiliza un algoritm asemntor generrii permutrilor, pornind de la pasul k=2:
S={2,3,.,n}
x[1]=1
vectorul soluie: X=(x1, x2, ... xn) Sn
x[k] = 1,2,...,n
valid(k): A[x[k-1]][x[k]]=1 i x[i]x[k], i=1,2,...,k-1
obinem soluia dac k=n+1 i A[x[k-1]][x[k]]=1

G2) probleme n care vectorul soluie are lungime variabil i fiecare element poate s apar de
mai multe ori n soluie;
11. Partiiile unui numr natural. Fie n>0, natural. S se scrie un program care s afieze toate
partiiile unui numr natural n. Numim partiie a unui numr natural nenul n o mulime de
numere naturale nenule {p1, p2, , pk} care ndeplinesc condiia p1+p2+ +pk = n.
Ex: pt n = 4 programul va afia:
4 = 1+1+1+1
4 = 1+1+2
4 = 1+3
4 = 2+2
4=4
Observaii:

lungimea vectorului soluie X cel mult n;

exist posibilitatea ca soluiile s se repete;


condiia de final este ndeplinit atunci cnd suma elementelor vectorului soluie este n.

Am menionat mai sus c vom folosi doi parametri, unul pentru poziia n vectorul soluie i un al
doilea n care avem sumele pariale la fiecare moment. Avem determinat o soluie atunci cnd
valoarea celui de-al doilea parametru este egal cu n.
n aceast situaie la fiecare plasare a unei valori n vectorul X valoarea celui de al doilea
parametru se mrete cu elementul ce se plaseaz n vectorul soluie. Apelul procedurii back din
programul principal va fi back(1, 0). Exist i posibilitatea de a apela procedura back din programul
principal back(1, n) i valoarea celui de al doilea parametru se decrementeaz cu valoarea
elementului ce se plaseaz n vectorul X, iar o soluie avem cnd acest parametru este zero.
Indiferent care modalitate este aleas acest al doilea parametru ne permite s optimizm puin
programul n sensul c putem considera nite condiii de continuare mai strnse.
//generare partitiile unui numar
#include <iostream>
using namespace std;

//varianta II
#include <iostream>
using namespace std;

int x[100], n,k,nrsol;


void citeste()
{cin>>n;}

int x[100], n,k,nrsol;


void citeste()
{cin>>n;}

void scrie(int k)
{ int i; cout<<endl;nrsol++;
for(i=1;i<k;i++) cout<<x[i]<<" ";}

void scrie(int k)
{ int i; cout<<endl;nrsol++;
for(i=1;i<k;i++) cout<<x[i]<<" ";}

void back(int k, int sum)


{ if(sum==0)scrie(k);
else
for(int i=x[k-1];i<=n && i<=sum;i++)
//i>=x[k-1]pt a evita repetiile
{x[k]=i; back(k+1, sum-i);}
}
int main()
{citeste(); x[0]=1; back(1,n);
cout<<endl<<"nr.solutii="<<nrsol;
return 0;}

void back(int k, int sum)


{ if( sum==n)scrie(k);
else
for(int i=x[k-1];sum+i<=n;i++)
{x[k]=i; back(k+1, sum+i);}
}
int main()
{citeste(); x[0]=1; back(1,0);
cout<<endl<<"nr.solutii="<<nrsol;
return 0;}

12. Plata unei sume cu monede de valori date


Scriei un program care s afieze toate modalitile prin care se poate plti o sum S folosind n bancnote
de valori b1<b2<b3<<bn. Se presupune c avem la dispoziie oricte bancnote din fiecare tip.
Numerele n i S, precum i valorile bancnotelor se citesc de la tastatur, iar modalitile de plat vor fi
scrise n fiierul bani.out.
Indicatie. Se construiete vectorul y cu numrul maxim de bancnote: y[i]=S/b[i] din fiecare tip
Varianta 1. Vectorul soluie x va avea n componente.
x[k]{0,1,2,,y[k]} are semnificatia: se folosesc x[k] bancnote de tipul b[k]
valid(k) : suma(x[1]*b[1]+x[2]*b[2]++b[k]*x[k])<=S
solutie: k=n+1 si suma(x[1]*b[1]+x[2]*b[2]++b[n]*x[n])=S
int valid(int k)
{
return S>=suma(k);}

void back(int k)
{ if(k==n+1){ if(suma(n)==S)scrie();}
else
for(int i=0; i<=y[k];i++)
{
x[k]=i;
if(valid(k)) back(k+1);}
}

Varianta 2. Vectorul soluie x va avea un numar variabil de componente.


x[k]{0,1,2,,y[k]} are semnificatia: se folosesc x[k] bancnote de tipul b[k]
valid(k) : suma(x[1]*b[1]+x[2]*b[2]++b[k]*x[k])
solutie: suma(x[1]*b[1]+x[2]*b[2]++b[k]*x[k])=S, k<=n+1
void back(int k, int sum)
{
if(k<=n&&sum<S)
{ for(int i=0; i<=y[k] && sum+b[k]*i<=S;i++)
{x[k]=i; back(k+1, sum+b[k]*i);}
}
else if(sum==S)scrie(k);
}

13. Submultimi de suma data.


Se da un numar natural nenul s si o multime A={a1,a2,,an} de numere naturale nenule. Sa se
determine toate submultimile lui A cu proprietatea ca suma elementelor acestor submultimi este s.
Exemplu:
Intrucat elementele multimii sunt numere consecutive vom lucra cu indici.
A={8,12,9,5,7,3}, n=6, s=17
x[i]=1,2,,6 sunt indici care indica pozitia elementului din multime;
x[1]=1 => A[x[1]]=8 ; x[2]=3 => A[x[1]]=9 ;
Indicatie. Problema este un caz particular al problemei anterioare, b[1]=b[2]=b[3]=...=b[n]=1.
int valid(int k)
void back(int k, int sum)
{
return S>=suma(k);}
{ if(k<=n&&sum<S)
{ for(int i=0; i<=1;i++)
void back(int k)
if(sum+i*a[k]<=S)
{ if(k==n+1){ if(suma(n)==S)scrie();}
{x[k]=i; back(k+1, sum+i*a[k]);}
else
}
for(int i=0; i<=1;i++)
else if(sum==S)scrie(k);
{ x[k]=i;
}
if(valid(k)) back(k+1);}
}

G3) probleme n plan, atunci cnd spaiul n care ne deplasm este un tablou bidimensional
Backtracking generalizat n plan
Metoda Backtracking n plan are cteva modificri:
- stiva conine mai multe coloane (este dubl, tripl, ...);
- trebuiesc codificate oarecum direciile prin numere, litere, elemente, etc.
Problema labirintului se poate rezolva dup un algoritm de backtracking generalizat n plan.
14. Problema labirintului
Se d un labirint sub form de matrice de n linii i m coloane. Fiecare element din matrice
reprezint o camer. ntr-una din camerele labirintului se gsete un om. Se cere s se afle toate
soluiile ca acel om s ias din labirint, fr s treac de dou ori prin aceeai camer.
OBS:
O camera poate avea iesire spre alta camera sau in afara labirintului la N, la E, la S sau la V.
Se poate trece dintr-o camer n alta, doar dac ntre cele dou camere exist o u.
Prin labirint, putem trece dintr-o camer n alta, folosind usa, doar mergnd n sus N, n jos S, la
stnga V sau la dreapta E, nu i n diagonal.
Codificare
Principiul backtracking generalizat impune codificarea direciilor.
n aceste caz vor fi codificate i combinaiile de ui/perei ai fiecrei camere.
Asftel, un element al camerei va fi un element al unei matrici cu n linii i m coloane, avnd valori
de la 0 la 15. n sistemul binar, numerele 0..15 sunt reprezentate ca 0..1111, fiind memorate pe 4
bii consecutivi.
Vom lua n considerare toi cei 4 bii, astfel numerele vor fi 0000..1110.
Fiecare din cei 4 bii reprezint o direcie, iar valoarea lui ne spune dac n acea direcie a
camerei exist sau nu o u.
Vom reprezenta numrul astfel: nr = b1 b2 b3 b4 (b = bit)
Asftel, b1 indic direcia N (sus), b2 indic direcia E (dreapta), b3 indic direcia S (jos) iar b4
indic direcia V (stnga). Valorile unui bit sunt, firete, 0 i 1.
1 nseamn c n direcia respectiv exist o u, iar 0 nseamn c n direcia respectiv exist
un perete, deci pe acolo nu se poate trece.
Linia i coloana camerei n care se va deplasa omul din camera curent se stabilesc astfel:
dac direcia este 1 (sus), linia se va micora cu 1,
dac direcia este 2 (dreapta), coloana se va mri cu 1,
dac direcia este 3 (jos), linia se va mri cu 1,
dac direcia este 4 (stnga), coloana se va micora cu 1.
De exemplu: 1000 - camera aceasta are perei n E,S,V, iar n N este u spre camera vecin la
N. |_| (linia se micoreaz cu 1)
Acest numr este de fapt 8, aa fiind notat n matricea labirintului. n ceea ce privete direciile,
vom reine doar coordonatele unde se afl omul din labirint, acestea fiind schimbate n funcie de
drumul pe care-l urmeaz.
Exemplu:
S presupunem c avem urmtoarea matrice:
9 8 10 2
12 7 15 11
1 10 10 8
4 13 9 0
Punctul de pornire din acest labirint este
linia 3, coloana 4.

Aceast matrice corespunde labirintului din


desen:

Vom avea 3 soluii de a iei din labirint, fr a trece de dou ori prin aceeai camer:
(3,4)-(2,4)-(2,3)-(1,3) -ieire
(3,4)-(2,4)-(2,3)-(2,2)-(2,1)-(1,1) -ieire
(3,4)-(2,4)-(2,3)-(3,3)-(4,3)-(4,2)-(3,2)-(2,2)-(2,1)-(1,1) ieire

O camer vizitat se reine prin coordonatele ei lin i col.Pentru a memora coordonatele tuturor
camerelor vizitate vom folosi o matrice cu 2 coloane: d[k][1]=lin; d[k][2]=col;
Adugarea coordonatelor unei camere n matricea d se face numai dup verificarea existenei
acestor coordonate n d, pentru a nu trece de dou ori prin acest camer. Aceast verificare se
face comparnd coordonatele camerei n care suntem cu cele ale camerelor memorate in
matricea d.
int valid(int i,int j,int k)
{int y;
for(y=1;y<=k;y++)
if((d[y][1]==i)&&(d[y][2]==j))return 0;
return 1;
}

Dup metoda Backtracking, trebuiesc gsite toate posibilitile de a iei din labirint.
S-a ieit din labirint cnd linia = coloana = 0, linia = n+1 sau cnd coloana =m+1.
#include <iostream>
#include <fstream>
using namespace std;
int a[20][20],i0,j0,n,m,d[20][3],nr;
ofstream g("a.out");
void citire()
{ int i,j; ifstream f("a.in");
f>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++) f>>a[i][j];
f>>i0>>j0;
}
void scrie(int k)
{int i;
nr++;
for(i=1;i<=k;i++)
g<<"("<<d[i][1]<<","<<d[i][2]<<")->";
g<<endl;
}
int valid(int i,int j,int k)
{int y;
for(y=1;y<=k;y++)
if((d[y][1]==i)&&(d[y][2]==j))return 0;
return 1;
}

void back(int i, int j, int k)


{
int x=8,ok=0;
if (valid(i,j,k))
{ d[k+1][1]=i; d[k+1][2]=j;
if (a[i][j]&x)
{if(i==1)ok=1;//am iesire
else back(i-1,j,k+1);}
x=x>>1;
if (a[i][j]&x)
{if(j==m) ok=1;//am iesire
else back(i,j+1,k+1);}
x=x>>1;
if (a[i][j]&x)
{if(i==n)ok=1;//am iesire
else back(i+1,j,k+1);}
x=x>>1;
if (a[i][j]&x)
{if(j==1)ok=1;
else back(i,j-1,k+1);}
if(ok)scrie(k+1);
}
}
int main()
{citire();back(i0,j0,0);
if(nr==0) cout<<"\nNu exista iesire";
else cout<<"\nSunt "<<nr<<" variante";
return 0;}

15. Problema Bilei


Se d un teren sub forma de matrice cu n linii i m coloane. Fiecare element al matricei reprezint
un turn cu o anumit altitudine dat de valoarea reinut de element (numr natural). Pe un astfel
de turn, de coordonate (lin,col) se gsete o bil. Stiind c bila se poate deplasa pe orice turn
nvecinat aflat la nord, est, sud sau vest, de nlime strict inferioar turnului pe care se gsete
bila, s se gseasc toate posibilitile ca bila s prseasc terenul.
Fie terenul alturat. Initial, bila se afl pe turnul de coordonate
(2,2). O posibilitate de iesire din teren este data de drumul:(2,2),
(2,3), (3,3), (3,4). In program, altitudinile subteranului vor fi
retinute de matricea t.
Initial : (2,2) Solutii: (2,2) ; (2,3) ; (2,4)
(2,2) ; (2,3) ; (3,3) ; (3,4) ; (2,4)
(2,2) ; (2,3) ; (3,3) ; (3,4)
(2,2) ; (2,3) ; (3,3) ; (3,4) ; (4,4)

Indicaie. Problema se poate rezolva folosind algoritmul de la


labirint nlocuind testul de intrare ntr-o camera cu cel de nlime
mai mic.
Nu mai este necesar s testm dac bila a ajuns pe un turn deja
vizitat, deoarece la fiecare pas, bila se deplaseaz pe un teren
de altitudine strict inferioar.
44
6893
9763
5854
8371
22
//bila
#include <iostream>
#include <fstream>
using namespace std;
int a[20][20],i0,j0,n,m,d[20][3],nr;
ofstream g("bila.out");
void citire()
{ int i,j; ifstream f("bila.in");
f>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++) f>>a[i][j];
f>>i0>>j0;
}
void scrie(int k)
{int i;
nr++;
for(i=1;i<=k;i++)
g<<"("<<d[i][1]<<","<<d[i][2]<<")->";
g<<endl;
}

void back(int i, int j, int k)


{ int ok=0;
d[k+1][1]=i; d[k+1][2]=j;
if (a[i-1][j]<a[i][j])
{if(i==1)ok=1;//am iesire
else back(i-1,j,k+1);}
if (a[i][j+1]<a[i][j])
{if(j==m) ok=1;//am iesire
else back(i,j+1,k+1);}
if (a[i+1][j]<a[i][j])
{if(i==n)ok=1;//am iesire
else back(i+1,j,k+1);}
if (a[i][j-1]<a[i][j])
{if(j==1)ok=1;
else back(i,j-1,k+1);}
if(ok)scrie(k+1);
}
int main()
{citire(); back(i0,j0,0);
if(nr==0) cout<<"\nNu exista iesire";
else
cout<<"\nSunt
"<<nr<<"
variante
de
iesire";
return 0;
}