今天早上做了无数个梦,然后被紧紧地吸附在床上。挣扎一番后爬起来,已经是9点了。然后我开始研究旅行商问题。
在一个无向图中找到一个可以遍历所有节点的一个最短回路。理论上说可以用全排列列出所有解的下标,然后一个一个试,时间复杂度o(n!)。但是可以用回溯法,用【约束函数】(constraint)判断当前路径是否连通,用【界限函数】(bound)判断当前路径是否比已经求得的最短路径小。这两个判断任意一个不符,则做“剪枝操作”(不再对后续节点进行遍历)。
可以看出回溯法比穷举要高明的多。这个回溯法和八皇后问题也有一些区别。TSP问题需要构造一棵排列树:
根节点为{0}
第一层{0,1}
第二层{0,1,2},{0,2,1}
第三层{0,1,2,3},{0,1,3,2},{0,2,1,3},{0,2,3,1},{0,3,1,2},{0,3,2,1}
……
并且回溯法要求对图进行DFS操作,即深度优先搜索。因为需要首先首次找到最深处的节点,才能设置当前最优解,好让后续问题能有参考。
Java代码:
1 public class Main {
2
3 public static void main(String[] args) {
4 int[][] adjMatrix={
5 {0,20,6,4},
6 {20,0,5,10},
7 {6,5,0,15},
8 {4,10,15,0},
9 };
10 TSP problem=new TSP(adjMatrix);
11
12
13 }
14 }
15
16 class TSP{
17 int vexnum=0;//顶点数目
18 int adjMatrix[][];
19 TSP(int[][] adjMat){
20 adjMatrix=adjMat;
21 vexnum=adjMatrix.length;
22 int init[]={0};
23 Backtrack(1,init);
24 int a;
25 a=0;
26 }
27 int bestCost=0;
28 int[] bestX;//最优解向量
29 boolean isTraverseDeep=false;
30 //回溯法递归
31 //初始x:[0]
32 void Backtrack(int t,int[] x){//对顶点t进行操作,父结点的解向量是x,
33 if(t>=vexnum){//解向量的第一个元素应该是初始顶点,如0,最后一个元素也是0
34 x[t]=0;//最后一个节点赋值:0。
35 constraint(x,t);
36
37 }else{//所有顶点都解完
38 int i,j;
39 int cx[]=new int[vexnum+1];
40 for(j=0;j<t;j++) cx[j]=x[j];//拷贝父结点
41 cx[t]=t;
42 if(constraint(cx,t)) Backtrack(t+1,cx);//不交换的情况下进行递归
43 //不断递归调用【Backtrack】,进行DFS
44 for(i=1;i<t;i++){
45 cx=new int[vexnum+1];
46 for(j=0;j<t;j++) cx[j]=x[j];//拷贝父结点
47 cx[t]=t;
48 swap(cx,i,t);
49 if(constraint(cx,t)) Backtrack(t+1,cx);//交换的情况下进行递归
50 }
51 }
52 }
53 boolean constraint(int[] x,int len){//对解进行约束
54 int cost=0;
55 int i;
56 int pre=x[0];
57 for(i=1;i<=len;i++){
58 int dist=adjMatrix[pre][x[i]];
59 if(dist<=0) return false;//不连通,则为否。约束(constraint)函数
60 cost+=dist;
61 pre=x[i];
62 }
63 if(isTraverseDeep){//如果已经进行了最底部的遍历,则对这个当前花费进行判别。界限(bound)函数
64 if(cost<bestCost){//比最优解要小
65 if(len==vexnum){//已经遍历完
66 bestCost=cost;
67 bestX=x;//设置最优解向量
68 }
69 return true;
70 }else return false;
71 }else if(len==vexnum){//首次遍历到底部
72 bestCost=cost;
73 bestX=x;//设置最优解向量
74 isTraverseDeep=true;
75 return true;
76 }
77 return true;
78 }
79 private void swap(int[] nums,int a,int b){
80 int tmp=nums[a];
81 nums[a]=nums[b];
82 nums[b]=tmp;
83 }
84 }