Sunteți pe pagina 1din 30

Probleme rezlvate prin

Backtracking
Prof.dr.ing. Stefan-Gheorghe Pentiuc
Facultatea de Inginerie Electric i tiina Calculatoarelor
Universitatea tefan cel Mare Suceava
Problema mesei rotunde
Cum trebuie asezati n cavaleri la o masa
rotunda cu n locuri astfel incat alaturi sa nu
stea 2 rivali ?
Varianta modern: cum trebuie
aezai reprezentantii unor firme care
pot fi concurente
Datele problemei:
n = numarul de firme (identificate prin 0 .. n-1)
concurenta firmelor este reprezentata printr-o matrice
(simetrica) c[nxn],
c[i][j]=1 indicand faptul ca firmele i si j sunt concurente

Solutia problemei va fi data printr-un tablou de intregi
mr[n] ,
mr[i] indica firma al carei reprezentant ocupa scaunul i

Solutia: tablou de intregi mr[n]
In cursul rezolvarii se va utiliza si un tablou logic
asezat[n],
asezat[i]=FALS, daca reprezentantul firmei i nu i-a
fost fixat nc locul la masa.

Se va utiliza si functia concurente(x,y) care va furniza
ADEVARAT atunci cand firmele x si y sunt concurente.
In implementareain C va fi realizata prin macrodefinitia

#define concurente(i,j) (c[i][j])
Algoritm: aplicare Bkt
procedura MasaRotunda(k) este
daca k==n atunci
solutie() @
altfel
pentru i=0, n-1 executa
daca asezat[i]=FALS SI Posibil(i,k) atunci
asezat[i]=ADEVARAT
mr[k]=i
MasaRotunda(k+1)
asezat[i]=FALS
@
@
@
sfirsit
Posibil() si Solutie()
functia Posibil (i,k) este
daca k>0 SI concurente(mr[k-1],i) !=0
atunci intoarce FALS @
daca k==n-1 SI concurente(mr[0],i) != 0
atunci intoarce FALS @
intoarce ADEVARAT
sfrsit

Procedura solutie() va incrementa
numarul solutiilor si va afisa solutia gasita.

Subiect de examen
La o Masa rotunda privind Cresterea rolului ntreprinderilor n finantarea
nvatamntului superior au fost invitate sa trimita cte un reprezentant n firme.
Este cunoscut faptul ca fiecare firma invitata are una sau mai multe firme concurente
printre celelalte invitate. Scrieti un program care sa aseze participantii la o masa
rotunda astfel nct reprezentantii a doua firme concurente sa nu stea alaturi.

Structura fisier de intrare
=====================
Firmele sunt codificate prin numere intregi de la 1 la n
Programul va citi matricea care da concurenta firmelor astfel:

n // nr. de firme
a1 a2 a3 0 // unde ai reprez nr. a 3 firme cu care este concurenta firma 1
b1 b2 0 // bi = numerele a 2 firme cu care este concurenta firma 2
....
0 // pentru firma i nu se indica nici o concurenta
...
In total n+1 inregistrari.

Programul va afisa urmatorul meniu
C - citire date despre concurenta firmelor
A - afisare matrice concurenta (simetrica)
R - rezolva problema
I - info autor
T - termina programul

La optiunea C daca numele fisierului de date nu este precizat in linia de
comanda atunci va fi citit de la consola. Daca de la consola se da enter atunci
numele fisierului se considera ex.dat.

Fisierul ex.dat
===========
5
2 0
3 0
0
5 0
3 0

La comanda A se va afisa pentru fisierul ex.dat urmatoarea matrice


Matricea concurentei
0 1 0 0 0
1 0 1 0 0
0 1 0 0 1
0 0 0 0 1
0 0 1 1 0


Solutia va fi afisata astfel
============================

Varianta 1 de asezare la masa rotunda: 1 3 4 2 5

Varianta 2 de asezare la masa rotunda: 1 5 2 4 3

Varianta 3 de asezare la masa rotunda: 2 4 3 1 5

Varianta 4 de asezare la masa rotunda: 2 5 1 3 4

Varianta 5 de asezare la masa rotunda: 3 1 5 2 4

Varianta 6 de asezare la masa rotunda: 3 4 2 5 1

Varianta 7 de asezare la masa rotunda: 4 2 5 1 3

Varianta 8 de asezare la masa rotunda: 4 3 1 5 2

