Vijos1002 过河

/*首先进行吐槽:这题交了N遍却总是RE,最后发现是数组开小了……
结论:对内存省吃俭用不一定有好处……*/

  • 思路:转移方程相当明显,即f[i]=f[j]+stonenum[i], j∈[i-t,i-s]。但是,10^9的数据规模直接DP显然不行。注意到m<=100,说明石子在桥上的分布是十分稀疏的,也就是说,有很长一段距离中没有石子,对这一段进行DP完全是浪费,于是我们要进行压缩。但是,压缩的下限是多少?这里取s、t的极值,令s=9,t=10,从0开始一步一步分阶段地将可能到达的点列举出来,发现当到达72以后青蛙便能到达72以后的每一个点,每个点都是等价的(因为它们的DP值与前面的点相同),那么若两点间的距离大于72,就可以对这两点进行压缩了,这里我选择大于100时利用pos[i+1]=pos[i]+(pos[i+1]-pos[i])%100进行处理。处理过后再进行DP就能通过了。

  • 注意:数组必须开的足够大(已吐槽……),并且当s=t时需要特判,只需记录下满足pos[stone] % s=0的个数即可。

  • 代码如下:

#include
#include
#include
#include
using namespace std;
int l,s,t,m,pos[1000];
int f[10000000],stone[10000000];

void init()
{
     scanf("%d",&l);
     scanf("%d%d%d",&s,&t,&m);
     for (int i=1;i<=m;++i)
       scanf("%d",&pos[i]);
     sort(pos+1,pos+m+1);
}

void dp()
{
     if (s==t)
     {
         int cnt=0; 
         for (int i=1;i<=m;++i)
         {
             if (pos[i]%s==0)
               cnt++;
         }
         printf("%d",cnt);
         return;
     }
     int i,j;
     memset(stone,0,sizeof(stone));
     for (i=1;i<=m-1;++i)
     {
         if (pos[i+1]-pos[i]>100)
         {
                pos[i+1]=pos[i]+(pos[i+1]-pos[i])%100;
         }
     }
     for (i=1;i<=m;++i)
       stone[pos[i]]=1;
     l=pos[m];
     for (i=1;i<=l+t;++i)
       f[i]=101;
     f[0]=0;
     for (i=s;i<=l+t;++i)
       for (j=s;j<=t;++j)
       {
           if (i-j>=0 && f[i]>f[i-j]+stone[i])
             f[i]=f[i-j]+stone[i];
       }
     int ans=m;
     for (i=l;i<=l+t;++i)
       ans=min(ans,f[i]);
     printf("%d",ans);
}

int main()
{
    init();
    dp();
    return 0;
}

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