Sunteți pe pagina 1din 24

Backtracking – generare permutari si combinari

Etichete : aranjamente, backtracking, backtracking in plang, combinari, generare aranjamente,


generare combinari, generare permutari
DefCamp 2011 @Iasi - conferinta regionala de hacking & INFOSEC

Cu totii stim ca Backtracking este una din cele mai cunoscute tehnici de
programare. Ideea din spatele acestui algoritm este de a genera toate solutiile posibile si a le
abandona in cazul in care observa ca nu indeplinesc conditiile necesare. Datorita generarii tuturor
solutiilor, complexitatea algoritmului este foarte mare, dar duce sigur la generarea tuturor
posibilitatilor valide. Din aceasta cauza, e o tehnica foarte cunoscuta in randul programatorilor (in
special a celor care participa la concursuri si olimpiade de informatica) deoarece este considerata
solutia ce trebuie abordata in cazul in care nu ai nici o idee.
Structura unei functii backtracking
void back(int k)
{
if(reject_sol(k)) return;
if(accept_sol(k)) show_sol(k);
for(i=0;i<n;i++)
{
v[k]=set();
back(k+1);
v[k]=unset();
}
}
Generarea tuturor permutarilor
Problema : Sa se genereze toate permutarile multimii {1, 2, …N}, in ordine lexicografica.
Solutie : Din moment ce problema ne cere generarea tuturor solutiilor, este evident ca vom avea
nevoie de o abordare backtracking.
#include<cstdio>
int a[10],b[10];

void back(int k,int len)


{
if(k-1 == len) //afisam solutia
{
for(int i = 1; i <= len;i++)
printf("%d ",a[i]);
printf("\n");
}
else
{
for(int i = 1; i <= len; i++)
if(!b[i]) //daca valoarea nu-i folosita
{
a[k] = i;
b[i] = 1; //o folosim
back(k+1,len); //trecem la pasul urmator
b[i] = 0; //o eliberam
}
}
}

int main()
{
freopen("permutari.in","r",stdin);
freopen("permutari.out","w",stdout);
int n;
scanf("%d",&n);
back(1,n);
fclose(stdin); fclose(stdout);
return 0;
}
Generarea tuturor combinarilor de n luate cate k
Problema : Sa se genereze toate combinarile de N luate cate K in ordine lexicografica.
Solutie : Din moment ce si aceasta problema ne cere generarea tuturor posibilitatilor este evident ca
solutia este una ce are nevoie de o abordare backtracking.
#include<cstdio>
int a[19],b[19];

void back(int k,int len,int max)


{
if(k-1 == max) //afisam solutia
{
for(int i = 1; i <= max;i++)
printf("%d ",a[i]);
printf("\n");
}
else
{
for(int i = 1; i <= len; i++)
if(!b[i] && a[k-1] < i) //ne asiguram ca generam solutiile crescatoare si unice
{
a[k] = i;
b[i] = 1; //o folosim
back(k+1,len,max); //trecem la pasul urmator
b[i] = 0; //o eliberam
}
}
}

int main()
{
freopen("combinari.in","r",stdin);
freopen("combinari.out","w",stdout);
int n,k;
scanf("%d %d",&n,&k);
back(1,n,k);
fclose(stdin); fclose(stdout);
return 0;
}
Aplicatii Backtracking
1. Generarea aranjamentelor
2. Problema damelor
3. Problema turelor
4. Generarea produsului cartezian a n multimi
5. Sa se genereze n perechi de parantezari care se inchid corect
6. Cel mai lung prefix
Concluzie
Aceasta metoda este extrem de raspandita si de utila in cazul problemelor in care generarea tuturor
solutiilor este necesara. De asemenea, in cazul concursurilor este una din cele mai populare
modalitati de “furare” a catorva puncte.
Referinte

http://infoscience.3x.ro/c++/pbbacktracking.html

http://infoarena.ro/problema/combinari

http://infoarena.ro/problema/permutari

http://en.wikipedia.org/wiki/Backtracking

http://www.cse.ohio-state.edu/~gurari/course/cis680/cis680Ch19.html

Tags:
• backtracking
• problema damelor
• probleme rezolvate
//problema damelor
#include<iostream.h>
#include<conio.h>
#include<math.h>
int st[20],n,k;
void init()
{st[k]=0;}
int succesor()
{if (st[k]<n)
{st[k]++;
return 1;
}
else return 0;}
int valid()
{for(int i=1;i<k;i++)
if(st[i]==st[k] || abs(st[k]-st[i])==abs(k-i)) return 0;
return 1;}
int sol()
{return (k==n);}
void tipar()
{for(int i=1;i<=n;i++) cout<<st[i];
cout<<endl;
}
void bkt()
{int as;k=1;
init();
while(k>0)
{
do {} while ((as=succesor()) && !valid());
if (as)
if (sol()) tipar();
else {k++;init();}
else k--;
}
}
main()
{cout<<"n=";cin>>n;
bkt();
getch();
}
permutari
#include<iostream.h>
#include<conio.h>
#include<stdio.h>
int x[100],n,k;
//k este este nivelul din stiva (indexul - vetorul solutie),curent
int e_valid()
{for(int i=1;i<=k-1;i++)//parcurg nivelurile anterioarenivelului curent
if(x[i]==x[k])
return 0;
return 1;
}
void afisare()
{for(int i=1;i<=n;i++)
cout<<x[i]<<" ";
cout<<endl;
}
void back()
{k=1; //pe primul nivel initial
while(k>0)//cand k va descreste la 0 algoritmul se incheie
if(x[k]<n)
{x[k]++;
if(e_valid())//daca elementul incarcat este valid
if(k==n)//verific daca am ajuns la solutia completa.
afisare();
else //daca nu am solutia completa urc in stiva (maresc vectorul, adica pe k)
{k++;
x[k]=0;}
}
else
k--;
}
void main()
{clrscr();
cout<<"n=";
cin>>n;
back();
getch();
}

Metoda Backtracking. Probleme propuse


1. Problema permutarilor primelor n numere
2. Problema aranjamentelor
3. Problema combinarilor
4. Permutari , aranjamente, combinari de numere.
5. Se citesc n numere. Sa se genereze toate secventele din exact m dintre ele (m<n) astfel incat
secventele sa contina numere distincte si doua numere alaturate sa nu aiba aceeasi paritate.
Daca nu exista solutii se va afisa un mesaj;
6. Problema turelor
7. Problema damelor
8. n camile sunt numerotate de la 1 la n sunt aranjate in sir indian. Sa se rearanjeze camilele
astfel incat fiecare camila sa aiba in fata o camila diferita de configuratia initiala.
9. Sa se genereze produsul cartezian a n multimi. Pentru fiecare multime se cunoaste numarul
de elemente. Daca o multime a p elemente atunci va contine valorile de la 1,2 …la p.

Exn=3, p1=2, p2=3, p3=3 p4=4 Atunci:


