1、算法描述
旅行售货员问题的解空间是一颗排列树。对于排列树的回溯搜索与生成1,2,……,n的所有排列的递归算法Perm类似。开始时 x = [ 1,2,……,n],则对应的排列树由x[ 1 :n ]的所有排列构成。
在递归算法backtrack中,当i=n时,当前扩展结点是排列树的叶子结点的父结点。此时算法检查图G是否存在一条从顶点X[ n-1 ]到顶点X [ n ] 的边和一条从顶点X[ n ] 到顶点1的边。如果这两条边存在,则找到一条旅行售货员回路。此时,算法还需要判断这条回路的费用是否存在最优于已找到的当前最优回路的费用bestc。如果是,则必须更新当前最优值bestc和当前最优解bestx。
当 i<n 时,当前扩展结点位于排列树的第 i-1 层。图G中存在从顶点 X[ i-1 ]到顶点X[i]的边时,x[ 1:i ]构成图G的一条路径,且当x[ 1 : i ]的费用小于当前最优值时算法进入排列树的第i层,否则将剪去相应子树。算法中变量cc记录当前路径X[ 1:i ]的费用。
2、实例
旅行售货员
时限:1000ms 内存限制:10000K 总时限:3000ms
描述:
某售货员要到若干城市去推销商品,已知各城市之间的路程(或旅费)。他要选定一条从驻地出发,经过每个城市一遍,最后回到驻地的路线,使总的路程(或旅费)最小。各个城市之间可能是有向连通的、无向连通的、以及存在某个城市不连通的情况,你的程序应该能够处理所有可能的情况。如下图表示各个城市间无向连通。
输入:
第一行为一个整数n(n<=10),表示城市的总个数。接下来是一个n*n的矩阵,用来表示城市间的连通情况以及花费,例如path[i][j]=len,len=-1表示从城市i到城市j没有通路,len>0表示从i到j的路程长度为len。
对于上面图示的问题我们可以这样输入:
4
-1 30 6 4
30 -1 5 10
6 5 -1 20
4 10 20 -1
输出:
输出占一行,对于给定的问题,如果找到了最小路程(花费),输出该最小花费,如果没有通路可以到达每个城市,则输出-1。
完整代码如下:
#include<stdlib.h>
#include<iostream>
using namespace std;
const int NoEdge=-1;
const int MAX=20;
int G[MAX][MAX];
int x[MAX],bestx[MAX];
int bestc,cc;
void init(int n)
{
int i,j;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cin>>G[i][j];
for(i=1;i<=n;i++)
x[i]=i;
bestc=99999;
cc=0;
}
void Swap(int &i,int &j)
{
int t;
t=i;
i=j;
j=t;
}
void Traveling(int i,int n)
{
int j;
if(i==n+1)
{
if(G[x[n-1]][x[n]]!=NoEdge&&G[x[n]][1]!=NoEdge&&
(cc+G[x[n-1]][x[n]]<bestc))
bestc=cc+G[x[n]][1];
for(int j=1;j<=n;j++)
{
bestx[j]=x[j];
}
}
else
{
for(j=i;j<=n;j++)
{
if(G[x[i-1]][x[j]]!=NoEdge&&(cc+G[x[i-1]][j]<bestc))
{
Swap(x[i],x[j]);
cc+=G[x[i-1]][x[i]];
Traveling(i+1,n);
cc-=G[x[i-1]][x[i]];
Swap(x[i],x[j]);
}
}
}
}
void main()
{
int i,j, n;
cin>>n;
init(n);
Traveling(2,n);
if(bestc==99999)
cout<<"-1"<<endl;
else
cout<<bestc<<endl;
for(i=1;i<=n;i++)
cout<<bestx[i]<<" ";
cout<<endl;
system("pause");
}