Sunteți pe pagina 1din 75

Obiective operaonale/rezultate ateptate

La sfritul unitii de nvare Metoda de programare backtraking, elevii vor fi capabili:


O1. S recunoasc tipurile de probleme care pot fi rezolvate cu metoda backtracking.
O2. S-i nsueasc modul de funcionare a rutinei backtracking.
O3. S recunosc prile eseniale ale algoritmului: funciile specifice.
O4. S identifice corect toate soluiile unei probleme date cel puin pentru algoritmii studiai la
clas (generare permutri, aranjamente, combinri, produs cartezian, submulimi).
O5. S identifice corect valoarea de iniializare pentru orice nivel k al stivei pentru o problem
dat (generare permutri, aranjamente, combinri, produs cartezian, submulimi)
O6. S identifice corect mulimea valorilor posibile pentru un nivel k al stivei, pentru o problem
studiat. (generare permutri, aranjamente, combinri, produs cartezian, submulimi)
O7. S identifice condiiile de continuare pentru obinerea unei soluii la algoritmii studiai.
O8. S simuleze corect lucrul cu stiva.
O9. S determine corect soluiile unui algoritm de generare permutri, aranjamente, combinri, produs
cartezian pe exemple concrete.
O10. S identifice corect numrul de soluii pentru o problem de Backtracking cu un algoritm
clasic.
O11. S stabileasc valorile variabilelor utilizate n algoritm, cnd se dau datele de intrare, pentru o
problem care se rezolv prin tehnica backtracking.
O12. S scrie funciile corespunztoare unei probleme pentru algoritmii studiai.
O13. S ruleze programul n vederea afirii soluiilor.
O14. S optimizeze algoritmul.

Metoda Backtracking
1.
2.
3.
4.
5.
6.
7.
8.
9.

Aspecte teoretice
Exemplu pentru nelegerea metodei
Permutri
Aranjamente
Combinri
Problema celor n dame
Problema colorrii hrilor
Problema comis voiajorului
Problema plaii unei sume s utiliznd m tipuri de
monede
10. Backtracking recursiv
11. Aranjamente si permutari rezolvate recursiv

1. Aspecte teoretice
Metoda Backtracking este o metod de elaborare a
algoritmilor. Ea se aplic problemelor n care soluia se poate
reprezenta sub forma unui vector, X=(x1,x2,...xm), care aparine
lui S=S1xS2x...Sm
S=S1xS2x...Sm se numete spaiul soluiilor posibile
Pentru fiecare problem n parte se dau anumite condiii ntre
componentele vectorului soluie care se numesc condiii
interne
Soluiile posibile care verific condiiile interne se
numesc soluii rezultat
Metoda Backtracking i propune s genereze toate soluiile
rezultat
O metod simpl de a genera soluiile rezultat const n a
genera ntr-un mod oarecare toate soluiile posibile i de a alege
dintre acestea doar pe cele care verific condiiile interne.
Dezavantajul const n faptul c timpul cerut este foarte mare.

Metoda Backtracking urmrete s evite generarea


tuturor soluiilor posibile. Pentru aceasta elementele
vectorului x primesc pe rnd valori n sensul c lui x[k] i
se atribuie o valoare doar dac componentele din faa
sa x1, x2,...x[k-1] au primit valori.
Dac lui x[k] i s-a atribuit o valoare, nu se trece direct
la atribuirea de valori lui x[k+1], ci se verific nite
condiii de continuare, referitoare la x1, x2,...x[k-1],
x[k]. Dac condiiile de continuare au fost satisfcute,
se trece la calculul lui x[k+1]. Nendeplinirea lor
exprim faptul c oricum s-ar alege x[k+1],...,x[n], nu
se va ajunge la o soluie rezultat. Evident, ca n cazul
nendeplinirii condiiilor de continuare va trebui s se
fac o alt alegere pentru x[k]. Sau dac S[k] a fost
epuizat, s se micoreze k cu o unitate, ncercnd s se
fac o nou alegere pentru xk.

2. Exemplu pentru nelegerea


metodei
Presupunem c dorim s ne mbrcm de la un
magazin pentru o festivitate i dorim s cumprm:
pantofi, ciorapi, pantaloni, cma i cravata
astfel nct acestea s se asorteze ntre ele, s se
genereze toate modalitile de a ne mbrca.
Magazinul are
La
La
La
La
La

etajul
etajul
etajul
etajul
etajul