{1,2} x {1,2,3} x {1,2,3}x{1,2,3,4}=
(1 1 1 1 ) (1 1 1 2 ) (1 1 1 3 ) (1 1 1 4 ) (1 1 2 1 ) (1 1 2 2 ) (1 1 2 3 ) (11 2 4 ) (1 1 3 1 )(1 1 3 2 ) (1
1 3 3 ) (1 1 3 4 )
(1 2 1 1 ) (1 2 1 2 ) (1 2 1 3 ) (1 2 1 4 ) (1 2 2 1 ) (1 2 2 2 ) (1 2 2 3 ) (1 2 2 4 ) (1 2 3 1 ) (1 2 3 2) (1
2 3 3 ) (1 2 3 4 )
(1 3 1 1 ) (1 3 1 2 ) (1 3 1 3 ) (1 3 1 4 ) (1 3 2 1 ) (1 3 2 2 ) (1 3 2 3 ) (1 3 2 4 ) (1 3 3 1 ) (1 3 3 2 ) (1
3 3 3 ) (1 3 3 4 )
(2 1 1 1 ) (2 1 1 2 ) (2 1 1 3 ) (2 1 1 4 ) (2 1 2 1 ) (2 1 2 2 ) (2 1 2 3 ) (2 1 24 ) (2 1 3 1 ) (2 1 3 2 ) (2
1 3 3 ) (2 1 3 4 )
(2 2 1 1 ) (2 2 1 2 ) (2 2 1 3 ) (2 2 1 4 ) (2 2 2 1 ) (2 2 2 2 ) (2 2 2 3 ) (2 2 2 4 ) (2 2 3 1 ) (2 2 3 2 ) (2
2 3 3 ) (2 2 3 4 )
(2 3 1 1 ) (2 3 1 2 ) (2 3 1 3 ) (2 3 1 4 ) (2 3 2 1 ) (2 32 2 ) (2 3 2 3 ) (2 3 2 4 ) (2 3 3 1 ) (2 3 3 2 ) (2
3 3 3 ) (2 3 3 4 )
N copii se aseaza in sir indian. Se cunosc numele celor n copii. Sa se gaseasca toate posibilitatile de
aranjare in sir.
10. Gigel are n cartonase (n<=10). Pe fiecare este scrisa o cifra de la 1 la 9. Uilizand doua tipuri
de cartonase cu + si - vrea sa obtina rezultatul 2. Care sunt solutiile pentru n citit?
11. Sa se genereze n perechi de paranteze care se inchid corect. Exemplu:
n=3: ( ( ( ) ) ) ( ( ) ( ) ) ( ) ( ( ) ) etc
12. Se cer toate solutiile de asezare in linie a m caini si n pisici astfel incat sa nu existe o pisica
intre doi caini
13. Sa se genereze toate numerele palindrome de lungime n
14. Sa se genereze toate partitiile unui numar (sa se descompuna in suma de numere). Ex: n=4
Solutii:
1111
112
13
22
16. Sa se decompuna un numar in suma de numere prime. Generati toate solutiile.

