Sunteți pe pagina 1din 35

LICEUL COLEGIUL NATIONAL GEORGE COSBUC MOTRU

PROIECT ATESTAT
INFORMATICA

ELEV: DAFINOIU ADRIAN


TEMA: METODA BACKTRACKING
CLASA: A XII-A C
PROFESOR COORDONATOR: CIOCAN NATALIA

TEMA:
Metoda
backtracking

CUPRINS
Prezentarea generala .
pag.4 Exemple de implementare a
metodei pag. 6
Probleme si exercitii
. pag.24
Bibliografie .
pag.34

1.Prezentare general
Imaginai-v c astzi este ziua vostr i avei invitai. Aranjai o mas
frumoas, apoi v gndii cum s v aezai invitaii la mas. Ai vrea s tii toate
posibilitile de aezare a invitailor la mas, dar realizai n acelai timp c trebuie s
inei seama i de preferinele lor. Printre invitai exist anumite simpatii dar i unele
antipatii, de care dorii neaprat s inei seama, pentru ca petrecerea s fie o bucurie
pentru toi.
S ne gndim cum procedai pentru a identifica toate posibilitile de a plasa
invitaii la mas. ncepei prin a scrie nite cartonae cu numele invitailor.
Alegei un invitat.
Pentru a-l alege pe al doilea, selectai din mulimea cartonaelor rmase un alt
invitat. Dac tii c cele dou persoane nu se agreeaz, renuntai la cartonaul lui i
alegei altul i aa mai departe.
Se poate ntmpla ca la un moment dat, cnd vrei s alegei cartonaul unui
invitat s constatai c nici unul dintre invitaii rmai nu se potrivete cu ultima
persoan slectat pn acum. Cum procedai?
Schimbai ultimul invitat plasat cu un invitat dintre cei rmai i ncercai din
nou, dac nici aa nu reuiti, schimbai penultimul invitat cu alcineva i ncercai din
nou i aa mai departe pn cnd reuii s plasati toi invitaii. nseamn c ai
obinut o soluie.
Pentru a obine toate celelalte soluii, nu v rmne dect s o luai de la
nceput.

Avei

cam

mult

de

muncit,

iar

dac

numrul

invitailor

este

mare...operaiunea devine foarte anevoioas. Iat de ce avei nevoie de un calculator


i cunotine de programare .

Backtracking este o metod de parcurgere sistematic a spaiului


soluiilor posibile al unei probleme.
Este o metod general de programare, i poate fi adapt pentru orice
problem pentru care dorim s obinem toate soluiile posibile, sau s selectm o
soluie optim, din mulimea soluiilor posibile.
Backtracking este ns i cea mai costisitoare metod din punct de vedere
al timpului de execuie.

n general vom modela soluia problemei ca un vector v=(v1,v2,v3,...,vn) n


care fiecare element vk aparine unei mulimi finite i ordonate Sk, cu k=1,n. n
anumite cazuri particulare, mulimile S1 ,S2, S3,...,Sn pot fi identice . Procedm astfel:
1. La fiecare pas k pornim de la o soluie parial v=( v1,v2,v3,...,vk-1)
determinat
pn n acel moment i ncercm s extindem aceast soluie adugnd un nou
element la sfritul vectorului.
2. Cutm n mulimea Sk , un nou element.
3. Dac exist un element neselectat nc, verificm dac acest element
ndeplinete condiiile impuse de problem, numite condiii de continuare.
4. Dac sunt respectate condiiile de continuare, adugm elementul soluiei
pariale.
5. Verificm dac am obinut o soluie complet.
-

dac am obinut o soluie complet o afim i se reia algoritmul de


la pasul 1.

dac nu am obinut o soluie, k <----- k+1 si se reia algoritmul de la


pasul 1.

6. Dac nu sunt respectate condiiile de continuare se reia algoritmul de la pasul 2.


7. Dac nu mai exist nici un element neverificat n mulimea Sk nseamn c nu
mai avem nici o posibilitate din acest moment, s construim soluia final
aa c
trebuie s modificm alegerile fcute n prealabil, astfel k <----- k-1 i se reia
problema de la pasul 1.
Revenirea n caz de insucces sau pentru generarea tuturor soluiilor problemei, a
condus la denumirea de backtracking a metodei, traducerea aproximativ ar fi
revenire n urm.
Forma general a unei funcii backtracking
Implementarea recursiv a algoritmului furnizat de metoda backtracking, este
mai natural i deci mai uoar. Segmentul de stiv pus la dispoziie prin apelul
funciei este gestionat n mod automat de sistem. Revenirea la pasul precedent se
realizeaz n mod natural prin nchiderea nivelului de stiv.

