题目大意:一只青蛙要从数轴原点向右跳过L(L<=10^9)的距离,在(0,L)上存在m个位于整点位置的石头(m<=100),青蛙每次跳跃可以向右跳[s,t](1<=s<=t<=10)间的一个整数距离。请问它最少踩到几块石头?
思路:
本题容易从动态规划的角度去思考,用表示跳到位置i时最少踩了几个石头,直接枚举向右跳多远更新即可。但是L太大了,铁定超时超空间。
不过我们发现这题的m却是出奇的小,说明数轴上的石头其实非常稀疏。我们稍加观察就可以发现,大量的转移是没有意义的,因为如果两个石头和间的距离过大,很快数组会出现一个分界点,满足清一色等于一个相同的值。实际上,我们只要保证依然清一色就可以了,前面那么多一样的位置完全可以去掉,因为他们和之后的转移没有半毛钱关系,去掉并不会影响答案。
那么问题来了,我们怎么知道这个分界点是在哪里呢?究竟距离达到什么程度才可以压缩?如果你赶时间,我可以直接告诉你结论:令=110,只要两块石头的距离超过这个值,就直接将距离变为这个值,这样的压缩是没有问题的,然后再用一开始的方法动态规划即可在很短的时间内求出答案。
下面就是相关证明了:
我们考虑一个点,从它向右跳,可以达到哪些点呢?假设在步以内,我们可以分类讨论:
跳1次可以到达:
跳2次可以到达:
......
跳次可以到达:
上面这个区间的并就是可以到达的点的集合。
可以发现,比较小的时候,这些区间之间存在间隙,但是当大到一定程度,第个区间和第个区间之间就能实现无缝衔接,这就意味着,从无缝衔接开始往后,所有位置都可以到达。
举个例子:x=0,s=3,t=4,那么区间分别为[3,4],[6,8],[9,12],[12,16]……可以发现除了1,2,5不可以到达以外,其它位置都可以到达,后面的区间都会完美覆盖数轴,不会漏出空隙。
我们只要求出最早出现完美覆盖的,就可发现压缩距离的关键所在。
也就是求最小的,满足,求得,根据题目给出的数据范围,右边的式子不可能超过9,也就是说无论如何,9步之后所有的位置都可以到达。
我们再来看数组,很显然如果后面没有石头的干扰,f数组会逐渐变成清一色的值,具体地,由于中的每个位置都可以到达向后的任意位置,所以都会等于。我们令,即可保证压缩后,对下一个石头的转移没有影响(因为它前面个位置依然都是清一色的值)。也就是说时可以保证这样的压缩万无一失。为了保险,我的程序设置的是125。
这题还有一个坑,那就是s=t的情况,这个时候不能用上面的分析,因为这个式子的分母会变成0。这个时候注意特判掉,直接看有多少个石头的位置是t的倍数即可。
说了那么多,其实想起来挺快的,也就几分钟的事情,写起来也很方便,具体看代码吧(这个代码在洛谷上AC了,不过如果读者发现其它什么错误还请留言哈)。
#include
#include
#include
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int Lim=125,N=100;
int s,t,m,n,i,j,ans,L,a[N+5],b[N+5],f[N*Lim+50],stone[N*Lim+50];
void updata(int &x,int y) { if (x==-1) x=y; else x=min(x,y); }
int main()
{
scanf("%d",&L);
scanf("%d%d%d",&s,&t,&m);
rep(i,1,m) scanf("%d",&a[i]);
sort(a+1,a+1+m);
if (s==t)
{
rep(i,1,m) if (a[i]%t==0) ans++;
printf("%d\n",ans);
return 0;
}
rep(i,1,m) b[i]=min(a[i]-a[i-1],Lim);
rep(i,1,m) a[i]=a[i-1]+b[i],stone[a[i]]=1;
memset(f,-1,sizeof(f));
f[0]=0; n=a[m]+t; ans=m+1;
rep(i,0,n)
if (f[i]!=-1)
rep(j,s,t)
updata(f[i+j],f[i]+stone[i+j]);
rep(i,a[m]+1,n)
if (f[i]!=-1) ans=min(ans,f[i]);
printf("%d\n",ans);
return 0;
}