17. N copii se aseaza in cerc. Se cunosc numele celor n copii. Sa se gaseasca toate posibilitatile
de rearanjare in cerc.
18. N copii se aseaza in sir indian. Se cunosc numele celor n copii. Sa se gaseasca toate
posibilitatile de aranjare in sir astfel incat un baiat sa urmeze dupa cel mult doua fete
alaturate.
19. N copii au fost asezati in sir indian. Se cunoaste configuratia initiala. Sa se reaseze copiii
astfel incat fiecare copil sa urmeze dupa un alt copil, diferit de cel din configuratia initiala.
20. Se citeste un numar. Sa se genereze toate numerele avand aceleasi cifre ca el. Care este cel
mai mare?
21. N copii au fost asezati in sir indian. Se cunoaste configuratia initiala. Sa se reaseze copiii
astfel incat fiecare copil sa se situeze intre alti copii, diferiti de cei din configuratia initiala.
22. Plata unei sume in bancnote de n tipuri. Solutia cea mai lunga (scurta)
23. Problema drapelelor. Sa se afiseze ca drapel
24. Sa se genereze anagramele unui cuv
25. Sa se genereze toate triunghiurile de perimetru n
26. Intre n persoane care stau pe scaune s-au iscat conflicte. Acestea stau pe scaune numerotate
de la 1 la n. Scrieti un program care sa afiseze toate modurile posibile de reasezare a
persoanelor astfel incat sa nu se gaseasca alaturi doua persoane in conflict.
27. Sa se genereze toate matricile binare (avand 0 si 1) simetrice cu nxn componente.
28. Sa se genereze o secventa de n sunete avand lungimea p care respecto o anumita conditie
29. La un spectacol trebuie sa interpreteze cate o poezie copiii A, B, C, D, E astfel incat copilul
D sa recite inainte de A si B. Sa se genereze toate posibilitatile de recitare a poeziilor.
30. Sa se genereze toate numerele de lungime n formate doar cu cifre pare / impare
31. Scrieti un program care sa afiseze toate numerele de n (n<=10) cifre, formate numai din cifre
distincte si care sunt divizibile cu 4.
32. Sa se aranjeze in toate modurile elementele unui vector a[1],a[2]…a[n] formand secvente de
lungime p, astfel incat fiecare element sa apara de cel mult doua ori
33. Sa se genereze toate cuvintele de lungime p, distincte / nedistincte, care se pot forma cu
literele alfabetului dintr-o multime data
34. Pe o tabla de dimensiune nxn se gasesc n regi. Sa se gaseasca toate posibilitatile de aranjare
a regilor pe tabla astfel incat oricare 2 regi sa nu se atace. Obs. Fiecare rege se va gasi pe alta
linie.
35. Problema partitiilor unui numar
36. Submultimile unui numar
37. a) Fie sirul primelor n numare naturale (n citit de la tastatura). Sa se insereze inainte de
fiecare semnul + sau minus. Pentru fiecare solutie astfel generata se va afisa valoarea
expresiei. Ex pt n =3:
+1+2+3=6
+1+2-3=0
+1-2+3=2 etc
b) sa sedetermine solutiile pentru care expresia este egala cu x. Daca nu exista solutii sa se
afiseze un mesaj
38. a) Fie n numare naturale (n citit de la tastatura) citite de la tastatura. Sa se insereze inainte de
fiecare semnul + sau minus. Pentru fiecare solutie astfel generata se va afisa valoarea
expresiei. Ex pt n =3 si numerele 2 5 4 se vor genera expresiile:
+2+5+4=11
+2+5-4=3
+2-5+4=1 etc.
b) sa sedetermine solutiile pentru care expresia este egala cu x. Daca nu exista solutii sa se
afiseze un mesaj
39. La o cofetarie se comercializeaza n sortimente de prajituri. Sa se determine toate variantele
de a face pachete cu cate p prajituri diferite. Scrieti un program care permite citirea de la
tastatura a celor n sortimente de prajituri si afiseaza variantele solutie precum si numarul
acestora.
40. Fiind data o multime de n cuburi, fiecare cub fiind caracterizat de lungimea laturii si
culoarea sa, sa se scrie un program care sa genereze toate turnurile care se pot forma cu p
cuburi astfel incat doua cuburi vecine sa nu aiba aceeasi culoare iar deasupra unui cub sa nu
se poata aseza un cub cu latura mai mare.
41. Un grup de copii are la dispozitie n cartonase cu n cuvinte disticte pentru jocul "cerc de
cuvinte". In acest joc un copil trebuie sa spuna un cuvant care sa aiba primele doua litere
identice cu ultimele doua ale cuvantului spus de predecesorul lui. fiind dat un cuvant de
inceput pentru joc, afisati varianta cu cele mai multe cuvinte care se pot obtine cu ajutorul
cartonaselor date. Observatie: un sir de cuvinte nu va contine un cuvant de mai multe ori.
42. O persoana a uitat numarul de telefon al unui prieten. Stie doar ca numarul are 6 cifre, incepe
cu 4 si contine 3 zerouri dintre care doua sunt alaturate. fisati toate variantele pe care trebuie
sa le incerce pentru a vorbi cu prietenul sau.
43. La o masa rotunda sunt n persoane de diverse nationalitati, pentru fiecare persoana
precizandu-se doua limbi straine cunoscute de ea. Se cere sa ajutati organizatorii mesei
rotunde sa aranjeze persoanele astfel incat fiecare sa poata conversa atat cu cea din stanga cat
si cu cea din dreapta.
44. Sa se genereze numerele mai mici decat n citit care trecute in baza 2 au in componenta lor
exact p cifre de 1.
45. Teste la geografie. Pentru lucrarea de control profesoara de geografie a pregatit n teste. In
clasa sunt p elevi (p>n). Sa se genereze toate posibilitatile de a imparti testele celor p elevi
astfel incat fiecare test sa fie rezolvat de macar un elev.
46. Sa se genereze toate drapelele tricolore care se pot forma cu n culori (eventual impunand
conditii : in mijloc sa fie o anumita culoare, o culoare sa nu stea langa alta culoare etc
47. Produsul cartezian a n multimi impunand conditia ca suma elementelor dintr-o solutie sa fie
egala cu un S citit
48. Sa se genereze toate submultimile de cate k elemente care se pot forma cu numerele 1,2…n
(sau a[1],a[2]…a[n]), cu conditia ca fiecare element sa fie divizibil cu un numar d dat.
49. Sa se rearanjeze elementele unui vector a[1],a[2]…a[n] in toate modurile posibile, astfel
incat oricare doua alaturate sa nu fie consecutive in sirul initial
50. Sa se aranjeze n margele de m culori astfel incat oricare doua margele alaturate sa aiba culori
diferite
51. Sa se genereze toate numerele de lungime p care sunt supermultiple de p (atat numerele cat
si toate prefixele lor sa fie multiplu de p)
52. La un festival de muzica usoara s-au inscris n melodii codificate cu numere de la 1 la n.
Stiind ca in prima zi intra in concurs k melodii, sa se afiseze toate posibilitatile de a stabili
ordinea intrarii in concurs a melodiilor in prima zi, stiind ca melodiile de coduri c1 si c2
trebuie sa intre in prima zi, a doua respectiv penultima
53. Sa se afiseze toate numerele de lungime p<=9 cu proprietatea ca au suma cifrelor egala cu x
dat
54. Sa se afiseze toate submultimile cu p elemente dintre elementele a[1],a[2]…a[n] cu
proprietatea ca suma elementelor din multime este un numar divizibil cu x dat
55. Sa se afiseze toate modurile in care se poate forma un grup de p persoane dintr-un grup de n
persoane, dintre care l persoane sa fie femei
56. La un concurs se prezinta n concurenti din m tari. Sa se stabileasca ordinea intrarii in concurs
a celor n concurenti astfel incat doi concurenti din aceeasi tara sa nu urmeze unul dupa altul
57. Sa se aranjeze elementele multimii {A,R,G,V} in grupuri de cate n (n par) astfel incat doua
caractere identice sa nu fie alaturate si R sa apara de exact n/2 ori
58. Sa se genereze toate numerele de lungime n formate doar cu cifre pare / impare
59. Sa se genereze toate numerele de lungime n divizibile cu x dat
60. Sa se determine toate numerele de lungime n care sunt egale cu inversele lor
61. Sa se determine toate modurile in care poate fi capsat un bilet, stiind ca pozitiile posibile sunt
de forma:
***
***
***
si se pot perfora p1<=k
Biletul poate fi introdus pe oricare din fete.
1. Sunt 2n copii de inaltimi diferite. Sa se aseze copiii pe 2 randuri astfel:
- pe primul rand copiii sa fie asezati in ordinea crescatoare a inaltimii
- copiii de pe al doilea rand sa fie mai inalti decat cei din fata lor
2. un pion se poate deplasa pe o tabla dreptunghiulara cate o casuta pe orizontala sau pe
varticala. Se dau coordonatele initiale, coordonatele finale si de cate ori trebuie sa treaca
pionul prin fiecare casuta. Sa se genereze toate solutiile. De fiecare data se afiseaza traseul de
parcurs.
Exemplu
112110
002220
000010
000000
din 1 1 pana in 1 5 o solutie este:
1 1, 1 2, 1 3, 2 3, 2 4 , 2 5, 3 5, 2 5, 2 4, 2 3, 1 3, 1 4, 1 5.
62. Sa se genereze toate solutiile naturale nenule ale ecuatiei 4x+y+3yz=100
63. sa se genereze toate codurile morse de lungime n coduri reprezentate prin ‚–‚ sau ‚.’ Astfel
incat intr-o secventa sa nu existe doua puncte alaturate. Pentru fiecare semn se va genera un
sunet.
64. Sa se genereze toate secventele in cod binar de lungime n. Pentru fiecare secventa se va
genera numarul asociat in baza 10.Sa se genereze toate codurile
65. Sa se genereze toate numerele naturale ale caror cifre se gasesc printre cifrele lui x citit si au
lungimea cel mult egala cu lungimea lui x. Cifrele se pot repeta
66. La Masa Rotunda sunt n cavaleri. Fiecare dintre ei are cel putin un dusman printre ceilalti.
Sa se gaseasca toate posibilitatile de a-i aseza la masa astfel incat doi vavaleri dusmani sa nu
fie vecini. Se citesc cele m perechi de dusmani de la tastatura (fisier)
67. Fie o harta cu n tari. M perechi de tari sunt vecine (se cunosc perechile de tari vecine). Sa se
coloreze harta astfel incat oricare doua tari alaturate sa fie colorate diferit.
68. Un comis voiajor trebuie sa ajunga la n case. Intre cele n case exista m dumuri (un drum este
dat ca o pereche de case vecine). Sa se genereze toate posibilitatile de parcurgere a celor n o
singura data case astfel incat comis voiajorul sa ajunga inapoi de unde a plecat. Casa de la
care se pleaca este casa p.
69. In cate moduri poate ajunge un pion de pe prima linie a unei table bidimensionale cu n linii
si n coloane pe ultima linie a tablei. Se cunoaste coloana de plecare p. Pionul se poate
deplasa numai pe o casuta alaturata si numai pe o linie mai mare.
70. Sa se determine partitiile unui numar pt care suma inverselor obtinute este subunitara. Ex.
n=5 3+2=5 si 1/3+1/2<1.
71. Se citeste un numar natural. Sa se determine toate numerele avand aceleasi cifre sau o parte
din cifre si care sunt divizibile cu p citit
72. Sa se determine toate numerele cu cifre distincte. Cate astfel de numere sunt?
73. Sa se genereze toate numerele avand cifre distincte de la 0 la p. Numarul de cifre poate fi
>=1 si <= p+1. Cate astfel de numere sunt?
74. Sa se determine cate numere cu cifre distincte sunt.
Backtracking in plan
Fie urmatoare problema: un soricel se gaseste intr-un labirint de forma dreptunghiulara cu m linii si
n coloane. Peretii sunt marcati cu 1 si culoarele cu 0. Se cunosc coordonatele initiale ale soricelului:
Li, Ci. Sa se determine toate posibilitatile pe care le are soricelul pentru a iesi din labirint. Soricelul
poate avansa pe 4 directii cate o celula (sus, dreapta , jos, stanga).
O astfel de problema presupune o abordare Backtracking in plan. Traseul soricelului va fi
retinut de un vector cu doua campuri: coordonatele x si y. Vom defini un tip struct:
struct pozitie
{int x,y;};
si vom declara un vector care retine drumul: pozitie d[50];
Pentru generarea unui drum vom defini un subprogram recursiv oid ies(int x,int y) care
primeste ca parametri coordonatele unei componente din matrice. Initial se apeleaza pentru
coordonatele initiale ale soricelului. O componenta din matrice va putea apartine drumului daca
evident este culoar (a[x][y]=0). O celula a matricii determinata ca apartinand drumului se marcheaza
cu 2 (pentru a preveni ciclarile):
a[x][y]=2;
Se incarca valoarea corespunzatoare in vectorul d pe nivelul curent:
d[k].x=x;
d[k].y=y;
De fiecare data cand s-a determinat o noua celula ca apartinand drumului se determina daca
s-a ajuns la solutie (conditie care difera de la o problema la alta).
In cazul probemei noastre se iese din labirint cand se ajunge la linia 0 sau coloana 0 sau linia
m+1 sau coloana n+1. testul este:
if((x<1)||(x>m)||(y<1)||(y>n))
tipar(k);

In caz afirmativ se tipareste (se afiseaza vectorul d si/sau matricea a) altfel (daca solutia este
incompleta) se incearca parcurgerea, pe rand, a celor 4 celule alaturate. Acest lucru se realizeaza
prin autoapelul functiei ies pe cele patru directii:
ies(x-1,y);
ies(x,y+1);
ies(x+1,y);
ies(x,y-1);
Observatie: vectorul d functioneaza ca o stiva cu doua campuri.
La revenire din apel se elibereaza celula pentru a o putea accesa si in cadrul altor prelucrari:
a[x][y]=0 si se elibereaza componenta drumului k=k-1 (practic se coboara in stiva).
void ies(int x,int y)
{if(a[x][y]==0)
{k++;
a[x][y]=2;
d[k].x=x;
d[k].y=y;
if((x<1)||(x>m)||(y<1)||(y>n))
tipar(k);
else
{ies(x-1,y);
ies(x,y+1);
ies(x+1,y);
ies(x,y-1);
}
a[x][y]=0; //la revenire din apel demarchez celula pentru a o putea
//accesa si in cadrul altei prelucrari
k--; //eliberez componenta din vectorul drumului
}
}
Fie urmatorul labirint: m=6 n=10 Li=4, Ci=3
1111011111
1111011111
1111011111
1100000000
1111111011
1111111011
Solutiile vor fi:
solutia 1
(4,3) (4,4) (4,5) (3,5) (2,5) (1,5) (0,5)
1111211111
1111211111
1111211111
1122200000
1111111011
1111111011
solutia 2
(4,3) (4,4) (4,5) (4,6) (4,7) (4,8) (4,9) (4,10) (4,11)
1111011111
1111011111
1111011111
1122222222
1111111011
1111111011
solutia 3
(4,3) (4,4) (4,5) (4,6) (4,7) (4,8) (5,8) (6,8) (7,8)
1111011111
1111011111
1111011111
1122222200
1111111211
1111111211
Programul complet este:
#include<fstream.h>
#include<conio.h>
struct pozitie
{int x,y;};
int a[20][20];//labirintul
int k,n,m,Li,Ci,nr_sol;
pozitie d[50];
void afis_mat() //afiseaza matricea
{cout<<endl;
for(int i=1;i<=m;i++)
{for(int j=1;j<=n;j++)
cout<<a[i][j]<<" ";
cout<<endl;}
}
void tipar(int k) //tipareste vectorul drum
{nr_sol++;
cout<<"solutia "<<nr_sol<<endl;
for(int i=1;i<=k;i++)
cout<<"("<<d[i].x<<','<<d[i].y<<") ";
afis_mat();
getch();
cout<<endl;}
void ies(int x,int y) //genereaza drumul
{if(a[x][y]==0)
{k++;
a[x][y]=2;
d[k].x=x;
d[k].y=y;
if((x<1)||(x>m)||(y<1)||(y>n))
tipar(k);
else
{ies(x-1,y);
ies(x,y+1);
ies(x+1,y);
ies(x,y-1);
}
a[x][y]=0; //la revenire din apel demarchez celula pentru a o putea
//accesa si in cadrul altei prelucrari
k--;//eliberez componenta din vectorul drumului
}
}
void citire()
{ fstream f;
f.open("labir.txt",ios::in);
if(f)
cout<<"ok";
else
cout<<"eroare la deschidere";

//Citesc matricea ce reprezinta labirintul


f>>m>>n;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
f>>a[i][j];
f>>Li>>Ci; //coordonatele punctului in care se afla soricelul
}
void main()
{clrscr();
k=0;
citire();
ies(Li,Ci);
afis_mat();
getch();
}
Probleme propuse:
1. aceeasi problema ca in exemplu cu diferenta ca soricelul poate avansa in celule
alaturate pe cele 8 directii posibile
2. Un soricel se gaseste intr-un labirint de forma dreptunghiulara cu m linii si n
coloane. Peretii sunt marcati cu 1 si culoarele cu 0. Se cunosc coordonatele initiale
ale soricelului: Li, Ci. Sa se determine toate posibilitatile pe care le are soricelul
pentru a iesi ajunge la cascaval. Se cunosc coordonatele cascavalului: Lf,Cf.
3. Aceeasi problema ca la 2 cu diferenta ca se afiseaza doar cea mai scurta solutie.
4. Un labirint dreptunghiular cu m linii si n coloane contine culoare (reprezentate prin
0) si pereti (reprezentati prin -1). Se dau coordonatele initiale ale unui soricel si
coordonatele finale , unde trebuie sa ajunga. Pe culoare se gasesc bucati de branza pt
care se cunoaste greutatea in grame.
a) Sa se genereze toate solutiile., pt fiecare se afiseaza cantitatea consumata
b) Sa se afiseze soultia cea mai „consistenta” Indicatie: se vor marca cu -2
celulele parcurse si se vor retine in vectorul drum inclusiv cantitatea
condumata. Matricea se borda cu pereti (-1).
5. Pe o tabla de forma dreptunghiulara se afla un cal. Se cunosc coordonatele initiale
ale calului. Acesta trebuie sa ajunga intr-o pozitie finala sarind numai sub forma
sariturii calului. Stiind ca numai anumite celule sunt permise sariturii (acestea sunt
marcate) sa se determine ce posibilitati sunt ca sa se ajunga in pozitia finala.
6. Romeo si Julieta se gasesc intr-un labirint (se cunosc culoarele si peretii si
coordonatele celor doi indragostiti).
a)Exista posibilitatea ca Romeo sa ajunga la Julieta?
b)in cazul in care cei doi se indreapta simultan unul catre celalat pentru fiecare solutie se
va afisa locul intalnirii (coordonatele celulelor alaturate sau celulei comune de intalnire)
<a href='http://ads.3xmedia.ro/www/delivery/ck.php?n=aee207a1' target='_blank'><img
src='http://ads.3xmedia.ro/www/delivery/avw.php?zoneid=20&amp;n=aee207a1' border='0' alt=''
/></a>
PROBLEME BACKTRACKING
IN PLAN