void BK(int k)
//k-poziia din vector care se completeaz
{int i;
for (i=1; i<=nr_elemente_Sk; i++)
//parcurge elementele mulimii Sk
{ v[k]=i;
//selecteaz un element din mulime
if (validare(k)==1)
//valideaz condiiile de continuare ale problemei
{ if (solutie(k)==1)
//verific dac s-a obinut o soluie
afisare(k);
//afieaz soluia
else
BK(k+1);
//reapeleaz functia pentru poziia k+1
}
}
//dac nu mai exist nici un element neselectat n mulimea Sk,
}
//se nchide nivelul de stiv i astfel se revine pe poziia k-1 a
//vectorului
//execuia funciei se ncheie, dup ce s-au nchis toate nivelurile stivei, nseamn c n vectorul v nu
mai poate fi selectat nici un elemente din multimile Sk

2. Exemple de implementare a metodei:


1. PERMUTRI
S se genereze toate permutrile primelor n numere naturale.

Vom genera pe rnd soluiile problemei n vectorul v=(v1,v2,v3,...,vn), unde vkSk.


S facem urmtoarele observaii:
1. Pentru aceast problem toate mulimile Sk sunt identice, Sk={1,2,3,....,n}.
La pasul k selectm un element din mulimea Sk.
2.

ntruct n cadrul unei permutri elementele nu au voie s se repete


aceast condiie reprezent condiia de continuare a problemei.

3.

Obinem o soluie n momentul n care completm vectorul cu n elemente.

Exemplu pentru n=3

S1= S2= S3={1,2,3}

(1,2,3) (1,3,2) (2,1,3) (2,3,1) (3,1,2) (3,2,1)


v
k=1
1
k
1
2
v
k=2
1
1
k
1
2
element incorect (se repet)
v
k=2
1
2
k
1
2

v
1

k=3
2
1

3
k
3

1
2
3 element
incorect (se repet)
v
1

k=3
2
2

k
1
2
element incorect (se repet)

v
k=3
1
2
3
k
1
2
3
k=3
s-a obinut o soluie
se nchide ultimul nivel de stiv pentru
c nu mai sunt elemente n ultima
mulime
v
k=2
1
3
k
1
2
3
v
k=3
1
3
1
k
1
2
element incorect (se repet)
v
1
3
2
k
1
2
k=3
s-a obinut o soluie

v
k=3
1
3
3
k
1
2
3
element incorect (se repet)
v
k=2
1
3
k
1
2
3
v
k=1
2
k
1
2
3
se repet aceiai pai construindu-se
soluiile urmtoare

STIVA
i=1
STIVA

i=1 i=1
STIVA
i=1,2 i=1
STIVA
i=1
i=1,2
i=1
STIVA
i=1,2
i=1,2
i=1
STIVA
i=1,2,3
i=1,2
i=1

STIVA
i=1,2,3
i=1
STIVA
i=1
i=1,2,3
i=1
STIVA
i=1,2
i=1,2,3
i=1
STIVA
i=1,2,3
i=1,2,3
i=1
STIVA
i=1,2,3
i=1
STIVA
i=1,2

