一、题目
- 一个售货员必须访问n个城市,恰好访问每个城市一次,并最终回到出发城市。
售货员从城市i到城市j的旅行费用是一个整数,旅行所需的全部费用是他旅行经过的的各边费用之和,而售货员希望使整个旅行费用最低。 - (等价于求图的最短哈密尔顿回路问题)令G=(V, E)是一个带权重的有向图,顶点集V=(v0, v1, ..., vn-1)。从图中任一顶点vi出发,经图中所有其他顶点一次且只有一次,最后回到同一顶点vi的最短路径。
二、解题思路
此问题我们可以用全排列问题解决,全排列问题详见此篇文章。全排列就是枚举各个城市的全排列组合,但是必要时通过剪枝的方法求解,在交换时如果发现两个城市之间距离为无穷大,则可剔除此次无效排列。但是为了减少枚举数量,我们第一个城市将不参与全排列,所以枚举数量减少了.
三、测试用例
其中1,2,3,4,5代表五个城市。此模型可抽象为图,可用邻接矩阵表示,如下图所示:
四、代码
#include <iostream>
#define N 6
#define INF 10e7
using namespace std;
//定义邻接矩阵表示图模型
int g[N][N]= {{-1,-1,-1,-1,-1,-1},
{-1,INF,3,INF,8,9},
{-1,3,INF,3,10,5},
{-1,INF,3,INF,4,3},
{-1,8,10,4,INF,20},
{-1,9,5,3,20,INF}};
//保存路径的最小值
int minLength = INF;
//当前路径的长度
int currentLength = 0 ;
//保存路径
int path[N] = {0};
//保存城市编号
int city[N] = {-1,1,2,3,4,5};
//城市个数
int length = N -1;
//交换数组的两个数,参数为数组索引
void swap(int s,int t){
int temp = city[t];
city[t] = city[s];
city[s] = temp;
}
//index表示从第index开始,计算index到n的的全排列,
//即dfs(1)代表从第一数算起,生成1-n的全排列
void dfs(int index){
if(index > length){
//如果最后一个城市到第一个城市不是无穷大,
//并且是当前的最短路径,则保存新路径和最短路径
if(g[city[length]][city[1]] != INF && (currentLength+g[city[length]][city[1]] < minLength)){
for(int i=1 ; i <= length ;i++){
path[i] = city[i];
}
minLength = currentLength + g[city[length]][city[1]];
}
return;
}
else{
for(int i = index ; i <= length ;i++){
if(g[city[i]][city[index-1]] != INF && (currentLength + g[city[i]][city[index-1]] < minLength)){
swap(i,index);
currentLength += g[city[index]][city[index-1]];
//继续生成index+1到n的全排列
dfs(index+1);
currentLength -= g[city[index]][city[index-1]];
//回溯时交换回来
swap(i,index);
}
}
}
}
//打印路径
void print(){
cout<<"最短路径: ";
for(int i=1;i<=length;i++){
cout<<path[i]<<"--->";
}
cout<<city[1]<<endl;
cout<<"最短路径长度:"<<minLength;
}
int main()
{
dfs(2);
print();
return 0;
}
五、 运行结果及分析
- 时间复杂度:
- 空间复杂度:
本次算法只是暴力求法,旅行商问题也可以用动态规划算法来解决,具体 详看我的另一篇文章。