1. Sa se completeze o tabla de 5*5 cu saritura calului.


Iata doua solutii:

1 14 99 20
20 33
24 19 22 15
15 10
10
13
13 88 25
23 44 21
21
18
18 23
25 66 11
11 16
16
77 12
12 17
17 22
22 55

Aflati numarul de solutii si afisati tablele.


2.Se da un tablou bidimensional (m linii, n coloane si m*n componente) cu elemente
distincte. Se dau si coordonatele primului punct. Ss se genereze un traseu in matrice pornind de la
punctul dat astfel incat suma ∑ k*a[i][j] (unde k=1 → n*m ) sa fie maxima. In matrice se poate
avansa numai la elemente vecine (pe cele 8 directii).
Ex: n=m=4;pozitia initiala : linia 3,coloana 2;
1 14 3 4
3
12 2 15 5
11 1 16 6
10 9 8 7
Suma maxima va fi: 1*1+2*2+3*3+…+16*16.

3.Ali Baba
Ali Baba i-a surprins pe cei 40 de hoti, in timp ce cotrobaiau printre comorile lui. Hotii erau
multi, el era singur, asa ca a incercat sa negocieze cu ei. Avea o ladita speciala, pe care era notat
numarul de diamante aflate in aceasta, un numar avnd cel mult 40 de cifre, intr-o baza b. Ali Baba a
propus conducatorului hotilor sa elimine din numar b cifre, dupa care poate sa plece cu atatea
diamante cate reprezinta numarul ramas.
Hotul, mai intai a stabilit valoarea cea mai mica posibila b, deoarece a vrut sa stabileasca un numar
cat mai mic posibil de cifre pe care urmeaza sa le elimine. Apoi a inceput tierea cifrelor. In timpul
eliminarii a urmarit ca numarul ramas sa fie cat se poate de mare.
Scrieti un program care il ajuta pe hot(Ali Baba are destule comori…).
Date de intrare
Pe prima linie a fisierului de intrare ALIBABA.IN se afla un sir de cifre, nedespartite prin spatii.
Date de iesire
Pe prima linie a fisierului de iesire ALIBABA.OUT se va scrie pe fiecare cate un numar,
reprezentand numasrul ramas dupa eliminarea unei cifre.
Restrictii si precizari 2<=b<=10
Exemplu
ALIBABA.IN
323332112
ALIBABA.OUT
4
33332112
333212
33322
3332