#include <iostream.h> //
PERMUTRI
const MAX=20;
int n,v[MAX] ;
//n-nr. de elemente, v[20]-vectorul n care construim soluia
int valid(int k); int
solutie(int k); void
afisare(int k); void
BK(int k);
int main()
{cout<<"n= ";cin>>n; //se citete n
BK(1);
return 0;
//apelm funcia BK pentru completarea poziiei 1din vectorul v
}
void BK(int k)
{int i;
//i-elementul selectat din multimea Sk, trebuie sa fie variabil local, pentru
// a se memora pe stiv
for (i=1;i<=n;i++)
//parcurgem elementele mulimii Sk
{v[k]=i;
//selectm un element din mulimea Sk
if (valid(k))
//verificm dac eelementul ales ndeplinete condiiile de continuare
{if (solutie(k))
//verificm dac am obinut o soluie
afisare(k);
//se afieaz soluia obinut
else
BK(k+1);
//reapemm funcia pentru poziia k+1 din vectorul v
}
}
}
int valid(int k)
//verificm condiiile de continuare
{int i;
for (i=1;i<=k-1;i++) //comparm fiecare element din vectorul v cu ultimul element selectat
if (v[i]==v[k])
//deoarece ntr-o permutare elementele nu au voie s se repete,
return 0;
//returnm 0 n cazul n care elementul selectat, a mai fost selectat o dat
return 1;
//returnm 1 n cazul n care elementul nu mai apare n vector
}
int solutie(int k)
//verificm dac am obinut o soluie
{if (k==n)
//am obinut o permutare, dac am reuit s depunem n vector n elemente
return 1;
return 0;
}
void afisare(int k)
//afieaz coninutul vectorului v
{int i;
for (i=1;i<=k;i++)
cout<<v[i]<<" ";
cout<<endl;

Problema generrii permutrilor, este cea mai reprezentativ pentru metoda


backtracking, ea conine toate elementele specifice metodei.
Probleme similare, care solicit determinarea tuturor soluiilor posibile,
necesit doar adaptarea acestui algoritm modificnd fie modalitatea de selecie a
elementelor din mulimea Sk, fie condiiile de continuare fie momentul obinerii unei
soluii.

2. PRODUS CARTEZIAN
Se dau n mulimi, ca cele de mai jos:
S1={1,2,3,...,w1}
S2={1,2,3,...,w2}
.........................
Sn={1,2,3,...,wn}
Se cere s se gnereze produsul lor cartezian.

Exemplu:
pemtru n=3 i urmroarele mulimi
S1={1,2}

w1=2

S2={1,2,3}

w2=3

S3={1,2}

w3=2

produsul cartezian este:


S1 xS2 xS3 ={ (1,1,1),(1,1,2),(1,2,1),(1,2,2),(1,3,1),
(1,3,2), (2,2,1),(2,1,2),(2,2,1),(2,2,2),
(2,3,1),(2,3,2)}
Prin urmare o soluie este un ir de n elemente, fiecare element iSi, cu i=1,n
S analizm exemplul de mai sus:
1. La pasul k selectm un element din mulimea Sk ={1,2,3,...,w k}.
2. Elementele unei soluii a produsului cartezian, pot s se repete, pot fi n
orice ordine, iar valori strine nu pot apare, deoarece le selectm
doar dintre elementele mulimii Sk

. Prin urmare nu trebuie s

impunem condiii de continuare.


3. Obinem o soluie n momentul n care am completat n elemente n
vectorul v.
Vom memora numrul de elemente al fiecerei mulimi Sk , ntr-un vector w.
Soluiile le vom construi pe rnd n vectorul v.

#include <iostream.h>
#include <fstream.h>
const MAX=20;
int n,v[MAX],w[MAX];
void BK(int k);
void citire();
void afisare(int k);
int solutie(int k);
void main()
{citire();
BK(1);
}
void BK(int k)
{int i;
for (i=1;i<=w[k];i++)
{v[k]=i;
if (solutie(k))
afisare(k);
else
BK(k+1);
}
}
void citire()
{int i;
ifstream f("prod.in");
f>>n;
for(i=1;i<=n;i++)
f>>w[i];
f.close();
}
int solutie(int k)
{if (k==n)
return 1;
return 0;
}
void afisare(int k)
{int i;
for (i=1;i<=k;i++)
cout<<v[i]<<" ";
cout<<endl;
}

//

PRODUS CARTEZIAN

//n-nr. de mulimi, v-vectorul soluie, w-conine nr. de elemente di


//fiecare mulime Sk

//citire date
//apelm funcia BK pentru selectarea primului element n v
//funcia becktreacking
//parcurgem mulimea Sk ={1,2,3,...,w k}
//selectm elementul i din mulimea Sk
//nu avem funcie de validare- nu avem condiii de continuare
//verificm dac am obinut o soluie
//afim soluia
//reapelm funcia BK pentru completarea poziiei urmtoare n
// vectorul v
//se nchide un nivel de stiv si astfel se ajunge la poziia k-1 n v
//citirea datelor
//se citete numrul de mulimi
//se citete numrul de elemente al fiecrei mulimi
//funcia soluie determin momentul n care se ajunge la o soluie
//obinem o soluie dac am dpus n vectorul v, n elemente

//afueaz o soluie

3. ARANJAMENTE
Se citesc n i p numere naturale cu p<=n. Sa se genereze toate aranjamentle
de n elemente luate cte p.
Exemplu pentru n=3, p=2
(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)
Vom genera pe rnd soluiile problemei n vectorul v=(v1,v2,v3,...,vn), unde
vkSk.
S facem urmtoarele observaii:
1. pentru aceast problem toate mulimile Sk sunt identice, Sk={1,2,3,....,n}.
2. la pasul k selectm un element din mulimea Sk.
ntruct n cadrul unei aranjri, elementele nu au voie s se repete
aceast condiie reprezent condiia de continuare a problemei.
3. Oinem o soluie n momentul n care completm vectorul cu p elemente.
S observm c problema generrii aranjamentelor, nu difer prea mult de problema
generrii permutrilor. Singura deosebire pe care o sesizm este aceea c obinem o
soluie n momentul n care am plasat n vector p elemente.
Prin urmare, n cadrul programului pentru generarea permutrilor trebuie sa
modificm o singur funcie i anume funcia soluie, astfel:
int solutie(int k)
{if (k==p)
return 1;
return 0;
}

//verificm dac am obinut o soluie


//am obinut o aranjare, dac am reuit s depunem n vector p elemente

4. COMBINRI
Se citesc n i p numere naturale cu p<=n. S se genereze toate combinrile
de n elemente luate cte p.
Exemplu pentru n=3, p=2.

obinem

(1,2), (1,3), (2,3)

Vom genera pe rnd soluiile problemei n vectorul v=(v1,v2,v3,...,vn), unde


vkSk.
S

facem

urmtoarele

observaii:
1. Pentru aceast problem toate mulimile Sk sunt identice, Sk={1,2,3,....,n}.
La pasul k selectm un element din mulimea
Sk.
2. n cadrul unei combinri elementele nu au voie s se repete.
S mai observm i faptul c dac la un moment dat am generat de exemplu
soluia (1,2), combinarea (2,1) nu mai poate fi luat n considerare, ea nu mai
reprezint o soluie. Din acest motiv vom considera c elementele vectorului
reprezint o soluie, numai dac se afl n ordine strict cresctoare.
Acestea

reprezint

condiiile

de

continuare

ale

problemei.
3.

Oinem o soluie n momentul n care vectorul conine p elemente.

Putem genera toate elementele unei combinri, parcurgnd mulimea {1,2,3,...,n},


apoi s verificm condiiile de continuare aa cum am procedat n cazul permutrilor.
Putem ns mbuntii timpul de execuie, selectnd din mulimea {1,2,3,...,n}, la
pasul k un element care este n mod obligatoriu mai mare dect elementul v[k1], adic i=v[k-1]+1.
Ce se ntmpl ns cu primul element plasat n vectorul v?
Acest element a fost plasat pe poziia 1, iar vectorul v deine i elementul v[0] n mod
implicit n C++. v[0]=0, deoarece vectorul v este o variabil global i se iniializeaz
automat elementele lui cu 0.
n acest fel, impunnd aceste restricii nc din momentul seleciei unui element,
condiiile de continuare vor fi respectate i nu mai avem nevoie de funcia valid.
Algoritmul a fost substanial mbuntit, deoarece nu selectm

toate

elementele mulimii i nu verificm toate condiiile de continuare, pentru fiecare


element al mulimii.

#include <iostream.h>
// COMBINRI
const MAX=20;
int n,p,v[MAX] ;
int solutie(int k);
void afisare(int k);
void BK(int k);
void main()
{cout<<"n= ";cin>>n; cout<<"p= ";cin>>p;
BK(1);
}
void BK(int k)
{int i;
for (i=v[k-1]+1;i<=n;i++) //la pasul k selectm din mulime un element mai mare dect
elementul
{v[k]=i;
//de pe poziia k-1
if (solutie(k))
//nu este necesar s verificam condiiile de continuare, ele sunt
respectate afisare(k); //datorit modului n care am selectat elementele.
else
BK(k+1);
}
}
int solutie(int k)
{if (k==p) return 1;
return 0;}
void afisare(int k)
{int i;
for (i=1;i<=k;i++) cout<<v[i]<<" ";
cout<<endl;
}

4. SUBMULIMI
S se genereze toate submulimile mulimii S={1,2,3, ... ,n}.
Exemplu: pentru n=3, S={1,2,3}, submulimile sunt urmtoarele:
-mulimea vid, {1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}
S observm c pentru a obine toate submulimile unei mulimi, este suficient
n

s generm pe rnd C 1 , C 2 ,..., C n 1 , pentru mulimea S={1,2,3, ... ,n}, la care


trebuie s adugm mulimea vid i mulimea S.
n aceste condiii, este suficient s modificm doar funcia principal pentru a

genera toate submulimile i afiarea datelor ca mulimi de elemente. Funiile BK i


p
soluie genereaz n realitate
Cn

elemente.

#include <iostream.h>
//
SUBMULIMI 1
const MAX=20;
int n,p,v[MAX] ;
int solutie(int k);
void afisare(int k);
void BK(int k);
void main()
{int i;
cout<<"n= ";cin>>n;
cout<<"mulimea vida"<<endl;
p
for(p=1;p<=n-1;p++)
//generm C n elemente
BK(1);
cout<<"{";
for(i=1;i<n;i++)
cout<<i<<", ";
cout<<n<<"}";
}
void BK(int k)
{int i;
for (i=v[k-1]+1;i<=n;i++)
{v[k]=i;
if (solutie(k))
afisare(k);
else
BK(k+1);
}
}
int solutie(int k)
{if (k==p)
return
1;
return 0;
}
void afisare(int k)
{ cout<<"{ ";
for (int i=1;i<k;i++) cout<<v[i]<<", ";
cout<<v[k]<<" }"<<endl;
}

Putem construi un algoritm mai eficient pentru generarea tuturor submulimilor


unei mulimi.
De exemplu dac genetm mulimile n urmtoarea ordine:
mulimea vid, {1}, {1,2}, {1,2,3}, {2}, {2,3}, {3}

#include <iostream.h>
const MAX=20;
int n,p,v[MAX] ;
int valid(int k);
int solutie(int k);
void afisare(int k);
void BK(int k);

//SUBMULIMI 2

void main()
{int i;
cout<<"n= ";cin>>n;
cout<<"mulimea vida"<<endl;
BK(1);
}
void BK(int k)
{int i;
for (i=v[k-1]+1;i<=n;i++)
{v[k]=i;
afisare(k);
BK(k+1);
}
}

5. Problema celor n dame

Fiind dat o tabl de ah de dimensiune nxn, se cere s se aranjeze cele n dame


n toate modurile posibile pe tabla de ah, astfel nct s nu se afle pe aceeai linie,
coloan, sau diagonal (damele s nu se atace).

Exemplu pentru n=4 o soluie este:

D
D
D
D

Observm c o dam va fi plasat ntotdeauna singur pe o linie. Acest lucru


ne permite s memorm fiecare soluie ntr-un vector v, considernd c o csut k a
vectorului reprezint linia k iar coninutul ei, adic v[k] va conine numrul coloanei
n care vom plasa regina.

Pentru exemplul de mai sus, vectorul v va avea urmtorul coninut:

S facem urmtoarele observaii:


1. Pentru aceast problem toate mulimile Sk sunt identice, Sk={1,2,3,....,n} i
reprezint numrul coloanelor tablei de ah. Indicele k reprezint numrul liniei
tablei de ah.
Prin urmare, la pasul k selectm un element din mulimea Sk.

2.

Condiiile de continuare ale problemei :


a) Damele s nu fie pe aceeai linie - aceast condiie este ndeplinit prin modul
n care am organizat memorarea datelor i anume ntr-o csu a vectorului
putem trece un singur numr de coloan.
b) Damele s nu fie pe aceeai coloan adic v[k]v[i], cu 1<=i<=k-1.
c)