1
2
3
4
5

are
are
are
are
are

5 etaje
10
10
10
10
10

raioane
raioane
raioane
raioane
raioane

cu
cu
cu
cu
cu

pantofi
ciorapi
pantaloni
cmi
cravate

Deoarece soluia are mai multe componente, 5 cte


etaje are magazinul, putem folosi metoda Backtracking.
Pentru rezolvare vom folosi:
k : variabil ntreag care reprezint etajul pe care
ne gsim
x : vector care are 5 componente ntregi, adic exact
cte etaje are magazinul cu proprietatea c x[k]
reprezint numrul raionului de la care s-a cumprat pe
etajul k. n cazul de fa x[k] {1,...,10} unde
k{1,...,5}
as este o variabil ntreag care primete valoarea 1
dac pe etajul k mai sunt raioane nevizitate i
primete valoarea 0 dac pe etajul k nu mai sunt
raioane nevizitate.
ev este o variabil ntreag care primete valoarea 1
dac ce este la raionul x[k] convine i primete valoarea
0 dac ce este la raionul x[k] nu convine.

Cum se procedeaz:

se pleac de la primul etaj


din faa uii
atta timp ct nc ne aflm la un etaj , k
repetm
ne ntrebm dac mai sunt raioane pe etajul k
dac da, atunci
se verific dac ne convine ce conine raionul care urmeaz
atta timp ct mai sunt raioane i nu am gsit ce ne place.
dac am gsit atunci
dac le-am luat pe toate
atunci
se afieaz
altfel
se merge la etajul urmtor n faa uii
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 faa uii)
while (k>0) (atta timp ct nc ne aflm la un etaj , k)
{
do (repetm)
{
succ(x,k,as); (ne ntrebm dac mai sunt raioane pe etajul k)
if(as) (dac da, atunci)
valid(x,k,ev); (se verific dac ne convine ce conine raionul
care urmeaz)
}
while(as&&!ev); (atta timp ct mai sunt raioane i nu am gasit ce ne place.)
if (as) (dac am gsit atunci)
if(k==5) (dac le-am luat pe toate atunci)
afis(x,k) (se afieaz)
else (altfel)
{
k=k+1; (se merge la etajul urmtor)
x[k]=0; (n faa uii)
}
else (altfel)
k=k-1; (se coboar la etajul de jos)
}

Cum ne dm seama dac mai sunt raioane la


etajul k?
void succ(sir x,int k,int &as) (funcia care determin dac mai sunt
raioane la etajul k)
{
if(x[k]<10) (dac numrul raionului de la etajul k este mai mic
dect 10)
{ (atunci)
as=1; (mai sunt raioane pe etajul k)
x[k]=x[k]+1 (i mergem la raionul urmtor)
}
else (altfel)
as=0;
(nu mai
sunt
void afis(sir
x, int
k) raioane pe etajul k)
}{
int i;
for(i=1;i<=k;i++) (pentru i de la 1 la k)
cout<<x[i]<< ; (se afieaz x[i])
cout<<endl; (cursorul trece la linia urmtoare)
}

void valid(int &ev)