Un labirint dreptunghiular cu m linii si n coloane contine culoare (reprezentate prin 0) si


pereti (reprezentati prin 1). Se dau coordonatele initiale ale unui soricel si coordonatele finale , unde
trebuie sa ajunga. Soricelul poate avansa sus, jos , stanga sau dreapta.(Incercati si cu cele 8 directii)
1. Sa se genereze toate solutiile.
2. Sa se afiseze soultia cea mai lunga (scurta)
Un labirint dreptunghiular cu m linii si n coloane contine culoare (reprezentate prin 0) si
pereti (reprezentati prin 1). Se dau coordonatele initiale ale unui soricel care trebuie sa iasa din
labirint.SA se genereze toate solutiile
Un labirint dreptunghiular cu m linii si n coloane contine culoare (reprezentate prin 0) si
pereti (reprezentati prin -1). Se dau coordonatele initiale ale unui soricel si coordonatele finale ,
unde trebuie sa ajunga. Pe culoare se gasesc bucati de branza pt care se cunoaste greutatea in grame.
1. Sa se genereze toate solutiile., pt fiecare se afiseaza cantitatea consumata
2. Sa se afiseze soultia cea mai „consistenta”
Un broscoi se gaseste pe o frunza de nufar pe un lac. Broscoiul poate sari numai sub forma
sariturii calului si nu trebuie sa cada in apa. Pe lac se gasesc mai multe frunze. Se da dimensiunea
lacului (ca matrice mxn) si coordonatele frunzelor. Ajutati-l pe roscoi sa iasa din lac. (si alte
variante: de la o frunza la alta etc)
Problema fotografiei-
Metoda backtracking
Noţiuni teoretice
Prezentare generală
Această metodă generală de programare se aplică problemelor în care soluţia se poate reprezenta
sub forma unui vector X = (x1, ..., xn)ÎS unde S = S1 x ... x Sn , unde mulţimile S1, ...,Sn sunt
mulţimi finite având |Si| = si elemente. Pentru fiecare problemă concretă sunt date anumite relaţii
între componentele x1 , ... xn ale vectorului X, numite condiţii interne.
Mulţimea finită S = S1 x S2 x... x Sn se numeşte spaţiul soluţiilor posibile (este un produs
cartezian). Soluţiile posibile care satisfac condiţiile interne se numesc soluţii rezultat. Ceea ce ne
propunem este de a determina toate soluţiile rezultat, cu scopul de a le afişa sau de a alege dintre
ele una care maximizează sau minimizează o eventuală funcţie obiectiv dată.
O metoda simplă de determinare a soluţiilor rezultat constă în a genera într-un mod oarecare toate
soluţiile posibile şi de a verifica dacă ele satisfac condiţiile interne. Dezavantajul constă în faptul că
timpul cerut de această investigare exhaustivă este foarte mare. Astfel, chiar pentru |Si| = 2, " i,
timpul necesar este de ordinul 2n, deci exponenţial.
Metoda backtracking urmăreşte să evite generarea tuturor soluţiilor posibile. În acest scop,
elementele vectorului X primesc pe rând valori în sensul că lui xk i se atribuie o valoare numai
dacă au fost atribuite deja valori lui x1 ,... xk-1 . Mai mult, odată o valoare pentru xn stabilită, nu
se trece direct la atribuirea de valori lui xk+1 , neîndeplinirea lor exprimând faptul că oricum am
alege xk+1,...,xn nu vom putea ajunge la o soluţie rezultat, adică o condiţie pentru care condiţiile
interne să fie satisfăcute. Evident că în cazul neîndeplinirii condiţiilor de continuare va trebui să
facem o altă alegere pentru xk sau dacă Sk a fost epuizat să micşorăm pe k cu o unitate încercând
să facem o nouă alegere pentru xk etc.; această micşorare a lui k dă numele metodei, ilustrând
faptul că atunci când nu mai putem avansa, urmărim înapoi secvenţa curentă din soluţie. Este
evident că între condiţiile de continuare şi condiţiile interne există o strânsă legătură. O bună
alegere pentru condiţiile de continuare are ca efect o importantă reducere a numărului de calcule.
Metoda backtracking poate fi reprezentată uşor, pe un arbore construit astfel:
- nivelul 1 conţine rădăcina;
- din orice vârf de pe nivelul k pleacă sk muchii spre nivelul k+1 etichetaţi cu cele sk muchii ale lui
Sk.
Nivelul n+1 va conţine s1 × s2 × ... × sn vârfuri. Pentru fiecare vârf de pe nivelul n+1, etichetele
muchiilor conţinute pe drumul ce leagă rădăcina de acest vârf reprezintă o soluţie posibilă.
Exemplu - Să considerăm problema submulţimilor de sumă dată care constă în următoarele: Fie A
= (a1, a2, ..., an) cu ai > 0, " i. Fie MÎR+. Se caută toate submulţimile B ale lui A pentru care suma
elementelor este M.
Pentru a putea realiza problema prin metoda backtracking vom reprezenta soluţia sub forma x =
(x1, ..., xn) unde xi = 1 dacă aiÎB şi xi = 0 în caz contrar. Sa ne situăm în ipoteza ca n=4. Arborele
ataşat metodei backtracking este următorul:
Câştigul obţinut prin introducerea condiţiilor de continuare constă în faptul că, dacă într-un vârf ele
nu mai sunt verificate, se va renunţa la parcurgerea subarborelui care are ca rădăcină acest vârf.
Acest exemplu permite prezentarea unei variante a metodei backtracking. Într-adevăr, să
considerăm drept soluţie posibilă o valoare k £ n împreună cu k-uplul (x1, ..., xk) unde pentru i Î
{1, ..., k}, xi reprezintă indicele elementului pe care îl introducem în B. Evident xi ¹ xj pentru i¹j.
Pentru a nu se repeta soluţii, vom presupune x1<x2<...<xn .
Obţinem astfel următorul arbore în care fiecare vârf corespunde unei soluţii posibile.

