#UVA1347#Tour(DP :双调欧几里得旅行商问题)

来源:互联网 发布:数车的caxa编程 编辑:程序博客网 时间:2024/05/21 19:47

Description

John Doe, a skilled pilot, enjoys traveling. While on vacation, he rents a small plane and starts visiting beautiful places. To save money, John must determine the shortest closed tour that connects his destinations. Each destination is represented by a point in the plane pi = < xi,yi >. John uses the following strategy: he starts from the leftmost point, then he goes strictly left to right to the rightmost point, and then he goes strictly right back to the starting point. It is known that the points have distinct x-coordinates. Write a program that, given a set of n points in the plane, computes the shortest closed tour that connects the points according to John's strategy.
Input

The program input is from a text file. Each data set in the file stands for a particular set of points. For each set of points the data set contains the number of points, and the point coordinates in ascending order of the x coordinate. White spaces can occur freely in input. The input data are correct.
Output

For each set of data, your program should print the result to the standard output from the beginning of a line. The tour length, a floating-point number with two fractional digits, represents the result. An input/output sample is in the table below. Here there are two data sets. The first one contains 3 points specified by their x and y coordinates. The second point, for example, has the x coordinate 2, and the y coordinate 3. The result for each data set is the tour length, (6.47 for the first data set in the given example).
Sample Input

3
1 1
2 3
3 1
4
1 1
2 3
3 1
4 2
Sample Output

6.47
7.89



题意:(先转一段描述)

这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。

下图(b)显示了同样的7个点的最短双调路线。求最短路线。

 图a            图b

注:在一个单位栅格上显示的平面上的七个点。 a)最短闭合路线,长度大约是24.89。这个路线不是双调的。b)相同点的集合上的最短双调闭合路线。长度大约是25.58。

这是一个算导上的思考题15-1。


很经典的Dp问题,会给出两种思路(两种不同的转移方法)


首先将给出的点排序,关键字x,重新编号,从左至右1,2,3,…,n。

定义dis[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],只需将其加上dis[n-1][n]就是最短双调闭合路线。

写出方程式:

Dp[i][j]=Dp[i-1][j]+dis[i][i-1];

Dp[i][i-1]=min(Dp[i][i-1],Dp[i-1][j]+dis[j][i]);

初始状态为Dp[2][1] = dis[2][1];


法二:

定义Dp[i][j]表示第一个人走到i,第二个人走到j,下一个需要访问的点为k,k = max(i, j) + 1;

Dp[k][j] = min(Dp[i][j] + dis[i][k]);

Dp[i][k] = min(Dp[i][j] + dis[j][k]);

Dp[2][1] = Dp[1][2] = dis[1][2];


Code:

法一:

StatusAcceptedLength1580LangC++ 5.3.0Submitted2017-07-17 09:56:14Shared

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int Max = 1100;const int INF = 0x3f3f3f3f;struct node{    int x, y;    bool operator < (const node & X)  const{        return x < X.x;    }}P[Max + 5];int N;double dis[Max + 5][Max + 5], Dp[Max + 5][Max + 5];bool getint(int & num){    char c; int flg = 1;    num = 0;    while((c = getchar()) < '0' || c > '9'){        if(c == '-')    flg = -1;        if(c == -1) return 0;    }    while(c >= '0' && c <= '9'){        num = num * 10 + c - 48;        if((c = getchar()) == -1)   return 0;    }    num *= flg;    return 1;}double Get_Dis(int a, int b){   return sqrt(1.0 * (P[a].x - P[b].x) * (P[a].x - P[b].x) + (P[a].y - P[b].y) * (P[a].y - P[b].y));}int main(){    while(getint(N)){        for(int i = 1; i <= N; ++ i)            getint(P[i].x), getint(P[i].y);        sort(P + 1, P + 1 + N);        for(int i = 1; i <= N; ++ i)            for(int j = i + 1; j <= N; ++ j)                dis[j][i] = dis[i][j] = Get_Dis(i, j);        Dp[2][1] = dis[1][2];        for(int i = 3; i <= N; ++ i){            Dp[i][i - 1] = 1.0 * INF;            for(int j = 1; j < i - 1; ++ j){                Dp[i][j] = Dp[i - 1][j] + dis[i - 1][i];                Dp[i][i - 1] = min(Dp[i][i - 1], Dp[i - 1][j] + dis[i][j]);            }        }        double Ans = 1.0 * INF;        for(int i = 1; i < N; ++ i)            Ans = min(Ans, Dp[N][i] + dis[i][N]);        printf("%.2lf\n", Ans);    }    return 0;}


法二:

StatusAcceptedLength1553LangC++ 5.3.0Submitted2017-07-18 17:29:12Shared

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<vector>#include<map>#include<algorithm>using namespace std;const int Max = 1000;struct node{    double x, y;    bool operator < (const node & X) const{        return x < X.x;    }}P[Max + 5];int N;double Dp[Max + 5][Max + 5], p[Max + 5][Max + 5];bool getint(int & num){    char c; int flg = 1;    num = 0;    while((c = getchar()) < '0' || c > '9'){        if(c == '-')    flg = -1;        if(c == -1) return 0;    }    while(c >= '0' && c <= '9'){        num = num * 10 + c - 48;        if((c = getchar()) == -1)   return 0;    }    num *= flg;    return 1;}double Get_Dis(int a, int b){    return sqrt(1.0 * (P[a].x - P[b].x) * (P[a].x - P[b].x) + (P[a].y - P[b].y) * (P[a].y - P[b].y));}int main(){    while(getint(N)){        for(int i = 1; i <= N; ++ i)            scanf("%lf%lf", &P[i].x, &P[i].y);        for(int i = 1; i <= N; ++ i)            for(int j = i + 1; j <= N; ++ j)                p[i][j] = p[j][i] = Get_Dis(i, j);        for(int i = 0; i <= N; ++ i)            for(int j = 0; j <= N; ++ j)                Dp[i][j] = 1e12;        Dp[1][2] = Dp[2][1] = p[1][2];        for(int i = 1; i <= N; ++ i)            for(int j = 1; j <= N; ++ j) if(i != j){                int k = max(i, j) + 1;                Dp[k][j] = min(Dp[k][j], Dp[i][j] + p[i][k]);                Dp[i][k] = min(Dp[i][k], Dp[i][j] + p[k][j]);            }        printf("%.2lf\n", Dp[N][N - 1] + p[N][N - 1]);    }}


阅读全文
0 0