Documente Academic
Documente Profesional
Documente Cultură
Backtracking
Backtracking
PROIECT ATESTAT
INFORMATICA
TEMA:
Metoda backtracking
CUPRINS
Despre metoda backtracking
. pag.4
Prezentare
generala
pag.5
Descrierea metodei
. pag.7
Caracteristici ale metodei
. pag.11 Variante
ale metodei Backtracking
..pag.30 Probleme si exercitii
..pag 32
Bibliografie
..
pag.37
S1, S2, ..., Sn au fiecare doar cte dou elemente. Atunci exist 2
Timpul de execuie
9
2 /10 =2 /10 =0,000001024 secunde
n
9
20
9
2 /10 =2 /10 =0,001 secunde
n
9
30
9
2 /10 =2 /10 =1,074 secunde
n
9
40
9
2 /10 =2 /10 =1100 secunde
n
9
50
9
2 /10 =2 /10 =313 ore
n
9
100
9
2 /10 =2 /10 =40.196.936.841.331 ani
n
10
2. Prezentare general
Putem s spunem c metoda Backtracking se aplic la probleme n care soluia se poate reprezenta
sub forma unui vector unde mulimile S1, S2, ..., Sn sunt mulimi finite avnd s1, s2, ..., respectiv sn
1
elemente (unde am notat cu si cardinalul mulimii Si, adic numrul de elemente pe care le
conine mulimea Si).
Pentru fiecare problem concret sunt date anumite relaii ntre componentele vectorului x, numite
condiii interne.
Mulimea finit S1xS2x ... xSn (n matematic fiind numit produs cartezian, pe care s-l notm
cu S) se numete spaiul soluiilor posibile.
Soluiile posibile care satisfac condiiile interne se numesc soluii rezultat.
Ceea ce ne propunem este de a determina toate soluiile rezultat (cu scopul de a le lista sau de a alege
dintre ele pe cele care maximizeaz sau minimizeaz o eventual funcie obiectiv dat, adic soluiile
optime). Sunt i probleme n care se cere o singur soluie, iar n acest caz ne vom opri imediat ce am
determinat una dintre ele.
Aa cum am vzut, o metod simpl de determinare a soluiilor rezultat const n a genera ntr-un
mod oarecare toate soluiile posibile i, dintre acestea, de a verifica i a le alege doar pe cele satisfac
condiiile interne.
S lum dou exemple pentru a nelege mai bine la ce tipuri de probleme se aplic metoda
Backtracking, dar vom ncerca, mai nti, s le rezolvm prin metoda forei brute.
Problema 9.1. (Mulimi de litere) Fie dou mulimi de litere S1={A,B,C} i
S2={M,N}. Se cere s se determine acele perechi (x1,x2) cu
proprietatea c dac x1 este A sau B, atunci x2 nu poate fi N.
x 1 S 1 i x 2 S 2
cu
problem
spaiul
soluiilor
posibile
este
produsul
cartezian
S1 x
S2xS3xS4,
unde
=1.594.323 variante, adic un numr foarte mare de soluii posibile. Mai precis,
numrul soluiilor posibile, deci i timpul de lucru, depind exponenial de lungimea n a vectorului x.
Metoda Backtracking ncearc s evite generarea tuturor soluiilor posibile.
3.Descrierea metodei
Pentru a evita generarea tuturor soluiilor posibile, metoda Backtracking atribuie pe
rnd valori elementelor vectorului x=(x1, x2, ..., xn) n ordinea cresctoare a indicilor, ncepnd cu x1.
Astfel, lui xk i se atribuie o valoare numai dac au fost atribuite deja valori lui x1, x2, ..., xk-1. Mai mult,
odat stabilit o valoare pentru xk, nu se trece direct la atribuirea de valori lui xk+1, ci se verific nite
condiii de continuare referitoare la x1, x2, ..., xk. Aceste condiii stabilesc situaiile n care are sens s
trecem la
calculul
lui
xk+1,
nendeplinirea
lor
exprimnd
faptul
oricum
am
alege
xk+1,...,xn nu vom putea ajunge la o soluie rezultat, adic o soluie pentru care condiiile interne s fie
satisfcute. Deci, verificarea condiiilor de continuare este strict necesar pentru obinerea unei soluii.
De exemplu, dac n problema 9.2 componentele x1 i x2 au primit amndou valoarea X, atunci lui
x3 nu i se mai poate atribui valoarea X (pentru a nu nclca restricia ca numrul maxim de pronosticuri X
s fie cel mult 2, aceasta nsemnnd c nu sunt ndeplinite condiiile de continuare).
Evident c n cazul nendeplinirii condiiilor de continuare va trebui s facem o alt alegere pentru xk
sau, dac Sk a fost epuizat, se merge napoi la mulimea Sk-1 i se ncearc s se fac o nou alegere
pentru componenta precedent xk-1 a vectorului (ceea ce nseamn s micorm pe k cu o unitate) dup
care nainm la Sk i facem o nou alegere pentru xk (reconsidernd toate valorile din Sk). Aceast
revenire (prin micorarea lui k) d numele metodei, ilustrnd faptul c atunci cnd nu putem avansa,
revenim la componenta anterioar din soluie.
Este evident c ntre condiiile de continuare i condiiile interne exist o strns legtur.
O bun alegere pentru condiiile de continuare are ca efect o reducere a numrului
de calcule. Faptul c valorile curente v1, v2, ..., vk-1 ale lui x1, x2, ..., respectiv xk-1 satisfac condiiile
de continuare nu este suficient pentru a garanta c se va obine o soluie ale crei prime k-1 componente
coincid cu valorile v1, v2, ..., vk-1.
De exemplu, n cazul problemei 9.2, chiar dac valorile curente pentru x1 i x2 sunt
X, iar pentru x3 este 1 (deci aceste valori ndeplinesc condiiile de continuare), totui
(X, X, 1, 1) nu este soluie.
De obicei condiiile de continuare reprezint restricia condiiilor interne pentru primele k componente
ale vectorului. Este evident c n cazul k=n condiiile de continuare sunt chiar condiiile interne.
Prin metoda Backtracking, orice vector soluie este construit progresiv, ncepnd cu prima component
i mergnd ctre ultima, cu eventuale reveniri asupra valorilor atribuite anterior (cu unul sau mai muli
pai napoi, atta timp ct e posibil revenirea). Prin atribuiri sau ncercri de atribuire euate din cauza
nerespectrii condiiilor de continuare, anumite valori sunt "consumate" (reamintim c x1, x2, ..., xn
primesc valori
n mulimile finite S1, S2, ..., Sn). Vom nota cu Ck mulimea valorilor consumate deja
la momentul curent pentru componenta xk evident Ck S k .
Dac ne imaginm c vectorul x mai are o component suplimentar xn+1 (care nu
poate lua nici o valoare, adic Sn+1=) i ncercm s dm valori componentei k=n+1, atunci
putem spune c vectorul v=(v1, v2, ..., vn) a ndeplinit condiiile de continuare (ce coincid cu
condiiile interne) i prin urmare v este o soluie rezulat. n practic, aceast condiie este cea utilizat de
obicei pentru a sesiza obinerea unei soluii.
O problem important este aceea a determinrii momentului n care se ncheie procesul de gsire a
tuturor soluiilor. Avnd n vedere c mulimile S1, S2, ..., Sn sunt mulimi finite, iar prin reveniri
succesive nu se ajunge de dou ori la aceeai soluie parial, rezult c, la un moment dat, tot revenind, se
va ajunge la momentul n care i pentru x1 se epuizeaz toate valorile din S1, n aceast situaie putnd
s ne imaginm c vectorul x mai are o component suplimentar x0 (care nu poate lua nici o valoare,
adic S0=) i n acest caz nseamn c-am epuizat toate situaiile de a obine o soluie rezultat. n
practic, aceast condiie (k=0) este cea utilizat de obicei pentru a sesiza obinerea tuturor soluiilor
rezultat.
Prin urmare, algoritmul general pentru metoda Backtracking poate fi sintetizat astfel:
Iniializeaz mulimile de valori S1, S2, ..., Sn
C1, C2, ..., Cn {Iniial vectorul soluie nu are valori}
k1; {Se pornete de la prima component}
ct timp k>0 execut {Nu s-au construit toate soluiile}
dac k=n+1 atunci {S-a construit o soluie}
Reine/afieaz soluia v=(v1, v2, ..., vn)
kk-1 {Revine dup construirea unei soluii}
altfel
dac Ck<>Sk atunci {Mai exist valori neconsumate}
Alege o valoare vk din Sk\Ck; CkCk U {vk };
dac v=(v1,v2,...,vk) satisfac condiiile de continuare atunci {Atribuie i avanseaz}
xkvk; kk+1
altfel {ncercare euat}
altfel {Revenire}
Ck; kk-1
deci fiecare mulime Sk este format din primele sk numere naturale, ncepnd cu 1. Prin urmare,
mulimea Sk poate fi reprezentat simplu prin numrul su de elemente sk. Se presupune de asemenea
c valorile pentru fiecare component xk vor fi alese n ordine cresctoare.
k1;
ct timp k> 0 execut
dac k=n+1 atunci { S-a construit o soluie}
retsol; {Rene/afieaz soluia}
kk-1; {Revenire dup construirea unei soluii}
altfel
dac x[k]<s atunci {Mai exist valori neconsumate pentru x[k]}
x[k]x[k]+1; {Se alege valoarea urmtoare}
dac continuare(k) atunci kk+1; {Avanseaz}
altfel {Revenire}
x[k]0; kk-1
cout<<"1
";
break;
case
void backtracking()
{
int k=1;init(1);
while (k>0)
if (k==N+1)
{retsol(); k--;}
else
if (x[k]<S)
{
x[k]++;
if (continuare(k)) k++;
}
else {init(k); k--;};
}
int main()
{
backtracking();
return 0;
}
ncepnd cu primul element al vectorului x la care adugm pas cu pas urmtoarele elemente, numai c,
dac nu se mai poate nainta, se revine la pasul anterior, reconsiderndu-se toate elementele). n acest
fel pot fi determinate toate soluiile rezultat. n funcie de cerinele problemei, ne putem mulumi cu o
singur soluie sau le determinm pe toate. Sunt probleme (de optimizare) n care ni se cere ca dintre toate
soluiile rezultat s alegem, pe baza unor anumite criterii, doar pe cele care le satisfac (acele soluii care
maximizeaz sau minimizeaz o anumit funcie, numit funcie de optim).
eviden
condiiile
de
continuare prin aplicarea metodei Backtracking vom presupune c pe tabla de ah sunt plasate corect
damele de pe primele k-1 linii. Atunci dama de pe linia k poate fi plasat pe linia k dac sunt ndeplinite
urmtoarele condiii:
1) pe coloana xk s nu mai fi fost plasat nici o dam adic x k
x i i k .
2) pe diagonala ce trece prin csua (k,xk) s nu mai fi fost plasat nici o dam
#include <iostream>
#include <algorithm>
using namespace std;
#define NR 20
int n, x[NR];
int continuare(int k)
{
int i,b;
b=1; i=1;
while (b && (i<k))
if ((x[k]==x[i]) || (abs(x[k]-x[i])==k-i)) b=0;
else i++;
return b;
}
void retsol()
{
int i;
for (i=1;i<=n;i++) cout<<x[i]<<" ";
cout<<"\n";
}
void backtracking()
{
int k=1,i;
for (i=1;i<=n;i++) x[i]=0;
while (k!=0)
if (k==n+1)
{
retsol(); k--;
}
else
if (x[k]<n)
{
x[k]++;
if (continuare(k))
k++;
}
else
{
x[k]=0;
k--;
}
}
int main()
{
cout<<"Dimensiunea tablei de sah n= ";
cin>>n;
backtracking();
}
Soluie. Vecorul x conine numerele naturale n care poate fi descompus n. Observm c el are
lungimi diferite, n funcie de soluia determinat de algoritm. Mai observm c lungimea maxim a unei
soluii este chiar n, deoarece acesta poate fi descompus astfel: 1+1++1 (n valori, toate egale cu 1),
aceasta fiind prima soluie.
Mulimile S1, S2 , , Sn sunt toate egale cu muimea {1,2, ,n}, prin urmare, pentru ca xk s
aib successor, e suficient ca xk<n.
Condiia de continuare este ca suma s=x1+x2++xk<n. Se ajunge la o soluie
atunci cnd s=n. Programul e urmtorul:
#include <iostream> using
namespace std; const int
NR=30;
int x[NR],k,n,i;
void init(int k)
{
x[k]=0;
}
int succesor(int k)
{
if ( x[k]<n ) return 1;
else return 0;
}
int continuare(int k)
{
int s=0;
for(i=1;i<k;i++) s+=x[i];
if(s<n) return 1;
else return 0;
}
int solutie(int k)
{
int s=0;
for(i=1;i<k;i++) s+=x[i];
if(s==n) return 1;
else return 0;
}
void afisare()
{
int i;
cout<<x[1];
for(i=2;i<k;i++) cout<<'+'<<x[i];
cout<<'\n';
}
void back()
{
k=1; init(1);
while (k!=0)
{
if (solutie(k)) {afisare();k--;}
else
if (succesor(k))
{
x[k]++;
if (continuare(k)) k++;
}
else {init(k); k--;}
}
}
int main(void)
{
cout<<"Dati n = "; cin>>n;
back();
return 0;
}
Metoda backtracking poate fi reprezentat uor, pe un arbore construit astfel:
- nivelul 1 conine rdcina;
- din orice vrf de pe nivelul k pleac sk muchii spre nivelul k+1 etichetai cu cele
sk muchii ale lui Sk.
Nivelul n+1 va conine s1 s2 ... sn vrfuri. Pentru fiecare vrf de pe nivelul
n+1, etichetele muchiilor coninute pe drumul ce leag rdcina de acest vrf reprezint
o soluie posibil. S lum i un exemplu.
Dac ntr-un vrf condiiile de continuare nu mai sunt verificate, se va renuna la parcurgerea
subarborelui care are ca rdcin acel vrf, n acest fel eliminnd foarte multe dintre soluiile posibile ce
nu pot fi soluii rezultat.
Diferitele variante ale metodei backtracking nu schimb esena ei care const n faptul c poate fi
reprezentat pe un arbore care este parcurs "cobornd" numai dac exist anse de a ajunge la o soluie
rezultat (aceast metod de parcurgere a arborilor fiind cunoscut sub numele DF (Depth First n
adncime) i a fost propus de Trmaux n secolul al XIX-lea ca tehnic de rezolvare a problemelor
legate de parcurgerea labirintelor).
Programul e urmtorul:
#include <iostream>
using namespace std;
int x[20],a[20],k,m,i,n,nrv=0;
void sortare()
{
int schimb=1,t;
while(schimb)
{
schimb=0;
for(i=1;i<n;i++)
if(a[i]>a[i+1])
{t=a[i];a[i]=a[i+1];a[i+1]=t;schimb=1;}
}
}
void init()
{
x[k]=0;
}
int succesor()
{
int s=0; for(i=1;i<=k;i++)s+=a[x[i]]; if(s<m)
{x[k]++;return 1;} else return 0;
}
int continuare()
{
for(i=1;i<k;i++)
if(x[i]>=x[i+1])return 0;
return 1;
}
int solutie()
{
int s=0; for(i=1;i<=k;i++)s+=a[x[i]]; return
(s==m);
}
void afiseaza()
{
nrv++;cout<<'B'<<nrv<<"={";
for(i=1;i<=k;i++)
cout<<a[x[i]]<<' ';
cout<<"}\n";
}
void backtracking()
{
int as; k=1;init(); while(k>0)
{
do {} while ((as=succesor())&&(!continuare()));
if(as)
if(solutie()) afiseaza();
else {k++;init();}
else k--;
}
}
int main()
{
cout << "suma= ";cin>>m;
cout<<"n= ";cin>>n;
for(i=1;i<=n;i++)
{
cout<<"a["<<i<<"]= ";cin>>a[i];
}
sortare();
backtracking();
if(!nrv)cout<<"Nu exista solutie";
return 0;
}
Backtracking nu poate fi nlocuit prin alte metode mai eficiente cum ar fi, de exemplu, problemele
n care se cer toate soluiile acceptabile.
Deci, avnd un timp de execuie exponenial, utilizarea metodei Backtracking se
justific numai atunci cnd nu cunoatem o alt metod cu eficien mai mare.
Backtracking recursiv
Pn aici am studiat numai varianta iterativ pentru metoda Backtracking. n
continuare vom aborda modalitatea de implementare recursiv a metodei Backtracking.
Presupunem c toate cele n mulimi n care pot lua valori componentele vectorului sunt identice cu
mulimea finit S={1,2,...,s}. n aceast situaie, varianta recursiv a metodei Backtracking este
urmtoarea:
backtracking(ntreg k);
ntreg i ;
dac k = n+1 atunci retsol {Reine soluia}
altfel
pentru i = 1, s execut
x[k] := i;
dac continuare(k)
atunci Backtracking(k+1)
Parametrul k din aceast rutin reprezint numrul de ordine al componentei lui x ce urmeaz s
primeasc o valoare din S. Pentru iniierea procesului de generare a soluiilor, n unitatea de program
care iniiaz procesul backtracking se va utiliza apelul:
backtracking(1)
Colorarea hrilor. Fie dat o hart ca cea din figura de mai jos, n care sunt prezentate schematic 6
ri T1, T2, T3, T4, T5 i T6, dintre care unele au granie comune
T2
T4 T5
T1
T3
T6
O hart cu 6 ri
Presupunnd c dispunem doar de trei culori (galben, albastru i rou), se cere s se determine toate
variantele de colorare a hrii, care s respecte condiia ca orice dou ri vecine (care au frontier
comun) s fie colorate diferit.
Soluie. Pentru a memora relaia de vecintate dintre ri este folosit matricea
vecin definit prin:
vecini, j
Pentru harta din figura de mai sus, matricea vecin2 corespunztoare este urmtoarea:
0
1
0
1 0 1 0
0 1 1 1
1 0 0 1
1 0 0 1
1 11 1 0
1 0 10 1 1
0
1
{
vecin[i][j]=1;vecin[j][i]=1;
cin>>j;
}
}
cout<<"\n";
return 0;
}
int backtracking(int k)
{
int i;
if (k==N+1) retsol();
else
for (i=1;i<=S;i++)
{
x[k]=i;
if (continuare(k)) backtracking(k+1);
}
return 0;
}
int main()
{
citeste();
cout<<"Tara:
6"<<'\n';
backtracking(1);
return 0;
}
Soluie. Valorile celor n monede sunt reinute de vectorul v. Astfel, v[1] va reine valoarea monedei de
tipul 1, v[2] valoarea monedei de tipul 2, .a.m.d. Numrul de monede din fiecare tip va fi reinut de
vectorul sol. Astfel, sol[1] reine numrul de monede de tipul 1, sol[2] reine numrul de monede de tipul
2, .a.m.d.
Orice soluie are exact n componente (pentru monedele care nu sunt luate n calcul n vectorul sol se
reine 0, din acest motiv la nceput, fiecare component a vectorului sol va fi iniializat cu valoarea -1).
La
pasul
avem
la
dispoziie
suma
obinut:
b=(10,9,4),
varianta
matricii ce constituie traseul de deplasare a obiectului, aceti indici putem s spunem c reprezint
coordonatele unor puncte din planul XOY reprezentnd traseul de deplasare.
(0,0)
x
y1
y2
y3
yf-1
yf
Y
x2
x3
xf-1
xf
j0
i0
M
Exemplu de caroiaj
S presupunem c un mobil (pies de ah, robot etc.) pleac din punctul (ptratul) iniial (i0,j0), unde
i0 reprezint numrul liniei, iar j0 reprezint numrul coloanei i c el se deplaseaz conform unor
reguli sintetizate (memorate) n doi vectori di i dj
avnd o dimensiune d dat, cu urmtoarea semnificaie: dac mobilul se afl n punctul de coordonate (i,
j), el se poate deplasa doar ntr-unul dintre punctele (i+di[k],j+dj[k]), k=1,2,...,d, bineneles cu condiia
ca s nu ias din caroiaj.
De exemplu, s presupunem c mobilul se poate deplasa doar astfel:
- cu o poziie la dreapta pe aceeai linie;
- cu o poziie la stnga pe aceeai linie;
- cu o poziie n sus pe aceeai coloan;
- cu o poziie n jos pe aceeai coloan.
Ca
urmare
acestor
posibiliti
de
micare
mobilului,
vom
avea: di=(0,0,1,-1) i
dj=(1,-1,0,0), astfel c, de exemplu, pentru k=2 vom avea di[k]=0 i dj[k]=-1 ceea ce are urmtoarea
semnificaie: mobilul se deplaseaz zero linii i o coloan n jos (semnul minus indicnd micarea
mobilului la stnga i n jos iar semnul plus indicnd micarea la dreapta i n sus).
n practic apar de nenumrate ori astfel de situaii i, mai mult, n unele probleme, se consider c
anumite ptrate ale caroiajului sunt "ocupate" (ceea ce nseamn c mobilul nu poate fi plasat prin
deplasri succesive ntr-un astfel de ptrat). Bineneles c ptratul iniial se presupune c nu este ocupat.
Frecvent se ntlnesc probleme ce constau n:
- simularea micrii mobilului;
- determinarea ptratelor n care mobilul poate s ajung prin deplasri permise;
- determinarea unei succesiuni de deplasri n urma crora mobilul poate s ajung
ntr-un punct (if,jf) dat, dac o astfel de succesiune exist;
- determinarea celei mai scurte succesiuni de deplasri n urma crora mobilul poate s ajung ntr-un
punct (if,jf) dat, accesibil din punctul iniial (traseul optim).
Astfel de probleme pot fi rezolvate i prin aplicarea metodei Backtracking (dar n unele situaii sunt
mult mai eficiente alte metode cum ar fi, de exemplu, metoda Branch and Bound).
Pentru rezolvarea unor astfel de probleme cu metoda Backtracking, vectorului x ntlnit pn acum
n acest capitol va avea n continuare o nou semnificaie. Elementele sale vor lua valori n
mulimea {1,2,...,d} iar semnificaia sa este urmtoarea: dac dup k-1 mutri mobilul a ajuns n
poziia (i,j), atunci, dup urmtoarea sa mutare, mobilul va ajunge n poziia (i+di[x[k]],j+dj[x[k]]). Cu
alte cuvinte, vectorul x va conine numrul de ordine al deplasrilor permise (adic va conine indicele la
care s-a ajuns n tablourile di i dj).
Funcia continuare va fi tot o funcie boolean ce permite s determinm dac o anumit ncercare de
deplasare este permis, adic nu se ajunge la vreuna dintre eventualele poziii ocupate i nu se iese din
caroiaj.
Rutina Backtracking este asemntoare cu cele utilizate anterior n acest capitol numai c vor aprea
n plus n ea variabilele i i j cu semnificaia c (i,j) este poziia curent a mobilului.
S aplicm toate acestea la o problem concret.
Sritura calului. Fie dat o "tabl de ah", de dimensiune nxn. Se pleac cu un cal din poziia (1,1),
adic din colul din stnga sus. Nefiind interzis nici o poziie, se cere s se efectueze cu calul,
conform regulilor jocului de ah, o succesiune de mutri astfel nct calul s treac o dat i numai
o dat prin fiecare ptrat al tablei de ah.
Soluie. Ca urmare a posibilitilor micrii calului notat n figura de mai jos cu C,
ptrelele negre reprezentnd locurile unde poate fi mutat calul, vom avea:
di=(1,2,2,1,-1,-2,-2,-1) i
dj=(2,1,-1,-2,-2,-1,1,2).
Se va utiliza o matrice a. Pentru ca verificrile efectuate de funcia continuare s fie mai uoare, vom
"borda" matricea a cu dou prime linii, cu dou ultime linii, cu dou prime coloane i cu dou ultime
coloane, ptratele nou introduse prin bordare intrnd n categoria celor ocupate (interzise). Bordarea s-a
fcut cu cte 2+2 linii i 2+2 coloane deoarece calul poate s sar cu cte dou ptrele la dreapta, sus,
stnga sau jos. Drept urmare, liniile i coloanele vor fi numerotate cu -1,0,1,...,n,n+1,n+2. Ne propunem
ca n final matricea a s ilustreze traseul calului, deci s conin elementele 1,2,...,n2 astfel nct o sritur
a calului de pe o poziie pe poziia urmtoare s corespund la numere succesive n matrice. Poziiile
interzise vor primi valoarea -1, iar cele libere (poziiile efective ale tablei de ah) vor primi valoarea 0
(aceste iniializri fiind efectuate de rutina iniializare). Prin urmare, calul poate s treac n poziia (i,j)
dac i numai dac a[i,j]=0.
Pentru a evita determinarea de soluii simetrice, vom pune a[2,3]=2 i vom ncepe cu k=2, a[1,1]=1
i a[2,3]=2. De altfel vom urmri determinarea unei singure soluii pentru c vom vedea c i aa
timpul de execuie va fi destul de mare chiar pentru valori mici ale lui n. Deficiena acestui
algoritm const n faptul c ordinea de deplasare a calului plecnd din poziia iniial este prestabilit
prin vectorii di i dj, fixai de la nceput.
Rutina backtracking() se termin cnd au fost ocupate toate cele n2 cmpuri ale tablei de ah.
Programul este urmtorul:
#include <iostream>
using namespace std;
int
di[8]={1,2,2,1,-1,-2,-2,-1},
dj[8]={2,1,-1,-2,-2,-1,1,2},
[13],x[100],
i,n,n2;
a[13]
int initializare()
{
int i,j;
for (i=0;i<=n+3;i++)
{
a[0][i]=1; a[1][i]=1; a[n+2]
[i]=1; a[n+3][i]=1; a[i][0]=1;
a[i][1]=1; a[i][n+2]=1; a[i]
[n+3]=1;
}
for (i=2;i<=n+1;i++)
for (j=2;j<=n+1;j++) a[i][j]=0;
return 0;
}
int continuare(int i,int j)
{
if (a[i][j]==0) return 1;
else return 0;
};
int retsol()
{
int i,j;
for (i=2;i<=n+1;i++)
{
for (j=2;j<=n+1;j++)
if (a[i][j]>9) cout<<a[i][j]<<" ";
else cout<<" "<<a[i][j]<<" ";
cout<<"\n";
}
return 0;
}
int backtracking()
{
int k,i,j; a[2][2]=1;a[3][4]=2;
i=3;j=4;k=2;x[k]=-1;
while (k>1)
if (k==n2) k=0;
else
if (x[k]<7)
{
x[k]++;
if (continuare(i+di[x[k]],j+dj[x[k]]))
{
i=i+di[x[k]];j=j+dj[x[k]];
k++;a[i][j]=k;x[k]=-1;
}
}
else
{
a[i][j]=0;k--;
i=i-di[x[k]];j=j-dj[x[k]];
}
return 0;
}
int main()
{
cout<<"n= ";cin>>n;cout<<"\n";
initializare(); n2=n*n;
backtracking();
retsol();
return 0;
}
Problema sriturii calului poate fi rezolvat folosind i alte metode de programare
(ca, de exemplu, metoda Branch and bound sau algoritmii probabilistici cum ar fi algoritmii Las
Vegas) ce permit obinerea soluiei ntr-un timp mult mai scurt.
return 0;
}
6.Probleme si exercitii
Sa se afiseze in ordine alfabetica anagramele unui cuvant format din litere distincte.
Exemplu:
date.in
rac
date.out
acr
arc
car
cra
rac
rca
REZOLVARE:
#include <fstream>
#include <cstring>
using namespace std;
ifstream is("date.in");
ofstream os("date.out");
int n;
char v[100],st[50], p[50];
void scrie()//afisez litere conform permutarii
{
int i;
{
for(i=1;i<=n;i++)
os<<v[st[i]-1];
os<<'\n';
}
}
Se citesc n numere naturale. Determinati o aranjare a acestor numere sub forma unui
cerc, astfel incat suma produselor de cate doua numere alaturate sa fie maxima.
Exemplu:
date.in
6
183254
date.out
124853
REZOLVARE:
#include <fstream>
using namespace std;
ifstream is("date.in");
ofstream os("date.out");
int x[20],n,b[20],pmax=0;
void afis()
{
for (int i = 1; i <= n; i++) os<<b[i]<<" ";
os<<endl;
}
void alege()//alege permutarea pentru care se obtine suma maxima
{
int p=x[1]*x[n];
for(int i=1;i<n;i++)
p=p+x[i]*x[i+1];
if(p>pmax)
{
pmax=p;
for(int i=1;i<=n;i++) b[i]=x[i];
}
}
void inter(int &x, int &y)//interschimba doua valori
{
int aux=x; x=y; y=aux;
}
void perm(int k, int n)//genereaza permutarile
{
for (int i = k; i <= n; i++)
{
inter(x[k], x[i]);
if(k==n) alege();
else perm(k+1, n);
inter(x[k], x[i]);
}
}
int main()
{
is>>n;
for(int i=1;i<=n;i++) is>>x[i];
perm(1,n);
afis();
return 0;
}
Se citeste un numar natural n. Afisati permutarile multimii 1,2,3...n in care
elementele pare sunt puncte fixe (se afla pe pozitie egale cu valoarea lor).
Exemplu:
n=5
permutarile care respecta conditia sunt:
12345
12543
32145
32541
52143
52341
(2 si 4 sunt puncte fixe)
REZOLVARE:
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");