首先仔细观察发现一些性质
比如最多向左跳一次,而且必须第一次就跳。
这样只要每次让跳到的新的i+a[i]尽量的大就可以了。(60分做法)
把握住让i+a[i]尽量大,考虑将每一个装置的可以向后跳到的区间按照右端点从小到大排序(因为左边随便跳),倒序枚举,从问题的最后一步向前推,首先确定一个区间的右端点j,使得j在能用k步跳出的区间的最左端,若当前区间右端点>j,显然ans=k,同时我们要记录一下“能”跳出去的区间的最左d端的值,若前面的区间不能够跳到j的右端,k加一,让他先跳到前面的那个点再跳出去,即更新j=min,这样做一定是最优的。
标准题解:
教主的游乐场(park) 排序,贪心
无论从哪个装置开始,若要跳跃次数最少,最多向左跳1次,并且这1次是在一开始跳的,因为如果向右跳后再向左跳,那么显然这次向左能跳到的装置前面也可以跳到。那么每次跳跃则要选择一个能让下一步跳到的位置编号尽量大的装置,因为跳到第i个装置,它下一次的行动范围为[1, i+a[i]],所以显然要i+a[i]得到最大。这样可以得到60%的分数。
正因为每次选择一个能让下一步跳到的位置编号尽量大的装置总可以得到最优解,对于每个装置,下一步能跳到的装置编号为i+a[i](称之为右边界),那么若按右边界排序从大到小排序后,每个装置为起点的答案就会是不下降的。若在k步能到达的装置里面选一个装置编号最小的i,那么对于k步内不可达的装置,若右边界大等于i,那么它即为k+1步可达的,同样在里面选出个编号最小的。由于可以向左无限跳,所以不必考虑左边界。1步可达的编号为n+1。
PS:抽象模型,每个装置变成区间[i, i+a[i]],问题就变成了区间覆盖的变种。
#include<cstdio>
#include<algorithm>
struct newtype
{
int num,w;
};
newtype b[100010];
int ans[100010];
using namespace std;
bool cmp(const newtype & a, const newtype &b)
{
return a.w<b.w;
}
int main()
{
int n,m,x;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&x); b[i].w=i+x; b[i].num=i;
}
sort(b+1,b+n+1,cmp);
int j,i=n,min=n+1,k=0;
while(i>0)
{
j=min; k++;
while(b[i].w>=j)
{
ans[b[i].num]=k;
if(b[i].num<min) min=b[i].num;
i--;
}
}
for(int i=1;i<=m;i++)
{
scanf("%d",&x); printf("%d ",ans[x]);
}
}