Damele s nu fie pe aceeai diagonal. Dac dou dame se gasesc pe


aceeai diagonal nseamn c cele dou distane msutae pe linie respectiv
pe coloan, dintre cele dou dame sunt egale. Prin urmare condiia ca damele
s nu fie pe aceeai diagonal este: |v[k]-v[i]| k-i , cu 1<=i<=k-1.

v[i]
linia i

linia k

v[k]
D

3. Obinem o soluie dac am reuit s plasm toate cele n dame, adic k=n.

S urmrim modul n care se completeaz vectorul soluie v i o reprezentare


grafic a ceea ce nseamn valorile vectorului v pe tabla de ah.
Reprezentarea descrie completarea vectorului pn la obinerea primei soluii,
pentru n=4.
Algoritmul de backtracking continu n aceeai manier, pn la generarea
tuturor soluiilor.

v
1

k=1

v
1
v
1

k=2
1

k=2

k=2
1
3
pe linia 3 nu mai putem plasa nici o
dam, selectm alt valoare pe linia 2 v
k=2
1
4
v
1
v
1

D
D

k=3
4

k=3

4
2
-pe linia 4 nu putem plasa nici o dam
-pe linia 3 nici o alt coloan nu este
corect
-pe linia 2 nu mai exist nici o poziie
disponibil
-se revine la linia 1

