學習鏈接:
回溯法解旅行商問題(TSP)、貪心算法:旅行商問題(TSP)
今天早上做了無數個夢,然后被緊緊地吸附在床上。掙扎一番后爬起來,已經是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 }