UVA1347 Tour (DP)

题目大意:有n个点,给出x、y坐标。找出一条路,从最左边的点出发,严格向右走到达最右点再严格向左回到最左点。问最短路径是多少?

分析:
1.首先需要将原问题转化为,两个人A、B同时从最左边的点出发,一起严格向最右点走,且经过所有点一次(除了最左点和最右点)。这与原题的要求具有等价性。
2.先自然想到用dp(i,j)表示A走到i,B走到j时的状态还需要走多远到终点(注意表示的是还有多少到终点,所以其结果与前面怎么走的无关),那么可以证明dp(i,j)==dp(j,i);这里有的人可能会疑惑为什么会相等,刚刚说过dp(i,j)表示 已经 达到这个状态后还需要走多远到达终点,与怎么到达这个状态的并没有关系,所以dp(i,j)和dp(j,i)只是两个人角色对换了而已。
3.想到这一步之后,会出现一个问题,就是dp(i,j)无法知道i、j之间的某些点是否已经走过了,所以我们需要进一步思考,刚刚我们提到,dp(i,j)==dp(j,i),那么我们就可以始终让i>=j(等于只有终点和起点达到)。如果j>i了,只需要交换A、B的角色即可,即将i换为j,j换为i。
4.有了这个条件之后,我们就可以规定dp(i,j)规定为:A在i,B在j(i>=j)且i之前的所有点都走过了,这样也不会漏解,为什么呢?我们的自然的方法中,之所以i~j之间有点不知道走过了没,就是因为我们允许A连续走了多步,比如A从P1->P5->P6,而B可能从P1->P2。所以P3,P4我们不知道有没有被A或者B走到,因为我们只知道A走到了P6而B走到了P2。但是你明显发现了,在刚刚那个例子中,P3、P4之后必须要被B走到。所以我们改进的dp(i,j)中可以让A和B一格一格走,要么A走,要么B走(其实只是让顺序变化了一下而已)。
5.有了刚刚的论证,我们的状态转移就变成了下面这样:
dp[i][j] = min(DP(i + 1, j) + dist(i, i + 1), DP(i + 1, i)+dist(j,i+1));
即要么A走,要么B走,如果A走的话,那么走到状态dp(i+1,j);如果B走,那么走到状态dp(i,i+1)到要求前面大于后面,所以dp(i,i+1)==dp(i+1,i)即可。注意dist(i,j)表示i-j的距离。

#include <bits/stdc++.h>

#define INF 0x3f3f3f3f
#define eps 1e-6
typedef long long LL;
const double pi = acos(-1.0);
const long long mod = 1e9 + 7;

using namespace std;

int N;

struct data
{
    double x,y;
}a[1005];

double dp[1005][1005];

double dist(int i,int j)
{
    return sqrt( (a[i].x - a[j].x) * (a[i].x - a[j].x) +
                 (a[i].y - a[j].y) * (a[i].y - a[j].y) );
}

double fun(int i,int j)
{
    if(dp[i][j] > 0)
        return dp[i][j];
    return dp[i][j] = min(fun(i + 1,j) + dist(i,i + 1),
                          fun(i + 1,i) + dist(j,i + 1));
}


int main()
{
    int cas = 1;
    while(cin >> N)
    {
        for(int i = 1;i <= N;i++)
            cin >> a[i].x >> a[i].y;
        memset(dp,0,sizeof(dp));
        for(int j = 1;j < N - 1;j++)
            dp[N - 1][j] = dist(N - 1,N) + dist(j,N);
        double ans = fun(1,1);
        printf("%.2f\n",ans);
    }
    return 0;
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值