v
2

k=1

v
2
v
2
v

k=2
1
k=2

D
D

2
k=2
2
v
2

v
2

3
k=2
4
D

k=3

v
2
v
2
v
2

k=4
D

k=4
k=4

Algoritmul continu pentru a genera


toate soluiile.

D
D
D
Am obinut prima soluie !

#include <iostream.h>
#include <math.h>
#define MAX 20

//

DAME

int n,v[MAX],sol;
int valid(int k);
int solutie(int k);
void afisare();
void BK(int k);
void main()
{cout<<"n= ";cin>>n;
BK(1);
}
void BK(int k)
{int i;
for (i=1;i<=n;i++)
{v[k]=i;
if (valid(k)==1)
{if (solutie(k)==1)
sfisare();
else
BK(k+1);
}
}
}
int valid(int k)
{int i;
for (i=1;i<=k-1;i++)
if ((v[i]==v[k])||(abs(v[k]-v[i])==(k-i)))
return 0;
return 1;}
int solutie(int k)
{if (k==n)
return 1;
return 0;}
void afisare()
//afisam solutiile sub forma unei matrice
{int i,j,x;
sol++; cout<<"\n Solutia: "<<sol<<'\n';
for (i=1;i<=n;i++)
{for (j=1;j<=n;j++)
if (v[i]==j) cout<<"D ";
else
cout<<"_ ";
cout<<'\n';
}
}