{
ev=1
} ; (presupunem c orice alegere de haine ne

Pentru realizarea programului se


parcurg urmtoarele etape:
construirea tipului sir
declararea tuturor variabilelor care apar
n cadrul programului principal
realizarea funciei succ
realizarea funciei valid
realizarea funciei afi
programul principal care conine rutina
principal

Construirea
tipului sir
#include<iostream>
#include<stdio.h>
using namespace std;
typedef int sir[100];
sir x;

Declararea tuturor variabilelor care apar n cadrul programului principal

int k, as,ev;

Realizarea funciei 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 funciei
valid
void valid(int&ev)
{
ev=1;
}

Realizarea funciei afi

void afis(sir x,int k)


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

Am gsit ce mi place

Programul principal
int main(void)
{
k=1;
x[k]=0;
while(k>0)
{
do

Afiarea rezultatulu

if(as)
if(k==5)
afis(x,k);
else
{

k=k+1;

succ(x,k,as);
if (as)
x[k]=0;

valid(ev);

}
while(as && !ev);

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
Funcia 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 condiia dac s-au fcut
toate cumprturile sau s-a ajuns la ultimul etaj se scrie
if(k==m)
3.
Dac la fiecare etaj numrul de magazine este variabil (nu 10)
condiia de testare din funcia succesor este
if(x[k]<n[k])
Atunci cnd nu exist condiii ntre componentele
vectorului soluie funcia valid are forma:
void valid(int&ev)
{
ev=1;
}

Atunci cnd componentele vectorului soluie trebuie s


fie distincte trebuie artat c x[k]x[i] pentru i=1...k-1 se
procedeaz astfel:
- se presupune c x[k] este diferit de toate elementele din faa sa
- se parcurg indicii 1...k-1 cu i
- dac x[k] nu este diferit de x[i], 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]

Permutri
O permutare a unei mulimi cu n elemente este un ir
de elemente obinut prin schimbarea ordinii
elementelor mulimii date sau chiar mulimea
nsi.
Ne gndim la generarea permutrilor atunci cnd se
d o mulime cu n elemente ca date de intrare iar
soluia este sub forma de vector, tot cu n elemente, ale
crui componente sunt distincte i aparin mulimii date.
Exemplu: Fie A={1,2,3}. Permutrile mulimii 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 mulime cu elemente de tip
ntreg. Trebuie determinate elementele mulimii { 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 mulimi A={1,2,,p} i
B={1,2,,m} se cer toate funciile
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 funcia f:AB dat astfel f(1)=2,
f(2)=1. Avem relaiile: =m(m-1)...(m-p+1).

Avem relaiile:

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 cte p.
Se observ c dac se cunoate fiecare submulime
de p elemente a mulimii de m elemente, atunci
aranjamentele se pot obine permutnd n toate
modurile posibile elementele unei astfel de mulimi.
O soluie este de forma: x1,x2,...xp
unde
x1,x2,...xpB. n plus x1,x2,...xp trebuie s fie distincte.
O soluie are p numere din mulimea B i numerele
trebuie s fie distincte.
De aici rezult c algoritmul este acelai ca la
permutri, diferena fiind dat de faptul c soluia are p

#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

void afis(sir x, int k)


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

se afieaz elementele din


mulimea A care corespund
poziiilor date de elementele

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;
}
}

Combinri
Fiind dat o mulime A cu n elemente, a combina
elementele mulimii n grupe de cte p<n
elemente nseamn a determina toi vectorii cu p
elemente ale cror componente aparin mulimii A
i sunt sortate cresctor.
Ne gndim la generarea combinrilor atunci
cnd se d o mulime cu n elemente ca date de
intrare iar soluia este sub forma unui vector cu
p<n elemente, astfel nct s nu aib importan
ordinea pe care o au n cadrul irului.
Componentele sunt sortate cresctor i aparin
mulimii date.
Fie A={1,2,3}. Combinrile mulimii A n grupe
de cte dou elemente sunt (1,2), (1,3), (2,3).

#include<iostrea
m.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[k1]]))
ev=0;
}

se verific dac
componentele aparin
mulimii date i sunt

void afis(sir x, int k)


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

se afieaz elementele din mulimea A care


corespund poziiilor 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 aeaz a k-a dam
x = vector cu componente ntregi cu
proprietatea c xk reprezint coloana pe care se
aeaz 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 situaia


n care dama k i dama i sunt situate pe
aceeai diagonal.
k-i=xk-xi
(trebuie ca damele s fie aezate
n colurile unui ptrat cu latura
K-i, respectiv xk-xi)

k
i
xi

xk

n desenul de mai jos este ilustrat situaia


n care dama k i dama i sunt situate pe
aceeai diagonal.
k-i=xi-xk
(trebuie ca damele s fie aezate
n colurile unui ptrat cu latura
K-i, respectiv xi-xk)

k
i
xk

xi

Dama k i dama i se gsesc pe


aceeai diagonal dac k-i=|xk-xi|
Dama k i dama i se gsesc pe
aceeai coloan dac xk=xi.
Dama k i dama i nu se afl e
aceeai lunie niciodat datorit
modului de construire a vectorului x

Funcia valid trebuie s verifice dac dama


k nu se afl pe aceeai coloan sau pe
aceeai diagonal cu dama i.
Deci trebuie artat 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])))

typedef int sir[100];


