Uva 1347 - Tour(DP)

题目链接 https://vjudge.net/problem/UVA-1347

【题意】
       按照x坐标的递增顺序给定平面上的n个点,每个点的x坐标保证不相同(n<=1000),你的任务是设计一条路线,从最左边的点出发,走到最右边的点后返回,要求除了最左端和最右端的两点以外,每个点恰好经过一次,且要让总路程尽量短,两点之间的长度为它们的欧几里德距离。

【思路】
       没什么思路,甚至想不到是用dp做,看了紫书上的讲解才明白的。首先是思路转换,将”从左到右再走回来”这么一个过程看成是”两个人同时从最左端出发,沿着两条不同的路径走,最后都到达最右端” 状态的设计也很巧妙,设dp(i,j)表示1~max(i,j)的所有结点都已经走过,且两个人当前的位置是i和j,此时还需要走的最短距离是多少。稍加思考,我们就知道,dp(i,j)=dp(j,i),所以我们规定dp(i,j)中满足i > j
       这样一来,不管是哪个人,下一步都可以往i+1,i+2…n这些点走,但是比如说在dp(i,j)的状态下如果某个人直接走到i+2这个点,情况就变成了“1~i和i+2走过,i+1没走过”,这是一种没法表示的状态,怎么办?直接禁止这样的状态转移,也就是说这两个人不管是哪个都只能往i+1走,状态dp(i,j)只能转移到dp(i+1,i)或dp(i+1,i),并且这样是不会漏解的,证明不难,简单的说就是如果第一个人直接从i走到i+2,那么i+1这个点就只能让第二个人来走了,既然这样就先让第二个人走到i+1,最终的结果不会变化。
       状态转移方程即为dp[i][j]=min {dp[i+1][j] + dis(i, i+1), dp[i+1][i]+dis(j,i+1)}
       边界是dp[n-1][x]=dis(n-1,n)+dis(x,n),就是两个人都往终点走。最后的答案便是dis(1,2)+dp[2][1]

#include
using namespace std;

const double inf = 2e9;
const int maxn = 1050;

int n;
double dp[maxn][maxn];

struct Point {
    double x, y;
}p[maxn];

double dis(Point& a, Point& b) {
    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}

void d() {
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) dp[i][j] = inf;
    }
    for (int j = 1; j < n - 1; ++j) {
        dp[n - 1][j] = dis(p[n - 1], p[n]) + dis(p[j], p[n]);
    }

    for (int i = n - 2; i >= 1; --i) {
        for (int j = 1; j < i; ++j) {
            dp[i][j] = min(dp[i][j], dp[i + 1][j] + dis(p[i], p[i + 1]));
            dp[i][j] = min(dp[i][j], dp[i + 1][i] + dis(p[j], p[i + 1]));
        }
    }

    double ans = dis(p[1], p[2]) + dp[2][1];
    printf("%.2lf\n", ans);
}

int main() {
    while (scanf("%d", &n) == 1) {
        for (int i = 1; i <= n; ++i) {
            scanf("%lf%lf", &p[i].x, &p[i].y);
        }
        d();
    }
    return 0;
}

转载于:https://www.cnblogs.com/wafish/p/10465348.html

你可能感兴趣的:(Uva 1347 - Tour(DP))