Metoda Backtracking
Download
Report
Transcript Metoda Backtracking
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
Aspecte teoretice
Exemplu pentru înţelegerea metodei
Permutări
Aranjamente
Combinări
Problema celor n dame
Problema colorării hărţilor
Problema comis voiajorului
Problema plaţii unei sume s utilizând m tipuri de monede
Backtracking recursiv
Aranjamente si permutari rezolvate recursiv
Metoda Backtracking
1. Aspecte teoretice
Metoda Backtracking este o metodă de elaborare a algoritmilor.
Ea se aplică problemelor în care soluţia se poate reprezenta sub
forma unui vector, X=(x1,x2,...xm), care aparţine lui
S=S1xS2x...Sm
- S=S1xS2x...Sm se numeşte spaţiul soluţiilor posibile
- Pentru fiecare problemă în parte se dau anumite condiţii între
componentele vectorului soluţie care se numesc condiţii
interne
- Soluţiile posibile care verifică condiţiile interne se
numesc soluţii rezultat
- Metoda Backtracking îşi propune să genereze toate soluţiile
rezultat
O metodă simplă de a genera soluţiile
rezultat constă în a genera într-un mod
oarecare toate soluţiile posibile şi de a
alege dintre acestea doar pe cele care
verifică condiţiile interne. Dezavantajul
constă în faptul că timpul cerut este
foarte mare.
Metoda Backtracking urmăreşte să evite generarea
tuturor soluţiilor posibile. Pentru aceasta elementele
vectorului x primesc pe rând valori în sensul că lui xk i se
atribuie o valoare doar dacă componentele din faţa sa x1,
x2,...xk-1 au primit valori.
Dacă lui xk i s-a atribuit o valoare, nu se trece direct la
atribuirea de valori lui xk+1, ci se verifică nişte condiţii
de continuare, referitoare la x1, x2,...xk-1 xk. Dacă
condiţiile de continuare au fost satisfăcute, se trece la
calculul lui xk+1. Neîndeplinirea lor exprimă faptul că
oricum s-ar alege xk+1,...,xn, nu se va ajunge la o soluţie
rezultat. Evident, ca în cazul neîndeplinirii condiţiilor de
continuare va trebui să se facă o altă alegere pentru xk.
Sau dacă Sk a fost epuizat, să se micşoreze k cu o unitate,
încercând să se facă o nouă alegere pentru xk.
2. Exemplu pentru înţelegerea
metodei
Pentru a înţelege mai uşor prezentăm următorul exemplu:
Presupunem că dorim să ne îmbrăcăm de la un magazin
pentru o festivitate şi dorim să cumpărăm: pantofi, ciorapi,
pantaloni, cămaşă şi cravata astfel încât acestea să se
asorteze între ele, să se genereze toate modalităţile de a ne
îmbrăca.
Magazinul are:
5 etaje
La etajul 1 are 10
La etajul 2 are 10
La etajul 3 are 10
La etajul 4 are 10
La etajul 5 are 10
raioane
raioane
raioane
raioane
raioane
cu
cu
cu
cu
cu
pantofi
ciorapi
pantaloni
cămăşi
cravate
Deoarece soluţia are mai multe componente, 5 – câte etaje
are magazinul, putem folosi metoda Backtracking. Pentru
rezolvare vom folosi:
k : variabilă întreagă care reprezintă etajul pe care ne
găsim
x : vector care are 5 componente întregi, adică exact câte
etaje are magazinul cu proprietatea că xk reprezintă numărul
raionului de la care s-a cumpărat pe etajul k. În cazul de faţă
xk {1,...,10} unde k{1,...,5}
as este o variabilă întreagă care primeşte valoarea 1 dacă pe
etajul k mai sunt raioane nevizitate şi primeşte valoarea 0
dacă pe etajul k nu mai sunt raioane nevizitate.
ev este o variabilă întreagă care primeşte valoarea 1 dacă ce
este la raionul xk convine şi primeşte valoarea 0 dacă ce este la
raionul xk nu convine.
Cum se procedează:
se pleacă de la primul etaj
din faţa uşii
atâta timp cât încă ne aflăm la un etaj , k
repetăm
ne întrebăm dacă mai sunt raioane pe etajul k
dacă da, atunci
se verifică dacă ne convine ce conţine raionul care urmează
atâta timp cât mai sunt raioane şi nu am găsit ce ne place.
dacă am găsit atunci
dacă le-am luat pe toate
atunci
se afişează
altfel
se merge la etajul următor în faţa uşii
altfel
se coboară la etajul de jos
Reprezentarea a ceea ce s-a spus mai sus este:
k=1; (se pleacă de la primul etaj)
x[k]=0; (din faţa uşii)
while (k>0) (atâta timp cât încă ne aflăm la un etaj , k)
{
do (repetăm)
{
succ(x,k,as); (ne întrebăm dacă mai sunt raioane pe etajul k)
if(as) (dacă da, atunci)
valid(x,k,ev); (se verifică dacă ne convine ce conţine raionul
care urmează)
}
while(as&&!ev); (atâta timp cât mai sunt raioane şi nu am gasit ce ne place.)
if (as) (dacă am găsit atunci)
if(k==5) (dacă le-am luat pe toate atunci)
afis(x,k) (se afişează)
else (altfel)
{
k=k+1; (se merge la etajul următor)
x[k]=0; (în faţa uşii)
}
else (altfel)
k=k-1; (se coboară la etajul de jos)
}
Cum ne dăm seama dacă mai
sunt raioane la etajul k?
dacă numărul raionului de la etajul k este mai mic
decât 10
atunci
mai sunt raioane pe etajul k
şi mergem la raionul următor
altfel
nu mai sunt raioane pe etajul k
void succ(sir x,int k,int &as) (funcţia care determină dacă mai
sunt raioane la etajul k)
{
if(x[k]<10) (dacă numărul raionului de la etajul k este mai
mic decât 10)
{ (atunci)
as=1; (mai sunt raioane pe etajul k)
x[k]=x[k]+1 (şi mergem la raionul următor)
}
else (altfel)
as=0; (nu mai sunt raioane pe etajul k)
}
Cum se realizează afişarea
pentru i de la 1 la k
se afişează x[i]
cursorul trece la linia următoare
void afis(sir x, int k)
{
int i;
for(i=1;i<=k;i++) (pentru i de la 1 la k)
cout<<x[i]<<” ”; (se afişează x[i])
cout<<endl; (cursorul trece la linia următoare)
}
void valid(int &ev)
{
ev=1; (presupunem că orice alegere de haine
ne convine)
}
Pentru realizarea programului se parcurg
următoarele etape:
construirea tipului sir
declararea tuturor variabilelor care apar în
cadrul programului principal
realizarea funcţiei succ
realizarea funcţiei valid
realizarea funcţiei afiş
programul principal care conţine rutina
principală
construirea tipului sir
#include<iostream.h>
#include<stdio.h>
typedef int sir[100];
sir x;
declararea tuturor variabilelor care apar în
cadrul programului principal
int k, as,ev;
realizarea funcţiei succ
void succ(sir x, int k, int&as)
{
if(x[k]<10)
{
as=1;
x[k]=x[k]+1;
}
else as=0;
}
Verific dacă mai sunt sau nu raioane pe etajul k
realizarea funcţiei valid
void valid(int&ev)
{
ev=1;
}
Am găsit ce îmi place
realizarea funcţiei afiş
void afis(sir x,int k)
{
int i;
for(i=1;i<=k;i++)
cout<<x[i]<<" ";
cout<<endl;
}
Afişarea rezultatului
programul principal care conţine
rutina principală
int main(void)
{
k=1;
x[k]=0;
while(k>0)
{
do
{
succ(x,k,as);
if (as)
valid(ev);
}
while(as&&!ev);
if(as)
if(k==5)
afis(x,k);
else
{
k=k+1;
x[k]=0;
}
}
else k=k-1;
}
Comentarii
1.
Dacă
la etajul 1 ar fi fost n1
raioane
la etajul 2 ar fi fost n2
raioane
.......
la etajul k ar fi fost nk
raioane
Funcţia succesor se
modifică astfel:
void succ(sir x, int k,
int&as)
{
if(x[k]<n[k])
{
as=1;
x[k]=x[k]+1;
}
else as=0;
}
Comentarii
2.
Dacă magazinul are m etaje atunci condiţia „dacă
s-au făcut toate cumpărăturile” sau „s-a ajuns la
ultimul etaj” se scrie
if(k==m)
Comentarii
3.
Dacă la fiecare etaj numărul de magazine
este variabil (nu 10) condiţia de testare
din funcţia succesor este
if(x[k]<n[k])
Atunci când nu există condiţii între
componentele vectorului soluţie
funcţia valid are forma:
void valid(int&ev)
{
ev=1;
}
Atunci când componentele vectorului soluţie trebuie să fie
distincte trebuie arătat că xkxi pentru i=1...k-1 se procedează astfel:
- se presupune că xk este diferit de toate elementele din faţa sa
- se parcurg indicii 1...k-1 cu i
- dacă xk nu este diferit de xi, atunci presupunerea este falsă
void valid(int&ev)
{
int i;
ev=1;
for(i=1;i<=k-1;i++)
if(!(x[k]!=x[i]))
ev=0;
}
X[k] nu este diferit de x[i]
Permutări
O permutare a unei mulţimi cu n elemente este un şir de
elemente obţinut prin schimbarea ordinii elementelor
mulţimii date sau chiar mulţimea însăşi.
Ne gândim la generarea permutărilor atunci când se dă o
mulţime cu n elemente ca date de intrare iar soluţia este sub
forma de vector, tot cu n elemente, ale cărui componente sunt
distincte şi aparţin mulţimii date.
Exemplu: Fie A={1,2,3}. Permutările mulţimii A sunt: (1,2,3),
(1,3,2), (2,1,3), (2,3,1), (3,1,2), (3,2,1).
Fie A={a1, a2,…,am} o mulţime cu elemente de tip întreg.
Trebuie determinate elementele mulţimii { y1, y2,…,ym }| ykA,
k=1,2,...,m, pentru yiyj pentru ij}.
Deci, x=( x1, x2,…,xm) unde x{1,...,m}, elementele vectorului
x trebuie să fie distincte.
Programul:
#include<iostream.h>
#include<stdio.h>
typedef int sir[100];
sir x;
int i,k,m;
int as,ev;
sir a;
void succ(sir x, int k, int &as)
{
if(x[k]<m)
{
as=1;
x[k]=x[k]+1;
}
else as=0;
}
void valid(sir x, int k, int &as)
{
int i;
ev =1;
for(i=1;i<=k-1;i++)
if(!(x[i]!=x[k]))
ev=0;
}
void afis(sir x, int k)
{
int i;
for(i=1;i<=k;i++)
cout<<a[x[i]]<<" ";
cout<<endl;
}
int main(void)
{
cout<<"m=";
cin>>m;
for(i=1;i<=m;i++)
cin>>a[i];
k=1;
x[k]=0;
while(k>0)
{
do
{
succ(x,k,as);
if(as)
valid(x,k,ev);
}
while(as&&!ev);
if(as)
if(k==m) afis(x,k);
else
{
k=k+1;
x[k]=0;
}
else k=k-1;
}
}
Aranjamente
Se dau două mulţimi A={1,2,…,p} şi
B={1,2,…,m} se cer toate funcţiile injective
definite pe A cu valori în B. O astfel de problemă
este una de generare a aranjamentelor de n luate
cate p (Anp).
Exemplu: p=2, n=3. Avem (1,2), (2,1), (1,3),
(3,1), (2,3), (3,2). De exemplu (2,1) este funcţia
f:A→B dată astfel f(1)=2, f(2)=1. Avem relaţiile:
=m(m-1)...(m-p+1).
Avem relaţiile:
m!
A
(m p)
p
n
= m(m-1)...(m-p+1).
Se citesc m şi p. Să se genereze toate aranjamentele de m
luate câte p.
Se observă că dacă se cunoaşte fiecare submulţime de p
elemente a mulţimii de m elemente, atunci aranjamentele se pot
obţine permutând în toate modurile posibile elementele unei
astfel de mulţimi.
O soluţie este de forma: x1,x2,...xp unde x1,x2,...xpB. În
plus x1,x2,...xp trebuie să fie distincte. O soluţie are p numere
din mulţimea B şi numerele trebuie să fie distincte.
De aici rezultă că algoritmul este acelaşi ca la permutări,
diferenţa fiind dată de faptul că soluţia are p numere, nu m ca în
cazul permutărilor.
#include<iostream.h>
#include<stdio.h>
typedef int sir[100];
sir x;
int i,k,m,p;
int as,ev;
sir a;
void succ(sir x, int k, int &as)
{
if(x[k]<m)
{
as=1;
x[k]=x[k]+1;
}
else as=0;
}
void valid(sir x, int k, int
&as)
{
int i;
ev =1;
for(i=1;i<=k-1;i++)
if(x[k]==x[i])
ev=0;
}
se verifică dacă xkxi
unde i=1,2,...,k-1
void afis(sir x, int k)
{
int i;
for(i=1;i<=k;i++)
cout<<a[x[i]]<<" ";
cout<<endl;
}
se afişează elementele din mulţimea A
care corespund poziţiilor date de
elementele vectorului x
int main(void)
{
cout<<"m=";
cin>>m;
for(i=1;i<=m;i++)
cin>>a[i];
cout<<"p=";
cin>>p;
k=1;
x[k]=0;
while(k>0)
{
do
}
{
succ(x,k,as);
if(as) valid(x,k,ev);
}
while(as&&!ev);
if(as)
if(k==p) afis(x,k);
else
{
k=k+1;
x[k]=0;
}
else k=k-1;
}
Combinări
Fiind dată o mulţime A cu n elemente, a combina
elementele mulţimii în grupe de câte p<n elemente
înseamnă a determina toţi vectorii cu p elemente ale
căror componente aparţin mulţimii A şi sunt sortate
crescător.
Ne gândim la generarea combinărilor atunci când se
dă o mulţime cu n elemente ca date de intrare iar soluţia
este sub forma unui vector cu p<n elemente, astfel încât
să nu aibă importanţă ordinea pe care o au în cadrul
şirului. Componentele sunt sortate crescător şi
aparţin mulţimii date.
Fie A={1,2,3}. Combinările mulţimii A în grupe de câte
două elemente sunt (1,2), (1,3), (2,3).
#include<iostream.h
>
#include<stdio.h>
typedef int sir[100];
sir x;
int i,k,m,p;
int as,ev;
sir a;
void succ(sir x, int k, int &as)
{
if(x[k]<m)
{
as=1;
x[k]=x[k]+1;
}
else as=0;
}
void valid(sir x, int k, int &as)
{
int i;
ev =1;
for(i=1;i<=k-1;i++)
if((k>=2)&&!(a[x[k]]>a[x[k-1]]))
ev=0;
}
se verifică dacă componentele
aparţin mulţimii date şi sunt
sortate crescător
void afis(sir x, int k)
{
int i;
for(i=1;i<=k;i++)
cout<<a[x[i]]<<" ";
cout<<endl;
}
se afişează elementele din mulţimea A care
corespund poziţiilor date de elementele vectorului x
int main(void)
{
cout<<"m=";
cin>>m;
for(i=1;i<=m;i++)
cin>>a[i];
cout<<"p=";
cin>>p;
k=1;
x[k]=0;
while(k>0)
{
do
}
{
succ(x,k,as);
if(as) valid(x,k,ev);
}
while(as&&!ev);
if(as)
if(k==p) afis(x,k);
else
{
k=k+1;
x[k]=0;
}
else k=k-1;
}
Problema celor n dame
Pentru rezolvare se vor folosi:
k = variabila întreagă ce reprezintă linia pe care se
aşează a k-a damă
x = vector cu componente întregi cu proprietatea
că xk reprezintă coloana pe care se aşează a k-a
damă
Deoarece tabla are n linii şi n coloane k{1,2,..,n}
si xk{1,2,...,n} adică:
x=(x1,x2,...,xn) unde xk{1,2,…n}, k{1,2,...,n}.
În desenul de mai jos este ilustrată situaţia în care
dama k şi dama i sunt situate pe aceeaşi
diagonală.
k-i=xk-xi
(trebuie ca damele să fie aşezate
În colţurile unui pătrat cu latura
K-i, respectiv xk-xi)
k
i
xi
xk
În desenul de mai jos este ilustrată situaţia în care
dama k şi dama i sunt situate pe aceeaşi
diagonală.
k-i=xi-xk
(trebuie ca damele să fie aşezate
În colţurile unui pătrat cu latura
K-i, respectiv xi-xk)
k
i
xk
xi
Dama
k şi dama i se găsesc pe aceeaşi
diagonală dacă k-i=|xk-xi|
Dama k şi dama i se găsesc pe aceeaşi
coloană dacă xk=xi.
Dama k şi dama i nu se află e aceeaşi
lunie niciodată datorită modului de
construire a vectorului x
Funcţia valid trebuie să verifice dacă dama k nu
se află pe aceeaşi coloană sau pe aceeaşi
diagonală cu dama i.
Deci trebuie arătat că:
xkxi şi k-i|xk-xi| pentru i=1,2,…,k-1.
adică
if((x[k]==x[i])||(k-i==abs(x[k]-x[i])))
void valid(sir x, int k, int &ev)
typedef int sir[100];
{
sir x;
ev =1;
int i,k,n;
for(i=1;i<=k-1;i++)
int as,ev;
if((x[k]==x[i])||(ki==abs(x[k]-x[i])))
void succ(sir x, int k, int &as)
ev=0;
{
}
if(x[k]<n)
void afis(sir x,int k)
{
{
as=1;
int i;
x[k]=x[k]+1;
for(i=1;i<=k;i++)
}
cout<<x[i]<<" ";
else as=0;
cout<<endl;
}
}
int main(void)
{
cout<<"n=";
cin>>n;
k=1;
x[k]=0;
while(k>0)
{
do
{
succ(x,k,as);
if(as)
valid(x,k,ev);
}
while(as&&!ev);
if(as)
if(k==n) afis(x,k);
else
{
k=k+1;
x[k]=0;
}
else k=k-1;
}
}
Problema colorării hărţilor
Fiind dată o hartă cu n tări, se cer toate modalităţile
de colorare a hărţii, utilizând cel mult m culori, astfel
încât două ţări cu frontieră comună să fie colorare
diferit. Este demonstrat faptul că sunt suficiente numai 4
culori ca orice hartă să poată fi colorată.
Pentru rezolvare se vor folosi:
k: variabilă întreagă, care reprezintă o ţară
x: vector cu componente întregi cu proprietatea xk
reprezintă culoarea ţării cu numărul k
deoarece sunt n ţări şi m culori, k={1,...,n} şi
xk={1,...,m}
x=(x1,x2,...,xn) unde xk{1,...,n}.
Pentru reprezentarea hărţii în program se va folosi matricea de adiacenţă definită
astfel:
ai , j 1 daca tarai si tara j sunt vecine
A
ai , j 0 daca tarai si tara j nu sunt vecine
Exemplu: pentru harta de mai jos:
Matricea de adiacenţă este:
4
1
3
2
5
0
1
A 0
1
1
1 0 1 1
0 0 1 1
0 0 1 1
1 1 0 1
1 1 1 0
Concluzie:
Ţara k şi ţara i sunt vecine, dacă (ai,k=1) sau
(ak,i=1)
Ţara k şi ţara i au aceeaşi culoare dacă xk=xi
Comentarii la funcţia valid:
Trebuie verificat dacă ţara k şi ţara i ce sunt ţări
vecine au culori diferite, adică dacă
ak,i=1 pentru xkxi, pentru i=1...k-1.
#include<iostream.h>
#include<math.h>
typedef int sir[100];
sir x;
int m,i,k,n,j;
int as,ev;
int a[100][100];
void succ(sir x, int k, int &as)
{
if(x[k]<n)
{
as=1;
x[k]=x[k]+1;
}
else as=0;
}
void valid(sir x, int k, int &ev)
{
ev =1;
for(i=1;i<=k-1;i++)
if((a[k][i]==1)&&(x[k]==x[i]))
ev=0;
}
void afis(sir x,int k)
{
int i;
for(i=1;i<=k;i++)
cout<<x[i]<<" ";
cout<<endl;
}
Se verifică dacă ai,k=1 atunci
xkxi, unde i=1,...k-1
int main(void)
{
cout<<"Dati numarul de tari:";
cin>>n;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
cin>>a[i][j];
a[j][i]=a[i][j];
}
k=1;
x[k]=0;
while(k>0)
{
do
{
succ(x,k,as);
if(as) valid(x,k,ev);
}
while(as&&!ev);
if(as)
if(k==n) afis(x,k);
else
{
k=k+1; x[k]=0;
}
else k=k-1;
}
}
Problema comis voiajorului
Un comis voiajor trebuie să viziteze un număr
de n oraşe. Iniţial acesta se află într-unul din ele,
notat 1. Comis voiajorul doreşte să nu treacă de
două ori prin acelaşi oraş iar la întoarcere să
revină în oraşul 1. Cunoscând legăturile existente
între oraşe, se cere să se tipărească toate
drumurile posibile pe care le poate efectua comis
voiajorul.
Pentru rezolvarea problemei se vor folosi:
k: variabilă întreagă care reprezintă la al câtelea
oraş s-a ajuns (al doilea, al treilea...)
x: vector cu componente întregi, cu proprietatea
că xk reprezintă al k-lea oraş din traseu
Pentru a evita parcurgerea unui drum de
două ori, se va recurge la strategia de a
atribui lui x1 valoarea 1, adică toate
drumurile să plece de la primul oraş. Din
acest motiv, xk{2,…,n} pentru k{2,…,n}.
x=(x1,x2,…,xn) unde xk{2,…,n} şi x1=1,
pentru k{2,…,n}.
Observaţie: Pentru reprezentarea grafului seva
folosi matricea e adiacenţă definită astfel:
ai,j=1, dacă există drum între oraşele i şi j
ai,j=0, dacă nu există drum între oraşele i şi j
Exemplu: pentru harta de mai
jos,
5
1
2
4
3
matricea de
adiacenţă este:
0
1
A 0
1
1
1 0 1 1
0 0 1 1
0 0 1 1
1 1 0 0
1 1 0 0
Concluzii:
Între oraşele k şi i există drum dacă ak,i=1 şi
(ai,k=1), deci între oraşele xk şi xi există drum
dacă a[xk][xi]=1 (şi a[xi][xk]=1)
oraşul xk trebuie să fie diferit de oraşul xi pentru
i=1...k-1
oraşul xn trebuie să fie vecin cu oraşul x1 adică
a[xk][x1]=1
Comentarii la funcţia Valid:
Trebuie verificat dacă:
- există drum între oraşele xk-1 şi xk adică trebuie
de verificat dacă a[xk-1][xk]=1;
- oraşul xk este diferit de toate oraşele prin care
s-a trecut adică xkxi pentru i=1..k-1
- dacă s-a ajuns la al n-lea oraş, trebuie să existe
un drum între acesta şi primul oraş adică dacă
k=n trebuie a[xk][x1]=1
Programul
#include<iostream.h>
#include<math.h>
typedef int sir[100];
sir x;
int i,j,k,n,as,ev;
int a[100][100];
void succ(sir x, int k, int &as)
{
if(x[k]<n)
{
as=1;
x[k]=x[k]+1;
}
else as=0;
}
void valid(sir x, int k, int &ev)
{
ev =1;
if(a[x[k-1]][x[k]]==0) ev=0;
else
{
for(i=1;i<=k-1;i++)
if(x[i]==x[k]) ev=0;
if((k==n)&&(a[x[n]][x[1]]==0)) ev=0;
}
}
void afis(sir x,int k)
{
int i;
for(i=1;i<=k;i++)
cout<<x[i]<<" ";
cout<<endl;
}
int main(void)
{
cout<<"Dati numarul de orase:";
cin>>n;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
cin>>a[i][j];
a[j][i]=a[i][j];
}
x[1]=1;
k=2;
x[k]=1;
while(k>1)
{
do
}
}
{
succ(x,k,as);
if(as) valid(x,k,ev);
}
while(as&&!ev);
if(as)
if(k==n) afis(x,k);
else
{
k=k+1;
x[k]=1;
}
else k=k-1;
Pentru exemplul dat mai sus, si matricea de
adiacenţă corespunzătoare, drumurile posibile
5
sunt:
1
1
1
1
2
2
4
5
4
5
3
3
3
3
5
4
5
4
2
2
1
2
4
3
Problema plăţii unei sume s
utilizând m tipuri de monede
Se dau suma s si m tipuri de monede având
valorile a1, a2, ..., am lei. Se cer toate
modalităţile de plată a sumei s utilizând aceste
monede.
Va trebui să se genereze toţi vectorii de forma
X=(x1,...xm), care verifică relaţia:
x1·a1+x2·a2+...+xm·am=S
Din această relaţie se poate vedea că:
S
x1 poate lua valori între 0 şi n1= a1
x2 poate lua valori între 0 şi n2= aS
......................................................
S
xk poate lua valori între 0 şi nk= a
.......................................................
S
xm poate lua valori între 0 şi nm= a
2
k
m
Trebuie să se determine elementele mulţimii:
{x1,…,xm}| xk{0,…,m} unde k=1,…,m şi
x1·a1+x2·a2+...+xm·am=S
Exemplu: S=3; m=2, a[1]=1, a[2]=2.
Programul va genera soluţiile:
(1,1) ce corespunde la 1·a1+1·a2=1·1+1·2=3
(3,0) ce corespunde la 3·a1+0·a2=3·1+0·2=3
m
k
0
1
…
0
1
…
…
…
…
…
0
1
…
n2
0
1
…
nm
nm-1
…
…
n1
1
xk
nk
Programul
#include<iostream.h>
typedef int sir[100];
sir x,n,a;
int m,i,k,S,as,ev;
void succ(sir x, int k, int &as)
{
if(x[k]<n[k])
{
as=1;
x[k]=x[k]+1;
}
else as=0;
}
void valid(int &ev)
{
ev =1;
}
void afis(sir x,int k)
{
int i,s1;
s1=0;
for(i=1;i<=k;i++)
s1=s1+x[i]*a[i];
if(S==s1)
{
for(i=1;i<=k-1;i++)
cout<<x[i]<<"*"<<a[i]<<"+";
cout<<x[k]<<"*"<<a[k]<<"="<<S<<endl;
}
}
int main(void)
{
cout<<"Dati valoarea sumei:"; cin>>S;
cout<<"Dati numarul monedelor m="; cin>>m;
cout<<"Dati valorile celor "<<m<<" monede: ";
for(i=1;i<=m;i++)
{
cin>>a[i];
n[i]=S/a[i];
}
k=1;
x[k]=-1;
while(k>0)
{
do
}
}
{
succ(x,k,as);
if(as) valid(ev);
}
while(as&&!ev);
if(as)
if(k==m) afis(x,k);
else
{
k=k+1;
x[k]=-1;
}
else k=k-1;
Bactracking recursiv
void back(int k)
{
if(solutie(k)) afis(x,k);
else
{
x[k]=0;
while(succ(x,k))
{
valid(x,k,ev)
if(ev)
back(k+1);
}
}
}
1. Funcţia back are un parametru
întreg k şi funcţionează astfel:
- dacă s-a ajuns la o soluţie, se
tipăreşte şi se revine la nivelul
anterior
- dacă nu s-a ajuns la o soluţie
- se iniţializează nivelul
- atâta timp cât este
succesor pe acest nivel
- dacă este valid,
funcţia back se autoapelează pentru
k+1
- dacă nu este succesor, se
trece pe nivelul k-1 prin ieşirea din
funcţia back.
2. Funcţia succ verifică dacă mai sunt elemente în Sk.
3. Funcţia valid verifică dacă sunt satisfăcute condiţiile de
continuare
4. Funcţia afiş afişează o soluţie rezultat
5. Funcţia soluţie returnează
1 dacă pe nivelul k s-a ajuns la soluţie
0 dacă pe nivelul k nu s-a ajuns la soluţie
În general funcţia soluţie se defineşte astfel:
int soluţie(int k)
{
return(k==n+1)
}
Permutări – recursiv
funcţia succ
#include<iostream.h>
typedef int sir[100];
sir x;
int i,k,n,ev;
int succ(sir x, int k)
{
if(x[k]<n)
{
x[k]=x[k]+1;
return 1;
}
else return 0;
}
funcţiile valid şi afiş
void valid(sir x, int k, int&ev)
{
int i;
ev=1;
for(i=1; i<=k-1;i++)
if(!(x[k]!=x[i])) ev =0;
}
void afis(sir c, int k)
{
int i;
for(i=1;i<=k-1;i++)
cout<<x[i]<<" ";
cout<<endl;
}
funcţia back
void back(int k)
{
if (k==n+1) afis(x,k);
else
{
x[k]=0;
while(succ(x,k))
{
valid(x,k,ev);
if(ev)
back(k+1);
}
}
}
programul principal
int main(void)
{
cout<<"n=";
cin>>n;
back(1);
}
Aranjamente - recursiv
#include<iostream.h>
typedef int sir[100];
sir x;
int i,k,n,ev,m;
int succ(sir x, int k)
{
if(x[k]<n)
{
x[k]=x[k]+1;
return 1;
}
else return 0;
}
Funcţiile valid şi afiş
void valid(sir x, int k, int&ev)
{
int i;
ev=1;
for(i=1; i<=k-1;i++)
if((x[k]==x[i])) ev =0;
}
void afis(sir c, int k)
{
int i;
for(i=1;i<=k-1;i++)
cout<<x[i]<<" ";
cout<<endl;
}
Funcţia back
void back(int k)
{
if (k==m+1) afis(x,k);
else
{
x[k]=0;
while(succ(x,k))
{
}
}
}
valid(x,k,ev);
if(ev)
back(k+1);
programul principal
int main(void)
{
cout<<"n=";
cin>>n;
cout<<"m=";
cin>>m;
back(1);
}