Vijos 1002 过河

     这是我写的在Vijos上的第一题。这道题在我刚学完DP的时候,就做过。当时年少轻狂,没有看数据的范围,直接暴力DP,结果TLE。。。。后来就没有再碰过。知道最近觉得快要省赛了,有必要把原来没有做出来的题做一做,于是有了这篇博客。

    从那时学完的最简单的动规后,又学了一个名叫状压DP的算法,状压即状态压缩,把没有用的状态全部排除掉。BZOJ上就有一道状压DP的题(互不侵犯king)传送门!!而过河这道题就是一道状压DP。根据我现在的习惯,做题先看数据范围,然后判定算法。(1 <= L <= 10^9)这么大的数,根据经验应该用log级的算法,但是再看题目显然用不到任何log级的算法。而应该是DP。那么既然是DP又有这么大的数据范围,所以应该是状压DP。这样算法就判断出来了。那么该怎么压缩呢?这里要用到一个定理

Px+(P+1)y=Q     

      x,y是未知数,P是跳跃的距离,P+1也是跳跃的距离,对于任意Q,Q>=P*(P-1), x,y一定存在正整数解,换句话说当两个石子之间的距离大于等于T*(T-1)时,中间有相当大的距离是每个点都可以跳到的,因为没有石子,所以对答案没有贡献,可以取模(%90),这样就很好办了。。。于是我们就成功地把状态给压缩了。

注意:石子并没有按顺序给出要排序。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define inf 1<<30
using namespace std;
int l,s,t,m,f[900000],pos[200],a[900000];
void slove(){
    int ans=0;
    for(int i=1;i<=m;i++)
        if(pos[i]%s==0) ans++;
    printf("%d",ans);
}
int main()
{
    scanf("%d%d%d%d",&l,&s,&t,&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&pos[i]);
    sort(pos+1,pos+m+1);
    if(s==t){
        slove();
        return 0;
    }
    for(int i=1;i<=m;i++){
        int temp=pos[i]-pos[i-1];
        pos[i]=pos[i-1]+temp%90;
    }
    l=pos[m]+(l-pos[m])%90;
    for(int i=1;i<=m;i++)
        a[pos[i]]=1;
    for(int i=1;i<=l;i++)
        f[i]=inf;
    f[0]=0; 
    for(int i=1;i<=l;i++)
        for(int j=s;j<=t;j++)
            if(i-j>=0)
                f[i]=min(f[i-j]+a[i],f[i]);
    printf("%d",f[l]);
}
原文地址:https://www.cnblogs.com/xtx1999/p/4823073.html