Documente Academic
Documente Profesional
Documente Cultură
nr s
i 1
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)
(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.
...
xk Sk
x2 S2
x1 S1
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).
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:
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.
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.
Soluie: k=n+1
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
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
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
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:
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;
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)
{ 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);}
}
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.
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;
}