Diferitele variante ale metodei backtracking nu schimbă esenţa ei care constă în faptul că este
reprezentabilă pe un arbore care este parcurs "coborând" în arbore numai dacă există şanse de a
ajunge la o soluţie rezultat.
În continuare, problemele care vor fi prezentate vor urma o schema generală şi anume:
- se va testa dacă am obţinut o soluţie, situaţie în care acesta se va reţine;
- dacă nu am obţinut soluţie se încearcă plasarea unui nou element în vectorul soluţie cu
respectarea condiţiilor de continuare;
- dacă nu se reuşeşte plasarea unui nou element şi spaţiul valorilor posibile de plasat s-a epuizat, se
revine la poziţia anterioară şi se încearcă să se plaseze pe ea un alt element.
Faptul că după plasarea unui element în vectorul soluţie algoritmul presupune plasarea unui
element pe poziţia imediat următoare, adică de fapt reluarea algoritmului, conduce posibilitatea
abordării recursive a algoritmilor de tip backtracking. Acest lucru permite o scriere mult mai scurtă
şi mai simplă a algoritmilor şi apoi a programelor care îi implementează. Astfel, general, un
algoritm backtracking poate fi prezentat astfel:
Subalgoritm back (k)
pentru fiecare valoare i din multimea Sk execută
xk←i
dacă X respectă condiţiile interne atunci
dacă X este solutie atunci
afisează X
altfel
apelează back(k+1)
sfdacă
sfdacă
sfpentru
În funcţie de problema concretă, în algoritmul descris mai sus se vor modifica doar instrucţiunea
pentru, condiţiile interne şi cele de soluţie, structura algoritmului păstrându-se.
Probleme de generare. Oportunitatea utilizării metodei backtracking
Problemele care se rezolvă prin metoda backtracking pot fi împărţite în mai multe grupuri de
probleme cu rezolvări asemănătoare, in funcţie de modificările pe care le vom face în algoritm.
Principalele grupuri de probleme sunt:
a) probleme în care vectorul soluţie are lungime fixă şi fiecare element apare o singură dată în
soluţie;
b) probleme în care vectorul soluţie are lungime variabilă şi fiecare element poate să apară de mai
multe ori în soluţie;
c) probleme în plan, atunci când spaţiul în care ne deplasăm este un tablou bidimensional.
Vom prezenta în cele ce urmează câteva probleme care pac parte din primul grup. Cele mai
cunoscute sunt:
• generarea permutărilor unei mulţimi
• generarea aranjamentelor unei mulţimi
• generarea submulţimilor unei mulţimi
• generarea submulţimilor cu m elemente ale unei mulţimi (combinări)
• generarea produsului cartezian a n mulţimi
• generarea tuturor secvenţelor de n (par) paranteze care se închid corect.
• colorarea ţărilor de pe o hartă astfel încât oricare două ţări vecine să aibă culori diferite
• aranjarea a n regine pe o tablă de şah de dimensiune n fără ca ele să se atace.
Toate problemele din acest grup au particularitatea că soluţia se obţine atunci când vectorul soluţie
ajunge să conţină un anumit număr de elemente.
Vom exemplifica modul de lucru al metodei backtracking pentru problema damelor.
Aranjarea reginelor. Dându-se o tablă de şah de dimensiune nxn (n>3) să se aranjeze pe ea n
regine fără 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 poziţionată unde
indică litera “R”.

În algoritmul de mai sus avem de particularizat următoarele:


