/*首先进行吐槽:这题交了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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
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;
}