演算法設計與分析:第四章 動態規劃 4.2TSP之貨郎擔問題
阿新 • • 發佈:2019-02-01
/* 如果對於任意數目的n個城市,分別用1~n編 號,則這個問題歸結為在有向帶權圖中,尋找一 條路徑最短的哈密爾頓迴路問題。 這裡,V表示城市頂點,(i,j) ∈E 表示城市之 間的距離,用鄰接矩陣C表示城市之間的距離。 思想: 1設d(i,V-{i})表示從頂點i出發,經過V-{i}中各頂點一次,回到頂點i的最短路徑長度 2d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})} 3邊界:d(k,空集) = Cki,k!= i,從頂點k出發,不經過任何頂點,回到頂點i的長度,自然是Cki 4目標狀態:d(0,{1,2,3}) 難點:如何標識這個集合V,難道用標記法,這是集合上的動態規劃問題,可以通過位來進行操作 0001表示第i個節點選中 那初始時:1111...1110 n-1個1 1個0 輸入說明:0標識不可達 輸入: 4 0 3 6 7 5 0 2 3 6 4 0 2 3 7 5 0 4 0 8 5 6 6 0 8 5 7 9 0 5 9 7 8 0 輸出: 10 23 難點: 1到底遞迴求的是什麼? 分析即所得,求的是集合 求的是:d(i,V-{i}) = d(iStart,iFull - (1<<iStart) ) 2遞迴傳入的引數是什麼? iSart,初始節點,除去初始節點的剩餘節點集合,為一個數 3遞迴基是什麼? 遞迴基是:如果碰到已經求過的,則直接返回;碰到i != g_iStart && s == 0,返回g_dp[i][s] = g_iArr[i][g_iStart]; 4如何挑選k? for(int k = 0 ; k < n ; k++) { //如果已經含有k,才挑選 if(s & (1 << k)) { //d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})} g_dp[k][s ^ (1<<k)] = TSP(k,s ^ (1<<k),n); */ /* 關鍵: 1 //對於多個狀態,只能用遞迴來做。 2 //記憶化搜尋 if(g_dp[i][s] != -1) { return g_dp[i][s]; } //遞迴基 if(i != g_iStart && s == 0) { return g_dp[i][s] = g_iArr[i][g_iStart]; } 3 for(int k = 0 ; k < n ; k++) { //如果已經含有k,才挑選 if(s & (1 << k)) { //d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})} g_dp[k][s ^ (1<<k)] = TSP(k,s ^ (1<<k),n); //選取最小值 if(iMin > g_dp[k][s ^ (1<<k)] + g_iArr[i][k]) { iMin = g_dp[k][s ^ (1<<k)] + g_iArr[i][k];i } 4 1設d(i,V-{i})表示從頂點i出發,經過V-{i}中各頂點一次,回到頂點i的最短路徑長度 2d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})} 3邊界:d(k,空集) = Cki,k!= i,從頂點k出發,不經過任何頂點,回到頂點i的長度,自然是Cki 4目標狀態:d(0,{1,2,3}) */ #include <stdio.h> #include <string.h> const int MAXSIZE = 20; int g_dp[MAXSIZE][1 << MAXSIZE];//採用集合的方式來做 int g_iArr[MAXSIZE][MAXSIZE]; int g_iStart = 0; //對於多個狀態,只能用遞迴來做。 int TSP(int i,int s,int n) { //記憶化搜尋 if(g_dp[i][s] != -1) { return g_dp[i][s]; } //遞迴基 if(i != g_iStart && s == 0) { return g_dp[i][s] = g_iArr[i][g_iStart]; } //開始進行遞推,從底向上 int iMin = 1000000000; //選擇下一個城市 for(int k = 0 ; k < n ; k++) { //如果已經含有k,才挑選 if(s & (1 << k)) { //d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})} g_dp[k][s ^ (1<<k)] = TSP(k,s ^ (1<<k),n); //選取最小值 if(iMin > g_dp[k][s ^ (1<<k)] + g_iArr[i][k]) { iMin = g_dp[k][s ^ (1<<k)] + g_iArr[i][k]; } } } return g_dp[i][s] = iMin; } void process() { int n; while(EOF != scanf("%d",&n)) { if(n <= 0) { break; } memset(g_iArr,0,sizeof(g_iArr)); for(int i = 0 ; i <= n - 1 ; i++) { for(int j = 0 ; j <= n - 1 ; j++) { scanf("%d",&g_iArr[i][j]); } } int iStart = 0; memset(g_dp,-1,sizeof(g_dp)); //假設選定i=1作為初始節點 //初始化動態規劃陣列 for(int i = 0 ; i <= n-1 ; i++) { if(i != iStart) { g_dp[i][0] = g_iArr[i][iStart]; } } TSP(iStart,(1<<n)-2,n); printf("%d\n",g_dp[iStart][(1<<n)-2] ); } } int main(int argc,char* argv[]) { process(); getchar(); return 0; }