双调欧几里得旅行商问题是一个经典动态规划问题。《算法导论(第二版)》思考题15-1和北京大学OJ2677都出现了这个题目。
旅行商问题描述:平面上n个点,确定一条连接各点的最短闭合旅程。这个解的一般形式为NP的(在多项式时间内可以求出)
J.L. Bentley 建议通过只考虑双调旅程(bitonictour)来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。下图(b)显示了同样的7个点的最短双调路线。在这种情况下,多项式的算法是可能的。事实上,存在确定的最优双调路线的O(n*n)时间的算法。
上图中,a是最短闭合路线,这个路线不是双调的。b是最短双调闭合路线。
求解过程,算法导论官网上已给出来了,而且是写的最好的。
//双调欧几里德旅行商算法 #include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; struct point { int x; int y; }; #define MAXSIZE 202 int number; //点的个数 point a[MAXSIZE]; double d[MAXSIZE][MAXSIZE]; double straight(point m,point n) { double i=(double)(m.x-n.x)*(m.x-n.x); double j=(double)(m.y-n.y)*(m.y-n.y); return sqrt(i+j); } bool compare(point m,point n) { return m.x<n.x; } int main() { int i,j,k; //freopen("a.txt","r",stdin); while(cin>>number) { for(i=0;i<number;i++) cin>>a[i].x>>a[i].y; //先排序 sort(a,a+number,compare); //计算状态关系集d[i][j] d[1][1]=0; for(i=2;i<=number;i++) { for(j=1;j<=i;j++) { if(j==i-1) { d[i][j]=d[j][1]+straight(a[0],a[i-1]); for(k=2;k<=j-1;k++) { if(d[j][k]+straight(a[k-1],a[i-1])<d[i][j]) d[i][j]=d[j][k]+straight(a[k-1],a[i-1]); } } else if(j==i) { d[i][i]=d[i][1]+straight(a[0],a[i-1]); for(k=2;k<=j-1;k++) { if(d[i][k]+straight(a[k-1],a[i-1])<d[i][i]) d[i][j]=d[i][k]+straight(a[k-1],a[i-1]); } } else d[i][j]=d[i-1][j]+straight(a[i-2],a[i-1]); } } printf("%.2lf\n",d[number][number]); } }
原文地址:http://blog.csdn.net/bb2b2bbb/article/details/27206223