6. Plata unei sume cu monede de valori date.


Fiind dat o sum S i n monede de valori date, s se determine toate modalittile de
plat a sumei S cu aceste monede. Considerm c sunt monede suficiente din
fiecare tip.
#include <iostream.h>
// PLATA SUMEI
#include <fstream.h>
#define MAX 20
int n=0,x,v[MAX],w[MAX],z[MAX],S,Suma,sol;
//v-vectorul soluie,w-valoarea monedelor,z-nr.maxim de monede de un anumit tip
void citire();
int valid(int k);
int solutie();
void afisare(int k);
void BK(int k);
void main()
{citire();
BK(1);
}
void BK(int k)
{int i;
for (i=0;i<=z[k];i++)
{v[k]=i;
if (valid(k)==1)
{if (solutie()==1)
afisare(k);
else
BK(k+1);
}
}
}
void citire()
{int i;
ifstream f("c:\\casa\\sanda\\probleme\\cpp\\back\\monede.in");
f>>S>>n;
//se citete suma S i numrul de monede
for(i=1;i<=n;i++)
{f>>w[i];
//se citesc valorile monedelor
z[i]=S/w[i];} //z-memorez numrul maxim de monede de un anumit tip, penru a plati suma S
int valid(int k)
{int i;
Suma=0;
for (i=1;i<=k;i++)
Suma=Suma+v[i]*w[i];
if ((Suma<=S)&&(k<=n))
return 1;
return 0;}
int solutie()
{if (Suma==S)
return 1;
return 0;}
void afisare(int k)
{int i;
sol++;cout<<"Solutia :"<<sol<<endl;
for (i=1;i<=k;i++)
if (v[i]) cout<<v[i]<<" monede de valoarea "<<w[i]<<endl; cout<<endl;}

7. Problema rucsacului
ntr-un rucsac se poate transporta o anumit greutate maxim G.
O persoan dispune de n obiecte. Pentru fiecare obiect se cunoare greutatea i
ctigul pe care persoana l poate obine transportnd acelst obiect.
Ce obiecte trebuie s transporte persoana respectiv pentru a obine un ctig
maxim.
Datele de intrare se citesc din fiierul RUCSAC.IN astfel:
- linia 1: n G -unde n este numrul de obiecte i G greutatea maxim admis
de rucsac
- linia i: nume[i] g[i] c[i]
unde:

-nume este numele obiectului


-g este greutatea obiectului
- c este catigul obinut pentru acel obiect
cu i=1,n

Exemplu:
RUCSAC.IN
4 20
pantaloni 5 5
caciula 10 3
camasa 10 7
pantofi 5 2

pentru datele de intrare de mai sus, soluia optim este:

Castigul maxim: 14
pantaloni 5 5
camasa 10 7
pantofi 5 2

Dup cum observai prolema nu solicit obinerea tuturor soluiilor ci determinarea


soluiei optime.
Pentru a determina soluia optim vom genera cu metoda backtracking toate
soluiile i vom reine dintre acestea doar soluia cea mai bun. Aceasta presupune
s nu afim fiecare soluie ci, n momentul obinerii unei noi soluii o vom compara

cu soluia precedent, n cazul n care ctigul obinut este mai mare dect
precedentul vom reine aceast soluie. Vom considera ctigul iniial 0.

Vom folosi urmtoarele variabile:

n-numrul de obiecte
G

- greutatea maxim admis de rucsac

nume[ ][ ]

- reinem renumirea obiectelor

g[ ]

- reinem greutatea fiecrui obiect

c[ ]

-reinem ctigul pentru fiecare obiect

v[ ]

-vectorul soluie:
0-obiectul nu este transportat,
1-obiectul este transportat

s_max

-reine ctigul maxim

sol_max

-reine soluia maxim

#include <stdio.h>
#include <iostream.h>
#include <fstream.h>
#define MAX 20
int n,v[MAX],sol_max[MAX],g[MAX],c[MAX],s,s_max,G,gr,nr_sol;
char nume[MAX][MAX];
void back(int k);
int valid(int k);
void optim();
void citire();
void afisare();
main()
{citire();
back(1);
afisare();
//afisam solutia optima
}
void back(int k)
{ int i;
for(i=0;i<=1;i++)
{v[k]=i;
//0-obiectul k sete NEselectat, 1-obiectul k este selectat
if (valid(k))
if (k==n) optim();
//din multimea solutiilor vom retine doar solutia optima
else back(k+1); }
}
int valid(int k)
{ gr=0;
for(int j=1;j<=k;j++)
gr=gr+v[j]*g[j];
//-insumam greutatile obiectelor selectate pana la pasul k
if(gr<=G) return 1
//verificam daca greutatea cumulata nu depaseste greutatea maxima G
else return 0;
}
void optim()
{int s=0; nr_sol++;
for(int j=1;j<=n;j++)
s=s+v[j]*c[j];
//s-calculam suma ctigurilor obiectelor selectate
if((nr_sol==0)||(s>s_max))
//daca s>suma maxima, solutia este mai buna
{s_max=s;
//retinem solutia in sol_max
for(j=1;j<=n;j++)
sol_max[j]=v[j];
}
}
void citire()
{ ifstream f("RUCSAC.IN");
f>>n>>G;
//n-nr. obiecte, G-greutatea maxima
for (int i=1;i<=n;i++)
f>>nume[i]>>g[i]>>c[i];
//se citeste greutatea si ponderea fiecarui obiect
f.close();
}
void afisare()
{ nr_sol++;
cout<<"Castigul maxim:"<<s_max<<"\n";
for (int j=1;j<=n;j++)
if(sol_max[j]) cout<<nume[j]<<" "<<g[j]<<" "<<c[j]<<endl;
cout<<"\n";
}

3.Probleme si exercitii
Scrieti un program care afiseaza in fisierul cercuri.out toate
modalitatitile de inlocuire a literelor din imaginea alaturata cu cifre de
la 1 la 9 astfel incat suma cifrelor din fiecare cerc sa fie aceeasi.

REZOLVARE:
#include <fstream>
using namespace std;
ofstream fout("cercuri.out");
int X[11],P[10];
void afis()
{
for(int i=1;i<=9;i++)
fout<<X[i]<<" ";
fout<<endl;
}
int ok(int k)
{
if(k==9) if(X[8]+X[9]!=X[1]+X[2]) return 0;
else return 1;
if(k>3 && k%2==0)
if(X[k-2]+X[k-1]+X[k]!=X[1]+X[2]) return 0;
return 1;
}

void back(int k)
{
for(int i=1;i<=9;i++)
if(!P[i])
{
X[k]=i;
P[i]=1;
if(ok(k)) if(k==9) afis();
else back(k+1);
P[i]=0;
}
}
int main()
{
back(1);
return 0;
}

Hercule trebuie sa strabata un labirint cu capcane reprezentat de o matrice


nXn. Pentru fiecare celula a labirintului, se cunoaste timpul in minute dupa
care celula respectiva devine capcana. Dupa ce o celula devine capcana,
Hercule moare daca intra in acea celula.
Hercule porneste din coltul stanga-sus al labirintului si trebuie sa ajunga in
coltul dreapta jos. El are nevoie de un minut ca sa treaca dintr-o celula intruna vecina si se poate deplasa in sus, in jos, spre stanga sau spre dreapta.
Sa se afiseze timpul minim in care poate Hercule sa strabata labirintul,
numarul de drumuri de timp minim, precum si toate drumurile minime pe
care le poate urma Hercule prin labirint de la intrare la iesire, astfel incat
Hercule sa nu moara. Drumurile vor fi afisate ca matrici in care sunt indicati
pasii lui Hercule.

Exemplu:
hercule.in
66
345678
311119
5 6 7 12 11 10
1 7 1 13 1 1
1 8 1 14 15 16
1 9 10 11 12 17
hercule.out
11 4
100000
200000
345600
000700
0 0 0 8 9 10
0 0 0 0 0 11
100000
200000
345600
000700
000890
0 0 0 0 10 11
100000
200000
345600
000700
000800
0 0 0 9 10 11

100000
200000
340000
050000
060000
0 7 8 9 10 11
REZOLVARE:
#include <fstream>
using namespace std;
ifstream is("hercule.in");
ofstream os("hercule.out");
const int di[]={-1,0,1,0};
const int dj[]={0,1,0,-1};
int a[100][100], x[100][100], m,n,pmin=10000,nr=0;
void citire()
{
is>>m>>n;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
is>>a[i][j];
}
void afis(int x[100][100])
{
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
os<<x[i][j]<<" ";
os<<endl;
}

os<<endl;
}
void

