题目
TSP问题(旅行商问题)是指旅行家要旅行n个城市,要求各个城市经历且仅经历一次然后回到出发城市,并要求所走的路程最短。
假设现在有四个城市,0,1,2,3,他们之间的代价如图一,可以存成二维表的形式
现在要从城市0出发,最后又回到0,期间1,2,3都必须并且只能经过一次,使代价最小。
分析
TSP问题是NP完全问题;
从初始点出发的周游路线一共有(n-1)!条,即等于除初始结点外的n-1个结点的排列数,因此旅行商问题是一个排列问题。通过枚举(n-1)!条周游路线,从中找出一条具有最小成本的周游路线的算法,其计算时间显然为O(n!)。
方法1:动态规划方法
动态规划可行性:设s, s1, s2, …, sp, s是从s出发的一条路径长度最短的简单回路,假设从s到下一个城市s1已经求出,则问题转化为求从s1到s的最短路径,显然s1, s2, …, sp, s一定构成一条从s1到s的最短路径,所以TSP问题是构成最优子结构性质的,用动态规划来求解也是合理的。
推倒动态规划方程:
假设从顶点s出发,令d(i, V’)表示从顶点i出发经过V’(是一个点的集合)中各个顶点一次且仅一次,最后回到出发点s的最短路径长度。
推导:(分情况来讨论)
①当V’为空集,那么d(i, V’),表示从i不经过任何点就回到s了,如上图的 城市3->城市0(0为起点城市)。此时d(i, V’)=Cis(就是 城市i 到 城市s 的距离)、
②如果V’不为空,那么就是对子问题的最优求解。你必须在V’这个城市集合中,尝试每一个,并求出最优解。
d(i, V’)=min{Cik + d(k, V’-{k})} 注:Cik表示你选择的城市和城市i的距离,d(k, V’-{k})是一个子问题。
综上所述,TSP问题的动态规划方程就出来了:
将d(i, V’)转换成二维表,d[i][j]
实现
public class Num7 {
private static int[][] d;
private static int[] bits;
/**
* @param args
*/
public static void main(String[] args) {
int[][] c={
{0,3,6,7},
{5,0,2,3},
{6,4,0,2},
{3,7,5,0}
};
getMinCost(c);
}
/*动态规划 实现TSP问题
*
* 用二进制表示集合(比如1100,表示在当前集合中:节点1、节点2不存在,节点3和节点4存在)
*
* 从上到下求,递归
*/
public static void getMinCost(int[][] a){
int n=a.length;
bits=new int[n];
for(int i=0;i<n;i++){
bits[i]=1<<i;
}
d=new int[1000][n];
for(int i=0;i<d.length;i++){
for(int j=0;j<d[0].length;j++){
d[i][j]=-1;
}
}
int t=1;
for(int i=1;i<n;i++){ //初始化这些值:{1}->1 {2}->2 {3}-3
d[t<<(i-1)][i]=a[0][i];
}
int res=Integer.MAX_VALUE;
t=0;
for(int i=1;i<n;i++){
t=t<<1;
t=t|1;
}
for(int i=1;i<n;i++){
int r=getD(i,t,n,a);
if(r+a[i][0]<res){
res=r+a[i][0];
}
}
System.out.println(res);
}
private static int getD(int i, int t,int n,int[][] a) {
int min=Integer.MAX_VALUE;
if(d[t][i]!=-1){
return d[t][i];
}
t=t&(~bits[i-1]);
for(int j=1;j<n;j++){
int tt=t&bits[j-1]; //遍历每个集合中的节点
if(tt>0){
int res=getD(j,t,n, a);
if(res+a[j][i]<min){
min=res+a[j][i];
}
}
}
d[t][i]=min;
return d[t][i];
}
}