【紫书】(UVa1347)Tour

继续考虑dp题目。

题意分析

其实这里只是更加仔细的做一个lrj的复读机(Orz
他分析了一个很重要的结果:如果是一个人从左到右再回来,并且每个点恰经过一次,那么等价于两个人从左到右每个点经过一次地遍历这些点。因为这样,我们才能够得到d[i,j]这样的方法来记录值。
但是这样也不够。d[i,j]我们最开始得到的是比较简单的记录状态:A走到i,B走到j得到的最小距离——那我怎么知道A和B会不会重复的走?(i,j)能不能转移到(i+1,j)?我们的状态里面没有保存这个结论。也就是说,这个状态定义的不好。
因此,我们定义成这样:把原来的状态表示成1max(i,j)的点全部走过的情况下两人能够得到的最小值。那么状态的转移就很显然了。考虑到d(i,j)=d(j,i),规定i>j。直接就可以得到d(i,j)=min(d(i+1,j)+dist(i,i+1),d(i+1,i)+dist(j,i+1))
这样会不会漏解呢?不会。如果i能够直接走到i+2,那么根据定义就无法走到i+1了。因此,我们让j走到i+1,是能够做到不遗漏的——因为我们之前考虑的情况不存在。
从上面不是我的分析可以看出,一个对题目深入分析得到的状态对dp题目的解决多么重要。

代码

很神秘,我用记忆化搜索写了半天,tle了六次……可能真的是有效率问题。以我现在的水平搞不明白是怎么回事,以后再解决吧。但是这里用循环不困难。

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <queue>
#include <set>
#include <iomanip>
#include <vector>
#define ZERO(x) memset((x),0,sizeof(x))
using namespace std;
//const int maxn=;
int x[1005],y[1005];
double dp[1005][1005];
double dist[1005][1005];
double inf;
int n;
int main()
{
    while(scanf("%d",&n)==1)
    {
        memset(dp,0x43,sizeof(dp));
        inf=dp[0][0];
        for(int i=1;i<=n;++i)
            scanf("%d%d",&x[i],&y[i]);
        for(int i=1;i<=n;++i)
            for(int j=1;j<i;++j)
                dist[j][i]=dist[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
        for(int i=n-1;i>=1;--i)
            for(int j=1;j<i;++j)
                if(i==n-1) dp[i][j]=dist[i][n]+dist[j][n]; 
                else dp[i][j]=min(dp[i+1][j]+dist[i][i+1],dp[i+1][i]+dist[i+1][j]);
        printf("%.2lf
",dp[2][1]+dist[1][2]);
    }
    return 0;
}
如非注明,原创内容遵循GFDLv1.3发布;其中的代码遵循GPLv3发布。
原文地址:https://www.cnblogs.com/samhx/p/9652105.html