题面
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
对于30%的数据,L <= 10000;
对于全部的数据,L <= 10^9。
输入的第一行有一个正整数L(1 <= L <= 10^9),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1 <= S <= T <= 10,1 <= M <= 100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。
首先,对于前百分之30的数据,很容易想到状态转移方程:
f[i]=min(f[i],f[i-j])+(has_stone[i]?1:0);
也就是,从i-s到i-t之中找一个最小的走过来。最后如果当前位置是个石头,就+1
然后外层循环正着扫,内层循环循环上面的式子。时间O(kn)空间O(n)
但是,n实在是太大了。时间和空间都接受不了。
怎么办?
我们发现,石头的数量实在是太少了!只有100!
也就是说,会有大片的空地,青蛙在这片空地上瞎蹦跶了半天,而根本就没有必要!
T最大是10,也就是说,如果想要跳到某个石头上,一定是从石头左边最多10个位置跳过来的,同理,从石头左边跳,也就最多跳到石头右边十个的位置,在某两个石头中间(记为i,i+1),stone[i]+10到stone[i+1]-10,在这中间这一段里面蹦跶是没有意义的,因为一定踩不到石头,而无论怎么蹦跶,最后都要踩到左边10个和右边10个里面,因此如果两个石头间隔大于20,就直接视为20.这样一来,时空复杂度都只有O(MT)了
代码:
#include <cstdio>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define MAXN 214748364
int L,s,t,M,p;
int stone[101];
int has_stone[101*21+10];
int f[101*21+10];
int min(int a,int b){return a<b?a:b;}
void solve(){
int ans=0;
for(int i=1;i<=M;i++)
if(stone[i]%s==0)
ans++;
printf("%d",ans);
return;
}
int main(){
scanf("%d%d%d%d",&L,&s,&t,&M);
for(int i=1;i<=M;i++)
scanf("%d",&stone[i]);
if(s==t){
solve();
return 0;
}
sort(stone+1,stone+1+M);
for(int i=1;i<=M;i++){
p+=min(stone[i]-stone[i-1],20);
has_stone[p]=1;
}
p+=min(L-stone[M],20);
for(int i=1;i<=p+9;i++)f[i]=MAXN;
for(int i=s;i<=p+9;i++){
for(int j=s;j<=t;j++)
f[i]=min(f[i],f[i-j]);
if(has_stone[i])
f[i]++;
}
int min_s=MAXN;
for(int i=p;i<=p+9;i++)min_s=min(min_s,f[i]);
printf("%d",min_s);
system("pause");
return 0;
}
原文:https://www.cnblogs.com/Zarax/p/12032629.html