Instrucţiunea pentru fiecare valoare i din mulţimea Sk execută va fi înlocuită cu o instrucţiune
pentru care parcurge toate valorile de la 1 până la n.
Condiţia de a putea plasa o regină pe poziţia k este un pic mai complicată şi presupune verificarea
ca să nu se atace cu nici una dintre celelalte k-1 regine deja plasate pe tabla. Dacă pe poziţia k din
vectorul X punem o valoare ea va reprezenta coloana pe care se plasează pe tablă regina k.
Condiţiile devin astfel:
x[i]¹x[k] şi |k-i|¹|x[k]-x[i]| cu i de la 1 la k-1 şi |x| reprezentând modului lui x.
Condiţia de soluţie este simplă şi presupune plasarea corectă a tuturor celor n regine.
Programele Pascal si C++ rezultate prin implementarea algoritmului descris mai sus următoarele:
Varianta C/C++
Varianta Pascal
var x:array[1..100] of byte; #include<iostream.h>
n:byte; #include<math.h>
nrsol:word; int x[100],n,nrsol;
procedure scriesol; void scriesol ()
var i,j:byte; { int i,j;
begin nrsol++;
inc(nrsol); cout<<"Solutia a "<<nrsol<<" este";
writeln('Solutia a',nrsol,'este'); for(i=1;i<=n;i++)
for i:=1 to n do begin { cout<<endl;
writeln; for(j=1;j<=n;j++)
for j:=1 to n do if if (x[j]==i) cout<<"X ";
x[j]=i then write('X',' else cout<<"O ";
') else }
write('O',' '); }
end; int potcont(int k)
end; { int i;
function potcont(k:byte):boolean; for(i=1;i<=k-1;i++)
var i:byte; if (x[i]==x[k] || k-i==abs(x[k]-x[i])) return 0;
atac:boolean; return 1;
begin }
atac:=false; void back(int k)
for i:=1 to k-1 do {
if(x[i]=x[k]) or (k-i=abs(x[k]-x[i])) then int i;
atac:=true; for(i=1;i<=n;i++)
potcont:=not atac; {
end; x[k]=i;
procedure back(k:byte); if (potcont(k))
var i:byte; if (k==n) scriesol();
begin else back(k+1);
for i:=1 to n do }
begin }
x[k]:=i; void main()
if potcont(k) then {
if k=n then scriesol cin>>n;
else back(k+1); nrsol=0;
end; back(1);
end; cout<<nrsol<<" solutii";
begin }
read(n);
nrsol:=0;
back(1);
writeln(nrsol,'solutii');
end.
Din al doilea grup fac parte probleme a căror condiţie de soluţie nu se mai pune asupra numărului
de elemente din vectorul X, ci asupra elementelor din soluţie. Exemple:
• partiţiile unui număr natural n
• partiţiile unei mulţimi
• plata unei sumei cu monede de valori date
Partiţiile unui număr natural. Fie n>0, natural. Să se scrie un program care să afişeze toate
partiţiile unui număr natural n.
Numim partiţie a unui număr natural nenul n o mulţime de numere naturale nenule {p1, p2, …, pk}
care îndeplinesc condiţia p1+p2+ …+pk = n.
Ex: pt n = 4 programul va afişa:
4 = 1+1+1+1
4 = 1+1+2
4 = 1+3
4 = 2+2
4=4
Observaţii: - lungimea vectorului soluţie cel mult n;
• există posibilitatea ca soluţiile să se repete;
• condiţia de final este îndeplinită atunci când suma elementelor vectorului soluţie este n.
Am menţionat mai sus că vom folosi doi parametri, unul pentru poziţia în vectorul soluţie şi un al
doilea în care avem sumele parţiale la fiecare moment. Avem determinată o soluţie atunci când
valoarea celui de-al doilea parametru este egală cu n.
În această situaţie la fiecare plasare a unei valori în vectorul sol valoarea celui de al doilea
parametru se măreşte cu elementul ce se plasează în vectorul soluţie. 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 elementul ce se plasează în vectorul sol, iar o
soluţie avem când acest parametru este zero. Indiferent care modalitate este aleasă acest al doilea
parametru ne permite să optimizăm puţin programul în sensul că putem considera nişte condiţii de
continuare mai strânse.
Varianta C/C++
Varianta Pascal
program Partitii_nr_natural; #include<iostream.h>
var n, ns: byte; int n, ns,sol[20];
sol: array[1..20] of byte; void afis(int l)
procedure afis(l: byte); { int i;
var i: byte; ns++;
begin cout<<"Solutia nr. "<< ns<<" : ";
inc(ns); for(i=1;i<=l;i++) cout<<sol[i]<<" ";
write 'Solutia ', ns, ' : '); cout<<endl;
for i:=1 to l do }
write(sol[i]:3); void back(int i, int sp)
writeln; { int j;
end; if (sp==n) afis(i-1);
procedure back(i, sp: byte); else for(j=1;j<=n-sp;j++)
var j: byte; if (j>=sol[i-1])
begin {
if sp = n then afis(i-1) sol[i]=j;
else for j:=1 to n-sp do back(i+1, sp+j);
if (j>=sol[i-1]) }
then begin }
sol[i]:=j; void main()
back(i+1, sp+j) end; {
end; cin>>n;
begin ns=0;
read(n); back(1,0);
ns:=0; cout<<ns<<" solutii";
back(1,0); }
writeln(ns,'solutii');
end.

Problemele în plan necesită parcurgerea unui tablou unidimensional, iar cele mai cunoscute sunt:
• parcurgerea tablei de şah cu un cal, fără a trece de două ori prin aceeaşi poziţie
• găsirea ieşirii dintr-un labirint
Problemele care se rezolvă prin metoda backtracking în plan au ca cerinţă deplasarea în tablou, pe
linii, coloane sau diagonale sau prin săritură (de obicei săritura calului) dintr-un punct în alt punct
al tabloului sau pe frontieră (prima linie sau coloană, ultima linie sau coloană) eventual respectând
anumite condiţii de deplasare. Dacă ne găsim într-un anumit punct iar cerinţa este de a ne deplasa
în unul din cei opt vecini ai lui vom utiliza pentru acest lucru două cicluri for de la –1 la 1 cu care
valori vom modifica coordonata punctului curent. Dacă deplasarea este permisă numai pe linii
condiţia de respectat este ca suma în valoare absolută pentru cei doi indici să fie 1, iar pentru
deplasarea pe diagonală 2. De asemenea se mai impune condiţia de a nu părăsi tabloul, lucru care îl
vom respecta testând coordonatele noului punct să aparţină mulţimii [1..nrlinii] şi [1..nrcol].
Săritura calului. Fiind dată o tablă de şah de dimensiunea nxn şi un cal în colţul stânga sus al
acesteia, se cere să se afişeze toate posibilităţile de mutare a acestei piese de şah astfel încât să
treacă o singură dată prin fiecare pătrat al tablei. O soluţie va fi afişată ca o matrice nxn în care sunt
numerotate săriturile calului.
Exemplu, pentru n=5, o soluţie este
1 14 9 20 23
10 19 22 15 8
5 2 13 24 21
18 11 4 7 16
3 6 17 12 25
Pentru rezolvarea acestei probleme vom codifica direcţiile de deplasare pentru că ar fi ineficient să
scriem două cicluri for de la –2 la 2 cu cele 25 de variante de deplasare din care valide sunt doar
opt. De asemenea aici spre deosebire de celelalte probleme tratate la aplicarea metodei
backtracking în plan nu vom folosi un vector soluţie, ci vom scrie săriturile în tablou urmărind ca
la revenire să refacem poziţiile ocupate pentru a nu se lua blocaje. În figura de mai jos sunt
prezentate cele 8 mutări posibile pentru un cal.

Varianta C/C++
Varianta Pascal
const dx:array[1..8] of -2..2=(-1,1,2,2,1,-1,- #include<fstream.h>
2,-2); const int dx[8]={-1,1,2,2,1,-1,-2,-2};
dy:array[1..8] of -2..2=(-2,-2,-1,1,2,2,1,-1); const int dy[8]={-2,-2,-1,1,2,2,1,-1};
var a:array[1..10,1..10] of integer; int a[10][10],n;
n:integer; ofstream f("cal.out");
f:text; void afis()
procedure cit; { int i,j;
var i,j :integer; for(i=1;i<=n;i++)
begin { for(j=1;j<=n;j++) f<<a[i][j]<<" ";
readln(n); f<<endl;
for i:=1 to n do }
for j:=1 to n do a[i,j]:=0; f<<endl;
end; }
procedure afis; int inside(int i,int j)
var i,j:integer; {
begin return i>=1 && i<=n && j>=1 && j<=n;
for i:=1 to n do begin }
for j:=1 to n do write(f,a[i,j]:3); void back(int i, int j, int pas)
writeln(f); { int k,inou,jnou;
end; a[i][j]=pas;
writeln(f); if (pas==n*n) afis();
end; else for(k=0;k<8;k++)
FUNCTION inside (i,j:integer):boolean; { inou=i+dx[k];
begin jnou=j+dy[k];
inside:=(i in [1..n])and (j in [1..n]) if (inside(inou,jnou) && a[inou][jnou]==0)
end; back(inou,jnou,pas+1);
procedure back(i,j,pas:integer); }
var k,inou,jnou:integer; a[i][j]=0;
begin }
a[i,j]:=pas; void main()
if pas=n*n then afis { cin>>n;;
else for k:=1 to 8 do begin back(1,1,1);
inou:=i+dx[k]; }
jnou:=j+dy[k];
if inside(inou,jnou) and (a[inou,jnou]=0)
then back(inou,jnou,pas+1);
end;
a[i,j]:=0;
end;
begin
assign(f,'cal.txt');
rewrite(f);
cit;
back(1,1,1);
close(f);
end.

La aceste categorii de probleme se adaugă şi cele de optim, care presupun alegerea soluţiei optime
dintre cele generate.
De asemenea, problemele de combinatorică se pot rezolva folosind metoda backtracking. Soluţiile
cerute se pot reprezenta ca vectori de forma X = (x1, ..., xn)ÎS unde S = S1 x ... x Sn, unde
mulţimile S1, ...,Sn sunt mulţimi finite. Soluţiile se generează element cu element şi trebuie să
respecte o serie de reguli, în funcţie de problema de generare concretă.
Algoritmii de tip backtracking prezentaţi în capitolul anterior vor fi folosiţi pentru a genera
permutări, aranjamente, etc. Pentru fiecare probleme, însă, putem face modificări pentru a creşte
eficienţa algoritmului de rezolvare sau pentru a-l simplifica. Pentru fiecare algoritm vom identifica
particularităţile sale şi condiţiile care trebuie puse asupra vectorului soluţie.
Algoritmii şi programele prezentate mai jos folosesc mulţimi de forma {1,2,…,n} pentru a
simplifica lucrurile. Trecerea la o mulţime generală se face setul de simplu deoarece generând
indicii, practic se generează mulţimea, şi astfel, oricărui vector soluţie X generat pe baza indicilor i
se poate asocia o soluţie dintr-o mulţime oarecare.
De asemenea, este bine să numărăm câte soluţii generează fiecare program şi să verificăm aceste
numere cu ajutorul formulelor cunoscute de la matematică.
Generarea permutărilor
Se dă o mulţime cu n elemente A={a1,a2,…,an}. Se cere să se genereze si să se afişeze toate
permutările ei. Altfel spus, se cere să se afişeze toate modurile în care se pot amesteca elementele
mulţimii A.
Folosim pentru generare mulţimea {1,2,…,n}. Condiţiile care se pun sunt următoarele:
• Fiecare element din vectorul X se ia din {1,2,…,n};
• Un element Xk este valid dacă el nu a fost plasat anterior în vectorul soluţie X;
• Când am generat n elemente cu condiţiile de mai sus, atunci avem o soluţie.
Se pot identifica mai multe modalităţi de a verifica dacă elementul Xk a fost plasat deja în vectorul
soluţie. Cele mai importante două sunt:
• parcurgerea elementelor deja generate pentru a verifica daca Xk apare sau nu între ele;
• folosirea unui vector cu n elemente în care vom avea valori 0 sau 1 corespunzătoare
elementelor mulţimii iniţiale. Valoarea 1 va preciza faptul că elementul de pe poziţia
corespunzătoare a fost plasat anterior în vectorul soluţie, iar valoarea 0 că nu.
Corespunzător acestor două moduri de a verifica dacă un element a mai fost sau nu plasat în
vectorul soluţie, avem 2 moduri de generare a permutărilor.
Generarea aranjamentelor
Generăm aranjamentele unei mulţimi atunci când ne se cer toate modurile de a alege m elemente
distincte dintre cele n ale mulţimii (m<n).
Această problemă se rezolvă foarte uşor folosind metodele de generarea permutărilor. Singurele
modificări presupun citirea numărului m, modificarea condiţiei de soluţie, care va fi k=m în loc de
k=n şi a numărului de elemente afişate.
Generarea combinărilor
Generăm combinărilor unei mulţimi presupune o condiţie suplimentară faţă de permutări sau
aranjamente. Acest lucru se datorează faptului că generarea combinărilor presupune alegerea în
ordine strict crescătoare a elementelor care compun vectorul soluţie.
Astfel, condiţia de continuare, sau de validare a unui element este aceea că el trebuie să fie strict
mai mare decât cel plasat anterior. În acest mod asigurăm faptul că elementele nu se vor repeta şi
că vor fi generate în ordine strict crescătoare. Trebuie, însă, să avem grijă să nu punem această
condiţie si asupra primului element din vectorul soluţie, deoarece el nu are cu cine să fie comparat.

O optimizare a algoritmului de generare a combinărilor se poate obţine pornind instrucţiunea for


pentru plasarea unui element de la valoare următoare valorii generate anterior. Trebuie să avem
grijă la prima poziţie, care nu are element anterior. Am putea iniţializa X0 cu 0. Astfel nu mai
trebuie să verificăm dacă elementul Xk este mai mare ca Xk-1.
Generarea produsului cartezian
Se consideră n mulţimi A1, A2, ... , An de forma {1,2..,an}. Să se genereze produsul cartezian al
acestor mulţimi.
Am considerat mulţimile de forma {1,2..,an} pentru a simplifica problema, în special la partea de
citire si afişare, algoritmul de generare rămânând nemodificat.
Identificăm următoarele particularităţi şi condiţii:
• Fiecare element Xk din vectorul soluţie aparţine unei mulţimi {1,2..,ak}. Aici este o
diferenţă faţă de algoritmii anteriori în care fiecare element din soluţie era luat din aceeaşi
mulţime şi trebuie să avem grijă la acest fapt când scriem programul.
• Nu există condiţii între elementele vectorului soluţie.
• Obţinem soluţia când s-au generat n valori.
Generarea submulţimilor unei mulţimi
Generarea submulţimilor unei mulţimi A cu n elemente se poate face cu ajutorul algoritmului de
generare a combinărilor, apelându-l repetat cu valorile 1, 2, ..., n pentru a genera submulţimile cu
un element, apoi cele cu două elemente, apoi cu 3 elemente etc.
Această modalitate de rezolvare este şi mai complicată şi mai puţin eficientă decât următoarea, care
se bazează pe generarea produsului cartezian {0,1}n. Această a doua metodă este eficientă
deoarece generează 2n soluţii, adică exact atâtea câte submulţimi are o mulţime cu n elemente.
Aşadar, generăm toate combinaţiile de lungime n cu valorile 0 şi 1. Pentru fiecare combinaţie
parcurgem soluţia X şi afişăm elementele din mulţimea A cărora le corespund valori 1 în X.
Astfel, pentru combinaţia 001011 vom afişa elementele de pe poziţiile 3, 5 şi 6 din mulţimea
iniţială.
Generarea partiţiilor unei mulţimi
Generăm partiţiilor unei mulţimi presupune împărţirea mulţimii în mulţimi nevide şi disjuncte care
reunite să dea întreaga mulţime. Putem, ca şi în cazurile anterioare, să considerăm mulţimea {1,2,
…,n}. Construim un vector soluţie în care pentru fiecare element vom trece
submulţimea în care îl vom include. Această submulţime mai este numită şi clasă.
Algoritmul generează pe rând toate modalităţile de a împărţi elementele mulţimii
{1,2,…,n} folosind mai întâi o singură clasă, apoi două, ş.a.m.d. până la n clase.
Backtracking este numele unui algoritm general de descoperire a tuturor soluțiilor unei probleme
de calcul, algoritm ce se bazează pe construirea incrementală de soluții-candidat, abandonând
fiecare candidat parțial imediat ce devine clar că acesta nu are șanse să devină o soluție validă.[1][2][3]
Exemplul de bază folosit în numeroase manuale de liceu și de nivel universitar este problema
reginelor, care cere să se găsească toate modurile în care pot fi așezate pe o tablă de șah opt regine
astfel încât să nu se atace. În abordarea backtracking, candidatele parțiale sunt aranjamente de câte
k regine pe primele k rânduri ale tablei, toate pe rânduri și coloane diferite. Orice soluție parțială ce
conține două regine care se atacă poate fi abandonată, deoarece în mod clar restul de regine nu pot
fi așezate într-o soluție validă.
Tehnica backtracking se poate aplica doar pentru probleme ce admit conceptul de „candidat parțial
de soluție” și oferă un test relativ rapid asupra posibilității ca un astfel de candidat să fie completat
către o soluție validă. Când se poate aplica, însă, backtrackingul este adesea mult mai rapid decât
căutarea prin metoda forței brute prin toți candidații, întrucât este capabilă să elimine dintr-un
singur test un mare număr de candidați.
Backtrackingul este util la rezolvarea unor probleme de satisfacere a constrângerilor, cum ar fi
cuvintele încrucișate, jocuri de sudoku și alte probleme similare. Ea stă la baza unei serii de limbaje
de programare logică, cum ar fi Icon, Planner și Prolog.
Termenul „backtrack” a fost inventat de matematicianul american D. H. Lehmer în anii 1950.[4]

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