Varianta 9 de asezare la masa rotunda: 5 1 3 4 2

Varianta 10 de asezare la masa rotunda: 5 2 4 3 1


Fisierul ex.dat
===========
5
2 0
3 0
0
5 0
3 0

Problema mingii
Problema mingii
//
// Se considera un deal reprezentat prin matricea
cotelor mxn
// Dintr-un anumit punct se lasa libera o minge.
// Programul stabileste toate drumurile posibile
parcurse de minge
// pentru a iesi din zona reprezentata de matricea
cotelor
//


Deplasarea mingii
//
// Implementeaza metoda Backtracking recursiv
// Apel initial :
cale[x0][y0]=1;
CautaCale( 2, x0, y0 );

// In matricea 'cale' elementul cale[x][y] este un intreg,
valoarea 0
// insemnand ca mingea nu a trecut prin acel punct, altfel
fiind numarul
// de ordine in cadrul parcursului mingii
//


#define IN_AFARA_ZONEI(x,y) (x<1 || x>n || y<1 || y>n)

#define PE_MARGINE(x,y) (x==1 || x==n || y==1 || y==n)

#define POSIBIL() ( cale[xn][yn]==0 &&
cota[x][y]>cota[xn][yn] )

typedef int COORDONATA;
typedef char *TEXT;


Deplasare minge
Din fiecare punct mingea se va putea deplasa in
8 directii
pentru i =1,8 executa
xc = xc +dx[i]
yc = yc + dy[i]
@
COORDONATA dx[] = { 1, 0, -1, 0, -1, 1, 1, -1 };
COORDONATA dy[] = { 0, -1, 0, 1, -1, -1, 1, 1 }

Solutie
void CautaCale(int k, COORDONATA x, COORDONATA y)
{
COORDONATA dx[] = { 1, 0, -1, 0, -1, 1, 1, -1 };
COORDONATA dy[] = { 0, -1, 0, 1, -1, -1, 1, 1 };
COORDONATA xn,yn;
int i,
ndir=4; // doar 4 directii la 90 grade
// (sunt excluse diagonalele)

if( PE_MARGINE(x,y) ) AfiseazaSolutie();
for(i=0;i<ndir;i++) {
xn = x + dx[i];
yn = y + dy[i];
if(!IN_AFARA_ZONEI(xn,yn) && POSIBIL()) {
cale[xn][yn]=k;
CautaCale(k+1,xn,yn);
cale[xn][yn]=0;
}
}
}


Un fisier de test
3
1 2 1
2 3 4
1 4 1
2
2
0

Matricea cotelor
1 2 1
2 3 4
1 4 1

Coordonatele punctului initial 2 2

Solutii
Solutia 1 :

Matricea drumurilor
0 0 0
2 1 0
0 0 0

Solutia 2 :

Matricea drumurilor
0 0 0
2 1 0
3 0 0
Solutia 3 :

Matricea drumurilor
3 0 0
2 1 0
0 0 0

Matricea cotelor
1 2 1
2 3 4
1 4 1

Coordonatele punctului initial 2 2

Solutia 4 :

Matricea drumurilor
0 2 0
0 1 0
0 0 0
Solutia 5 :

Matricea drumurilor
3 2 0
0 1 0
0 0 0
Solutia 6 :

Matricea drumurilor
0 2 3
0 1 0
0 0 0

*** Au fost 6 solutii



Matricea cotelor
1 2 1
2 3 4
1 4 1

Coordonatele punctului initial 2 2

Problema drumurilor
Subiect de examen
Se considera o retea de sosele care leaga n orase.
Sa se scrie un program care sa stabileasca toate drumurile posibile intre un oras s
(start) si un oras d (destinatie).

Observatie. Toate circuitele sunt interzise pentru a avea un numar finit de solutii.
Datele de intrare se vor prezenta in ordinea urmatoare:

n
s d
c11 c12 c1k 0
c21 c2m 0
...
Cn1 ... 0

unde
n= numarul de orase,
s = orasul de start
d = orasul destinatie
cij = cel de-al j-lea oras cu care este legat printr-o sosea orasul i

Numarul de orase cu care poate fi legat un oras
este variabil, de aceea fiecare linie se termina cu
un 0.
Pe sosele se poate circula in ambele sensuri.