sir x;
int i,k,n;
int as,ev;
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((x[k]==x[i])||(ki==abs(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;
}

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 colorrii
hrilor
Fiind dat o hart cu n tri, se cer toate
modalitile de colorare a hrii, utiliznd cel mult m
culori, astfel nct 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 x k
reprezint culoarea rii cu numrul 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 hrii n program se va folosi matricea de adiacen


definit astfel:
ai , j 1 daca tara i si tara j sunt vecine
A
ai , j 0 daca tara i si tara j nu sunt vecine
Exemplu: pentru harta de mai jos:

Matricea de adiacen este:

3
2
5

0 1 0 1 1

1 0 0 1 1
A 0 0 0 1 1

1 1 1 0 1
1 1 1 1 0

Concluzie:
ara k i ara i sunt vecine, dac (a i,k=1) sau
(ak,i=1)
ara k i ara i au aceeai culoare dac x k=xi
Comentarii la funcia 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 atu


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
numr de n orae. Iniial acesta se afl ntrunul din ele, notat 1. Comis voiajorul
dorete s nu treac de dou ori prin
acelai ora iar la ntoarcere s revin n
oraul 1. Cunoscnd legturile existente
ntre orae, se cere s se tipreasc toate
drumurile posibile pe care le poate efectua
comis voiajorul.

Pentru rezolvarea problemei se vor folosi:


k: variabil ntreag care reprezint la al
ctelea 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}.

Observaie: Pentru reprezentarea grafului seva


folosi matricea e adiacen definit astfel:
ai,j=1, dac exist drum ntre oraele i i j
ai,j=0, dac nu exist drum ntre oraele i i j

Exemplu: pentru harta de


mai jos,
5

matricea de
adiacen este:

1
A 0

1
1

1
0
0
1
1

0
0
0
1
1

1
1
1
0
0

1
1

Concluzii:
ntre oraele k i i exist drum dac ak,i=1
i (ai,k=1), deci ntre oraele xk i xi exist
drum dac a[xk][xi]=1 (i a[xi][xk]=1)
oraul xk trebuie s fie diferit de oraul xi
pentru i=1...k-1
oraul xn trebuie s fie vecin cu oraul x1
adic a[xk][x1]=1

Comentarii la funcia Valid:


Trebuie verificat dac:
- exist drum ntre oraele xk-1 i xk adic
trebuie de verificat dac a[xk-1][xk]=1;
- oraul xk este diferit de toate oraele 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 corespunztoare, drumurile
5
posibile sunt:
1
1
1
1

2
2
4
5

4
5
3
3

3
3
5
4

5
4
2
2

Problema plii unei sume s


utiliznd m tipuri de
monede

Se dau suma s si m tipuri de monede


avnd valorile a1, a2, ..., am lei. Se cer
toate modalitile de plat a sumei s
utiliznd aceste monede.

Va trebui s se genereze toi vectorii de


forma X=(x1,...xm), care verific relaia:
x1a1+x2a2+...+xmam=S

Din aceast relaie se poate vedea c:


S
x1 poate lua valori ntre 0 i n 1a=

x2 poate lua valori ntre 0 i n 2aS=

......................................................
S
xk poate lua valori ntre 0 i n ka=

.......................................................
S
xm poate lua valori ntre 0 i nm a=

Trebuie s se determine elementele mulimii:


{x1,,xm}| xk{0,,m} unde k=1,,m i x1a1+x2a2+...
+xmam=S

Exemplu: S=3; m=2, a[1]=1, a[2]=2.


Programul va genera soluiile:
(1,1) ce corespunde la
1a1+1a2=11+12=3
(3,0) ce corespunde la
3a1+0a2=31+02=3

nm

n2

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. Funcia back are un parametru


ntreg k i funcioneaz astfel:
- dac s-a ajuns la o soluie, se
tiprete i se revine la nivelul
anterior
- dac nu s-a ajuns la o soluie
- se iniializeaz nivelul
- atta timp ct este
succesor pe acest nivel
- dac este valid,
funcia back se autoapeleaz
pentru k+1
- dac nu este succesor,
se trece pe nivelul k-1 prin ieirea
din funcia back.

2. Funcia succ verific dac mai sunt elemente n Sk.


3. Funcia valid verific dac sunt satisfcute condiiile
de continuare
4. Funcia afi afieaz o soluie rezultat
5. Funcia soluie returneaz
1 dac pe nivelul k s-a ajuns la soluie
0 dac pe nivelul k nu s-a ajuns la soluie
n general funcia soluie se definete astfel:
int soluie(int k)
{
return(k==n+1)
}

Permutri recursiv
funcia 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;
}

funciile 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;
}

funcia 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;
}

Funciile 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;
}

Funcia 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);
}

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