传送门:牛客
题目描述:
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在
这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成
数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥
的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括
S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需
要踩到的石子数。
输入:
10
2 3 5
2 3 5 6 7
输出:
2
首先,这道题的dp并不难推,随便推一推就可以出来了.显然我们每一个点都是可以被前面的点通过跳跃转移而来的,也就是:
但是当你仔细观察我们的数据范围时就会发现光光只有上述操作其实还是过不去本题的,因为我们的长度达到了 1 e 9 1e9 1e9,我们不可能开这么大的数组
所以我们需要进行路劲压缩操作,在这里我们不用数论知识进行证明,我们使用一种更为简单的方式进行证明
首先我们有这样的一个结论,也就是当我们的两个石头直接的距离差达到
>
s
∗
t
>s*t
>s∗t时我们完全可以使用我们的数字来组成这个范围内的任意一个数字.因为我们会发现我当数据等于
s
∗
t
s*t
s∗t时我们显然是可以是可以使用t个s
来组成,当我们的数字大于s*t
但是小于(t+1)*s
时,我们可以将我们的前面的t个s
逐步的增大,显然我们是可以组成的这里使用一种比较形象的方法浅浅的证明了一下
对于更加严谨的证明在这里就不给出了,可以自行搜索学习
下面是具体的代码部分:
```#include <iostream>
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int l,s,t,m;
int dp[maxn];int a[maxn];int vis[maxn];
int shinkpoint[maxn];int b[maxn];
int main() {
l=read();s=read();t=read();m=read();
for(int i=1;i<=m;i++) a[i]=read();
if(s==t) {
int cnt=0;
for(int i=1;i<=m;i++) {
if(a[i]%s==0) cnt++;
}
cout<<cnt<<endl;
return 0;
}
sort(a+1,a+m+1);
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=m;i++) {
if(a[i]-a[i-1]>=90) {
b[i]=b[i-1]+90;
}else {
b[i]=b[i-1]+a[i]-a[i-1];
}
vis[b[i]]=1;
}
int r=b[m]+90;
for(int i=1;i<=r;i++) {
for(int j=s;j<=t;j++) {
if(i-j<0) continue;
dp[i]=min(dp[i-j]+vis[i],dp[i]);
}
}
int ans=inf;
for(int i=b[m];i<=r;i++) ans=min(ans,dp[i]);
cout<<ans<<endl;
return 0;
}