HDU2224The shortest path(双调旅行商问题)经典

来源:互联网 发布:excel数据有效性应用 编辑:程序博客网 时间:2024/05/29 12:47

The shortest path

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 983    Accepted Submission(s): 490


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.
 

Author
8600
以下来源:http://www.cnblogs.com/fCarver7/archive/2012/09/11/2679773.html写得很清楚,所以就借介引用一下。

原题大意:平面n个不同的点(假设任意两点x坐标不同),旅程从最左点开始,严格地从左到右直至最右点,然后严格地从右到左至最左点。求最短双调路线。每个点只能走一次。

解法:

(1)对n个点按x坐标排序,我们用dp[i][j]表示其中一条路径走到i点,另一条路径走到j点(且i和j并不相连);则dp[i][j]==dp[j][i](想一想为什么),所以我们仅需要计算一个,我们只计算dp[i][j] (i<=j的情况)。另外,由几何学可以知道,如果存在交错路径,那该双调路径必然不是最优路径(我也不会从数学理论上严格证明T_T,但是画一画图很直观)。因此计算dp[i][i]没有意义(除了最右边一个点)。

(2)分两种情况:1. i<j-1,这种情况下,dp[i][j]只能由dp[i][j-1]进行转移,因为路径要求严格从左至右(到达最右点之后严格从右至左),所以能到达j点的只能是点j-1,否则的话j-1点没有被经过,与题设严格从左至右矛盾。此时dp[i][j]=dp[i][j-1]+dis[j-1][j]; 2. i==j-1,这种情况下,j点必由某一点k直接到达,则路径可分为一条路径从1到i;另一条路径从1到k,然后从k到j。计算方程时应该这么算:dp[i][j]=min(dp[i][j], dp[i][k]+dis[k][j]),有童鞋就疑问了,dis[i][k]此时肯定没有被计算啊,因为这种情况下必定有k<i,没错,dis[i][k]没有被计算,但是dis[k][i]计算过了,而且dis[i][k]和dis[k][i]是等价的,所以方程可改写为:dp[i][j]=min(dp[i][j], dp[k][i]+dis[k][j])。还有童鞋疑问,那么从点k+1到点j-2之间的点会不会有遗漏呢?肯定不会!因为这种情况下按题设和转移方程,点k+1,k+2,....,j-2肯定在从1到j-1(即从1到i)这条路径上。总之,一旦你觉得方程有问题可能会遗漏点,就想一下题设:严格从左至右,然后仔细理解一下第一种情况下的方程,问题就迎刃而解了。初始dp[1][2]=dis[1][2],最终结果为dp[n-1][n]+dis[n-1][n]   (n==2的情况单独处理)。

(3)复杂度

排序O(n*logn);

对于每个j,若i<j-1,则求dp[i][j]的转移费用为O(1),若i==j-1,则转移费用O(n),所以对于每一个j转移费用为O(n)+O(n)=O(n),共有n个j,所以总费用为O(n^2)。

所以复杂度为O(n*logn)+O(n^2)=O(n^2)。

<pre name="code" class="cpp">#include<stdio.h>#include<math.h>const int N =205;const double inf =99999999;double distance(double x,double y){    return sqrt(x*x+y*y);}double Min(double a,double b){    return a<b?a:b;}int main(){    double dis[N][N],x[N],y[N],dp[N][N];    //dp[i][j]代表0-->i,0-->j,但i与j不相连,属于两个不同的路径,没有公共点(除了0点)    int n;    while(scanf("%d",&n)>0)    {        for(int i=0;i<n;i++)        scanf("%lf%lf",&x[i],&y[i]);        for(int i=0;i<n;i++)        for(int j=i+1;j<n;j++)        dis[i][j]=dis[j][i]=distance(x[i]-x[j],y[i]-y[j]);        dp[0][1]=dis[0][1];        for(int i=2;i<n;i++)        {            for(int j=0;j<i-1;j++)             dp[j][i]=dp[j][i-1]+dis[i-1][i];             dp[i-1][i]=inf;             for(int j=0;j<i-1;j++)             dp[i-1][i]=Min(dp[i-1][i],dp[j][i-1]+dis[j][i]);        }        dp[n-1][n-1]=dp[n-2][n-1]+dis[n-2][n-1];        for(int j=0;j<n-1;j++)        dp[n-1][n-1]=Min(dp[n-1][n-1],dp[j][n-1]+dis[j][n-1]);        printf("%.2lf\n",dp[n-1][n-1]);    }}


0 0
原创粉丝点击