alege(int

pas)//alege

minimul

si

numarul

de

drumuri

de

lungime minima
{
if(pas<pmin) { pmin=pas; nr=1;}
else if(pas==pmin) nr++;
}
void back(int i, int j, int pas)
{
x[i][j]=pas;
if(i==m && j==n) alege(pas);
else
for(int d=0;d<4;d++)
{
int inou=i+di[d];
int jnou=j+dj[d];
if(pas<a[inou][jnou] && x[inou][jnou]==0 && pas<pmin)
//inca nu e capcana, e pozitie noua si nu s-a depasit nr min de
pasi
back(inou,jnou,pas+1);
}
x[i][j]=0;
}
void backmin(int i, int j, int pas) //face doar drumurile de
lungime minima
{
x[i][j]=pas;
if(i==m && j==n && pas==pmin) afis(x);
else
for(int d=0;d<4;d++)
{

int inou=i+di[d];
int jnou=j+dj[d];
if(pas<a[inou][jnou] && x[inou][jnou]==0 && pas<pmin)
backmin(inou,jnou,pas+1);
}
x[i][j]=0;
}
int main()
{
citire();
back(1,1,1);
os<<pmin<<" "<<nr<<endl;
backmin(1,1,1);
is.close();
os.close();
return 0;
}

sau
#include <fstream>
using namespace std;
ifstream is("hercule.in");
ofstream os("hercule.out");
const int di[]={-1,0,1,0};
const int dj[]={0,1,0,-1};
int a[100][100], x[100][100], m,n,pmin=10000,nr=0;
void citire()
{
is>>m>>n;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)

