Sunteți pe pagina 1din 4

Problema comis-voiajorului – Backtracking

Problema comis-voiajorului (Travelling Salesman Problem, prescurtat TSP) este o


problemă clasică de backtracking elementar.

Enunț
Se dau n orașe, numerotate de la 1 la n, și o listă cu m străzi bidirecționale (pe care se
poate circula în ambele sensuri), identificate prin cele două orașe-extremități și prin
lungime. Un comis-voiajor are misiunea de a livra colete în toate aceste orașe, cu
condiția să se întoarcă în orașul din care a plecat. El poate realiza asta în multe moduri,
însă îl interesează un drum de lungime minimă. Așadar, se cere determinarea unui
drum de lungime minimă, care să treacă prin fiecare oraș exact o singură dată, iar din
ultimul oraș vizitat să revină în primul.

Exemplu
De pe prima linie a fișierului comis.in se citesc n și m, numărul de orașe și respectiv
numărul de străzi. Pe fiecare dintre următoarele m linii se găsesc câte trei
numere x, y și z, cu semnificația că strada dintre orașele x și y are lungimea z.

comis.in graful asociat traseului din exemplul dat :

5 8

1 2 20
1 5 60
1 4 30
2 4 30
4 3 70
2 3 40
4 5 30
5 3 30

comis.out

150
1 2 3 5 4 1
Soluție
Practic, ni se dă un graf neorientat cu costuri pe muchii, și trebuie să determinăm un
drum care trece prin toate nodurile grafului și se întoarce în nodul inițial, de cost minim.
Desigur, prin costul drumului ne referim la suma costurilor muchiilor din care acesta
este format. Folosind metoda backtracking, vom genera toate ciclurile hamiltoniene ale
grafului dat, și îl vom reține pe parcurs pe cel de cost minim.

Vom reprezenta graful printr-o matrice de adiacență, adică o matrice a[Dmax][Dmax] în


care pe a[i][j] reținem 0 dacă nu există stradă între orașele i și j, sau lungimea străzii
respective în caz contrar.

Pentru graful dat prezentăm matricea de adiacență a costurilor muchilor :

0 20 0 30 60

20 0 40 30 0

0 40 0 70 30

30 30 70 0 30

60 0 30 30 0

De asemenea, în variabila n vom
reține lungimea traseului curent, iar în vectorul d orașele din care este compus acesta.
În plus, vom avea nevoie de o variabilă smin și de un vector dd pentru stocarea
traseului minim și a lungimii acestuia.

Acum urmează partea de backtracking. În primul rând, se observă ușor că nu contează


din ce oraș pornim, pentru că oricum trebuie să ne întoarcem de unde am plecat; de
exemplu, traseul [1,2,3,5,4,1] este tot una cu [3,5,4,1,2,3]. Prin urmare, vom fixa
pe x[1] valoarea 1 (adică vom porni mereu din orașul 1), și vom începe generarea prin
backtracking de la poziția 2. Această idee va reduce de n ori numărul de trasee
generate!

Programul C/C++ la această problemă :

#include <bits/stdc++.h>
#define Dmax 15
using namespace std;
ifstream f("comis.in");
ofstream g("comis.out");
int n, m, a[Dmax][Dmax], x[Dmax], d[Dmax], dd[Dmax], smin =
INT_MAX, gasit, Suma;
void afis()
{
g << smin << "\n";
for(int i = 1; i <= n + 1; i ++)
g << dd[i] << " ";
}
void calculez()
{
gasit = 1;
Suma = 0;
d[1] = x[1];
for(int i = 2; i <= n; i ++)
{
d[i] = x[i];
Suma = Suma + a[x[i - 1]][x[i]];
}
d[n + 1] = x[1];
Suma = Suma + a[x[n]][x[1]];
if(Suma < smin)
{
smin = Suma;
memcpy(dd, d, sizeof(d));
}
}
int valid(int k)
{
if(a[x[k-1]][x[k]] == 0) return 0;
for(int i = 1; i < k; i ++)
if(x[k] == x[i]) return 0;
return 1;
}
void back(int k)
{
for(int i = 1; i <= n; i ++)
{
x[k] = i;
if(valid(k))
{
if(k == n)
{
if(a[x[k]][x[1]] > 0) calculez();
}
else back(k + 1);
}
}
}

int main()
{
int i, j, cost;
f >> n >> m;
while(m)
{
f >> i >> j >> cost;
a[i][j] = a[j][i] = cost;
m --;
}
x[1] = 1;
back(2);
if(!gasit) g << 0;
else afis();
return 0;
}