J.L. Bentley 建议通过只考虑双调旅程(bitonic tour)来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。下图(b)显示了同样的7个点的最短双调路线。在这种情况下,多项式的算法是可能的。事实上,存在确定的最优双调路线的O(n*n)时间的算法。
注:在一个单位栅格上显示的平面上的七个点。 a)最短闭合路线,长度大约是24.89。这个路线不是双调的。b)相同点的集合上的最短双调闭合路线。长度大约是25.58。
这是一个算导上的思考题15-1。
首先将给出的点排序,关键字x,重新编号,从左至右1,2,3,…,n。
定义d[i][j],表示结点i到结点j之间的距离。
定义dp[i][j],表示从i连到1,再从1连到j,(注意,i>j,且并没有相连。)
对于任意一个点i来说,有两种连接方法,一种是如图(a)所示,i与i-1相连,另一种呢是如图(b),i与i-1不相连。
根据双调旅程,我们知道结点n一定与n相连,那么,如果我们求的dp[n][n-1],只需将其加上d[n-1][n]就是最短双调闭合路线。
根据上图,很容易写出方程式:
dp[i][j]=dp[i-1][j]+d[i][i-1];
dp[i][i-1]=min(dp[i][i-1],dp[i-1][j]+d[j][i]);
#include <cstdio> #include <cstring> #include <cmath> using namespace std; const int maxn = 1010; int dp[maxn][maxn]; int d[maxn][maxn]; struct point { int x, y; }a[maxn]; int dis(int i, int j) //计算两点之间的距离 { int p,q; if(a[i].y>a[j].y) q=(360+a[j].y-a[i].y)%360; else q=(360+a[i].y-a[j].y)%360; p=abs(a[i].y-a[j].y)>q?q:abs(a[i].y-a[j].y); //求出小弧的长度 return (abs(a[i].x-a[j].x)*400+p); //距离=两点的轨道差*400+两点的扇区差 } int main() { int t,n; scanf("%d",&t); while(t-- ) { scanf("%d", &n); a[1].x=0; a[1].y=0; //把起点(0,0)加上 for(int i = 2; i <= n+1; i++) scanf("%d %d", &a[i].x, &a[i].y); for(int i = 1; i <= n+1; i++) { for(int j = 1; j <= n+1; j++) { d[i][j] = dis(i, j); //d[i][j]为i点到j点的距离 } } dp[1][2] = d[1][2]; for(int i = 3; i <= n+1; i++) { for(int j = 1; j < i-1; j++) { dp[j][i] = dp[j][i-1] + d[i-1][i]; /* dp[j][i]为j点到1点,再从1点到i点的距离,这一步是为下一循环求dp[i][i+1]做准备,其实就是图a */ } dp[i-1][i] = 999999999; for(int j = 1; j < i-1; j++) { int sum = dp[j][i-1] + d[j][i]; if(dp[i-1][i] > sum) dp[i-1][i] = sum; /* dp[i-1][i]为i-1点到1点,再从1点到i点的最短距离,这个距离只要加上边d[i-1][i]就是从1点到i点的最短闭合旅程,其实就是图b */ } } dp[n+1][n+1] = dp[n][n+1] + d[n][n+1]; printf("%d\n", dp[n+1][n+1]+10*n); /* dp[n+1][n+1]就是最终的最短闭合旅程,n+1点到1点,再从1点到n+1点的最短距离 ,10*n为读取点中数据的时间 */ } return 0; }
leetCode解题报告5道题(十),布布扣,bubuko.com
原文地址:http://blog.csdn.net/ljphhj/article/details/26134395