hdu 2224(双调旅行商问题)

来源:互联网 发布:java web项目访问路径 编辑:程序博客网 时间:2024/06/16 15:02

The shortest path

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)


Problem Description
There are n points on the plane, Pi(xi, yi)(1 <= i <= n), and xi < xj (i<j). You begin at P1 and visit all points then back to P1. But there is a constraint:
Before you reach the rightmost point Pn, you can only visit the points those have the bigger x-coordinate value. For example, you are at Pi now, then you can only visit Pj(j > i). When you reach Pn, the rule is changed, from now on you can only visit the points those have the smaller x-coordinate value than the point you are in now, for example, you are at Pi now, then you can only visit Pj(j < i). And in the end you back to P1 and the tour is over.
You should visit all points in this tour and you can visit every point only once.
 

Input
The input consists of multiple test cases. Each case begins with a line containing a positive integer n(2 <= n <= 200), means the number of points. Then following n lines each containing two positive integers Pi(xi, yi), indicating the coordinate of the i-th point in the plane.
 

Output
For each test case, output one line containing the shortest path to visit all the points with the rule mentioned above.The answer should accurate up to 2 decimal places.
 

Sample Input
31 12 33 1
 

Sample Output
6.47Hint: The way 1 - 3 - 2 - 1 makes the shortest path.
解题思路:这道题目是一道双调旅行商问题:

欧几里德旅行商问题是对平面上给定的n个点的确定一条连接各点的最短闭合旅程的问题。图a给出了一个7个点问题的解。这个问题的一般形式是NP完全的,故其解需要多余多项式的时间。

J.L.Bentley建议通过只考虑双调旅程来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。图b显示了同样的7个点问题的最短双调路线。在这种情况下,多项式时间的算法是可能的。

描述一个确定最优双调路线的O(n^2)时间的算法。可以假设任何两点的x坐标都不相同。

双调欧几里得旅行商问题【转载】 - kevinlee_2010 - 云淡风轻

一个人从最左点开始,严格地从左到右直至最右点,然后从右到左直至出发点,可以等价为两个人同时从最左点,严格地从左到右经历不同路径到达最右点。假设这两个人为A和B,且A总是走在B后面。设Pij表示A走到pi、B走到pj时两人所经过的最短双调路径,根据假设,可得i<=j。又设dp[i, j]表示最短双调路径Pij的长度,dis[i, j]表示点pi到点pj的直线距离,则:dp[1, 2]=dis[1, 2]当i=j时,即A和B处于同一点,dp[i, j]=dp[i, i]=dp[i-1, i]+dis[i-1, i]当i=j-1时,即A在B紧邻的靠后一点,dp[i, j]=dp[j-1, j]=min(1<=k<j-1){dp[k, j-1]+dis[k, j]} 当i<j-1时,即A在B后且相隔多个点,dp[i, j]=dp[i, j-1]+dis[j-1, j]

由几何学知识可得,如果中间路径A和B经历了同一点,则这条路径肯定不是最短路径,故i=j的情况只可能用来计算b[n, n]=b[n-1, n]+d[n-1, n]。

这里最难理解的是当i=j-1时,按照定义的dp来讲,dp[k][j-1]应该是A走到k,B走到j-1,那这样怎么会走到i的呢?其实这里A和B的位置不是固定的,这里实际上是把之前k给了A,j-1给了B,因为A与B两者只差一个位置,如果只是从j-1转移到j的话,那么就会走重复的点,肯定不能这样,由于只要知道A与B的路径之和就行了,所以A与B的位置并不会固定死了。这个状态转移确实要多想想。

AC:
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>using namespace std;const int inf = 0x3f3f3f3f;int n;struct node{double x,y;}pos[205];double dp[205][205];double distance(int i,int j){double tmp;tmp = (pos[i].x - pos[j].x)*(pos[i].x - pos[j].x) + (pos[i].y - pos[j].y)*(pos[i].y - pos[j].y);return sqrt(tmp);}int main(){while(scanf("%d",&n)!=EOF){for(int i = 1; i <= n; i++)scanf("%lf %lf",&pos[i].x,&pos[i].y);dp[1][2] = distance(1,2);for(int j = 3; j <= n; j++){// i < j-1   dp[i][j] = dp[i][j-1] + distance(j-1,j);for(int i = 1; i < j - 1; i++){dp[i][j] = dp[i][j-1] + distance(j-1,j);}dp[j-1][j] = inf;//i = j-1   dp[i][j] = min{dp[k][i]+distance(k,j)}for(int i = 1; i < j - 1; i++){double tmp = dp[i][j-1] + distance(i,j);if(tmp < dp[j-1][j])dp[j-1][j] = tmp;}}dp[n][n] = dp[n-1][n] + distance(n-1,n);printf("%.2lf\n",dp[n][n]);}return 0;}


0 0
原创粉丝点击