is>>a[i][j];
}
void afis(int x[100][100])
{
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
os<<x[i][j]<<" ";
os<<endl;
}
os<<endl;
}
void alege(int pas)
{
if(pas<pmin) { pmin=pas; nr=1;}
else if(pas==pmin) nr++;
}
void creste()
{
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
a[i][j]++;
}
void scade()
{
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
a[i][j]--;
}
void back(int i, int j, int pas)

{
scade();
x[i][j]=pas;
if(i==m && j==n) alege(pas);
else
for(int d=0;d<4;d++)
{
int inou=i+di[d];
int jnou=j+dj[d];
if(a[inou][jnou]>0 && x[inou][jnou]==0 && pas<pmin)
{
back(inou,jnou,pas+1);
}
}
x[i][j]=0;
creste();
}
void backmin(int i, int j, int pas) //face doar drumurile de
lungime minima
{
scade();
x[i][j]=pas;
if(i==m && j==n && pas==pmin) afis(x);
else
for(int d=0;d<4;d++)
{
int inou=i+di[d];
int jnou=j+dj[d];
if(a[inou][jnou]>0 && x[inou][jnou]==0 && pas<pmin)
{
backmin(inou,jnou,pas+1);
}
}
x[i][j]=0;

creste();
}
int main()
{
citire();
back(1,1,1);
os<<pmin<<" "<<nr<<endl;
backmin(1,1,1);
is.close();
os.close();
return 0;
}

Se citeste un numar natural n. Generati si afisati toate combinatiile de cate n


cifre binare care nu au doua cifre de 1 alaturate.
Exemplu:
n=3
combinatiile sunt:
000
001
010
100
101
REZOLVARE:
#include <fstream>
using namespace std;
ifstream is("date.in");
ofstream os("date.out");
int x[15],n;

void afis()
{
for(int i=1;i<=n;i++)
os<<x[i]<<" ";
os<<endl;
}
void back01(int k)
{
for(int i=0;i<=1;i++)
{
x[k]=i;
if(k>1) if(x[k]*x[k-1]!=1)
if(k==n) afis();
else back01(k+1);
else ;
else back01(k+1);
}
}
int main()
{
is>>n;
back01(1);
is.close();
os.close();
return 0;
}

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