【洛谷P1052】过河

很经典的一道题,也是很著名的一道状态压缩DP,十一的时候lch讲过但是没听懂= =,当时太弱(其实现在也很弱),然后也是因为这道题来学习了一下状态压缩dp,其实也没学多少,因为状态压缩说实话范围挺广的,可以hash可以离散化,可以bulabula..
然后这道题的思路理解之后其实也是蛮简单的,就是因为L太大,1e9时空都会超,又因为n<100,是一个稀疏图,所以呢,可以对他进行路径压缩,我们可以认为中间的点都是大跳过去的,所以对于长了很多的路程就去取模啊,模一模差不多就出来了。
我们用 f[i]表示在数轴的 i 点时所能踩石子的最少个数
那么很容易得出状态转移方程:
if(i点有石子) f[i]=min(f[i],f[i-j]+1)
else f[i]=min(f[i],f[i-j])
然而数轴长到fai起,那么我们就压缩一下
先把石子位置(用数组a来存放)从小到大排序,计算两两石子间的距离(用数组d来存放),如果距离<=t,那么a[i]=a[i-1]+d[i]
如果距离大于t,那么就需要压缩距离了,即 a[i]=a[i-1]+t+(d[i]%t)
然后还有要注意的两点
1.它的石子没说是已经排好序的,我看了样例以为都是排好序的,RE了一遍。
2.注意取p的范围,就是要在压缩的路程后面加一个t,因为不一定最后一个点刚好是石头最少的,可能是p+1,p+2,所以要从它转移过来,你应该循环到p+t,这让我wa了一次。
附AC代码

#include
#include
#include
#include
#include
#define min(a,b) (a>b?b:a)
using namespace std;
int l,p,dp[10000],stone[200],a[10000],s,k[200],d[200],t,m,n,b[10000];
int main()
{
//  freopen("std.in","r",stdin);
    cin>>l>>s>>t>>n;
    for (int i=1;i<=n;i++)
    cin>>stone[i];
    stone[0]=0;
    a[0]=0;
    sort(stone+1,stone+n+1);//排序
    for (int i=1;i<=n;i++)
    {
        d[i]=stone[i]-stone[i-1];//取两石子间的距离
        k[i]=d[i]%t;//对距离取模以压缩路径
        if (d[i]<=t+k[i])
        a[i]=a[i-1]+d[i];//a为压缩后的路径
        else a[i]=a[i-1]+t+k[i];
        b[a[i]]=1;//对石子进行标记
    }
    p=a[n]+t+(l-a[n])%t;
    memset(dp,0x7f,sizeof(dp));
    dp[0]=0;
    for (int i=1;i<=p+t-1;i++)
        for (int j=s;j<=t;j++)//dp过程
        {
            if (i-j>=0&&i-jif (b[i])
                dp[i]=min(dp[i-j]+1,dp[i]);
                else 
                dp[i]=min(dp[i-j],dp[i]);
            }
        }
    int mx=200;
    for (int i=p;i<=p+t-1;i++)//寻找答案,在p与p+t-1之间
    mx=min(mx,dp[i]);
    cout<return 0;
}

你可能感兴趣的:(动态规划)