注:本文轉載自網絡,筆者略有改動,感謝作者整理。
歐幾里得旅行商問題,又叫貨郎問題(Traveling Salesman Problem,簡稱“TSP”)也叫貨郎擔問題,中國郵路問題,旅行商問題等,是計算機算法理論歷史上的經典問題。在過去幾十年中,它成為許多重要算法思想的測試平台,同時也促使一些新的理論領域的產生,比如多面體理論和復雜性理論。 貨郎問題:給定n個結點和任意一對結點{i,j}之間的距離為dist(i,j),要求找出一條閉合的回路,該回路經過每個結點一次且僅一次,並且該回路的費用最小,這里的費用是指每段路徑的距離和。 貨郎問題求解其精確解是NP難的,並且求解任意常數因子近以度的解也是NP難的。若將問題限定在歐氏平面上,就成為歐氏平面上的貨郎問題,也叫歐幾里德旅行商問題(Eculid Traveling Salesman Problem)。但是,即使是歐氏平面上的貨郎問題也是NP難的。因此通常用來解決TSP問題的解法都是近似算法。其中第一個歐幾里德旅行商問題的多項式近似算法是Arora在1996年使用隨機平面分割和動態規划方法給出的。
J.L. Bentley 建議通過只考慮雙調旅程(bitonic tour)來簡化問題,這種旅程即為從最左點開始,嚴格地從左到右直至最右點,然后嚴格地從右到左直至出發點。事實上,存在確定的最優雙調路線的O(n*n)時間的算法。
1 /********************************************************************** 2 * Bitonic path (詳見《算法導論》 P217) 3 * 一個人從p1嚴格地增的走到pn,然后再嚴格遞減的回到p1;求總路徑的最小值; 6 * 對於1 <= i <= j <= n, 我們定義P(i, j)是一條包含了P1, P2, P3 …… Pj的途徑; 7 * 這條路徑可以分成2部分:遞減序列與遞增序列 8 * 起點是Pi(1 <= i <= j),拐點是P1,終點是Pj, P[i, j]為其最小值; 9 * 狀態轉移方程為: 10 * b[1,2] = |P1P2|, 11 * i < j-1時, b[i,j] = b[i,j-1] + |Pj-1Pj| 點Pj-1在遞增序列中,
* 因為要求P(i,j)要求i<=j,所以在此種情況下新的節點是加在遞增序列部分的 12 * i = j-1時, b[i,j] = min{ b[k,j-1] + |PkPj|, 1<= k < j-1 }
* = min{b[k,i]+|PkPj|,1<=k<j-1} 點Pj-1在遞減序列中 13 * b[n,n] = b[n-1,n] + |Pn-1Pn| 14 **********************************************************************/ 15 16 17 #include <stdio.h> 18 #include <math.h> 19 #define INF 0x7fffffff 20 #define N 201 21 struct point{ 22 double x, y; 23 }point[N]; 24 int n; 25 double dis[N][N]; 26 27 double distant(int i, int j) 28 { 29 return sqrt((point[i].x - point[j].x)*(point[i].x - point[j].x) + (point[i].y - point[j].y)*(point[i].y - point[j].y)); 30 } 31 32 double dp() 33 { 34 int i, j, k; 35 double temp, b[N][N]; 36 37 b[1][2] = dis[1][2]; 38 for (j=3; j<=n; j++) 39 { 40 for (i=1; i<=j-2; i++) 41 b[i][j] = b[i][j-1] + dis[j-1][j]; 42 43 b[j-1][j] = INF; 44 for (k=1; k<=j-2; k++) 45 { 46 temp = b[k][j-1] + dis[k][j]; 47 if (temp < b[j-1][j]) 48 b[j-1][j] = temp; 49 } 50 } 51 52 b[n][n] = b[n-1][n] + dis[n-1][n]; 53 54 return b[n][n]; 55 } 56 57 int main() 58 { 59 int i, j; 60 double ans; 61 while (scanf("%d", &n) > 0) 62 { 63 for (i=1; i<=n; i++) 64 scanf("%lf %lf", &point[i].x, &point[i].y); 65 66 for (j=2; j<=n; j++) 67 for (i=1; i<j; i++) 68 dis[i][j] = distant(i,j); 69 70 ans = dp(); 71 72 printf("%.2lf\n", dp()); 73 } 74 }