过河 (优化dp)
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。对于30%的数据,L <= 10000;对于全部的数据,L <= 10^9。
L非常的大,那么怎么办呢?我们发现s和t很小,所以如果两个石头的间隔超过一定值,第二个石头后面一定有区域,青蛙能跳到其中的任意一个点。事实证明如果区域大于st,后面的有一部分区域都是没用的,直接把区域变成就行了。然后直接dp。
#include <cstdio>
#include <algorithm>
const int maxn=105, maxl=1e5+10, INF=1e9;
int l, s, t, m, f[maxl];
int stone[maxn], stone2[maxn];
int main(){
scanf("%d%d%d%d", &l, &s, &t, &m);
for (int i=1; i<=m; ++i) scanf("%d", &stone[i]);
std::sort(stone+1, stone+m+1);
if (s==t){
int ans=0;
for (int i=1; i<=m; ++i)
if (stone[i]%s==0) ++ans;
printf("%d", ans);
return 0;
}
for (int i=1; i<=m; ++i){
if (stone[i]-stone[i-1]>100)
stone2[i]=stone2[i-1]+100;
else stone2[i]=stone2[i-1]+stone[i]-stone[i-1];
f[stone2[i]]=1;
}
int tmp=0, ans=INF;
for (int i=1; i<=stone2[m]+t; ++i){
tmp=f[i]; f[i]=INF;
for (int j=s; j<=t; ++j){
if (i-j<0) break;
f[i]=std::min(f[i], f[i-j]+tmp);
}
if (i>=stone2[m]) ans=std::min(ans, f[i]);
}
printf("%d
", ans);
return 0;
}