Numarul de linii care urmeaza dupa precizarea
valorilor s si d este totdeauna egal cu numarul de
orase (n).
Daca exista un oras izolat, sau nu este necesar sa
se mai precizeze cu cine este legat, atunci acest
lucru este marcat printr-o linie care contine 0.

Exemplu date intrare
====================
4 // sunt 4 orase
4 1 // se pleaca din 4 si trebuie sa se ajunga in 1
2 3 0 // orasul 1 este legat prin sosele cu orasele 2 si 3
3 0 // orasul 2 este legat cu orasul 3
4 0 // orasul 3 este legat printr-o sosea cu orasul 4
0

Observatie: despre orasul 4 nu a fost nevoie sa se mai
precizeze ceva.
Subiect de examen
Programul va afisa meniul urmator:

C - citire date de la tastatura
F - citire date dintr-un fisier al carui nume va fi preluat de la tastatura
G - citire date dintr-un fisier al carui nume va fi preluat din linia de comanda
A - afisare matrice sosele
R - rezolvare problema
I - info autor
T - termina programul

Punctaj:
C=1p, F=1p, G=1p, A=1p, R = nr_teste_rez_corect x 1p ,
I=0.5p , T=0.5p si din oficiu 1p.

Vor fi 4 teste care nu necesita validari.

//
// Se considera o retea de sosele care leaga n orase.
// Programul stabileste toate drumurile posibile intre un oras
// s (sursa) si un oras d (destinatie)

// Observatie
// Se poate presupune ca circuitele sunt interzise
// pentru a avea un numar finit de solutii. In cazul in care s=d
// (neinterzis explicit de problema) se da tot o solutie fara
// circuite
//

typedef int ORAS,ORASE[N];
typedef char *TEXT;


void CautaDrumuri(unsigned k, ORAS crt);
LOGIC Posibil(unsigned k, ORAS alfa);
void AfiseazaSolutie(unsigned k);

int n;
unsigned nrsol;
ORAS s,d;
ORASE itinerar;
MATRICE sosele;

Solutie
//
// Implementeaza metoda Backtracking recursiv
// Apel initial :
// CautaDrumuri( 0, s );
// Variabile globale:
// d - orasul destinatie
// itinerar[] - memoreaza orasele prin care va trece
// drumul cerut de problema
//
void CautaDrumuri(unsigned k, ORAS oras_crt)
{
ORAS i;
itinerar[k]=oras_crt;
if(oras_crt==d ) AfiseazaSolutie(k);
else
for(i=1;i<=n;i++)
if(Posibil(k,i))
CautaDrumuri(k+1,i);
}

Posibil()
// Functia determina daca este posibil ca, la pasul k, dat
// fiind oras_crt, orasul alfa sa poata fi introdus in itinerar.
//
// Utilizarea variabilei 'rezultat' a fost necesara pentru
// a se putea afisa rezultatul functiei cand DEBUG este ADEVARAT
//
LOGIC rezultat=ADEVARAT;
//
// Exista sosea intre orasul curent si noul oras (alfa) ?
//
if(sosele[oras_crt] [alfa] ==FALS) rezultat=FALS;
else
//
// Orasul alfa nu trebuie sa fie deja in solutia partiala
// Observatie: in cazul s==d alfa poate fi in itinerar[0]
//
for(i=0;i<=k;i++)
if(itinerar[i]==alfa) { rezultat=FALS;
break;
}
return rezultat;
}

Subiect examen. Exemplu rezultate
Continutul fisierului de test este cel din exemplul anterior.
Atunci, daca se alege optiunea A :

Matrice sosele
1 1 1 0
1 1 1 0
1 1 1 1
0 0 1 1

Daca se alege R rezultatul trebuie prezentat in forma urmatoare:

Solutia 1 : ( 4 -> 3 -> 1 )
Solutia 2 : ( 4 -> 3 -> 2 -> 1 )

*** Au fost 2 solutii

Daca nu exista solutie veti specifica "Nu exista drum intre s si d", inlocuind bineinteles s si d cu valorile
din datele de intrare.

Timp de lucru: 90 minute
4 // sunt 4 orase
4 1 // se pleaca din 4 si trebuie sa se
// ajunga in 1
2 3 0 // orasul 1 este legat prin sosele cu
// orasele 2 si 3
3 0 // orasul 2 este legat cu orasul 3
4 0 // orasul 3 este legat printr-o sosea cu
//orasul 4
0



Intrebri ?