题意
你是一只青蛙(雾),现在你掉到了一个\(n\)米深的井里,当你位于深度为\(i\)的位置时,你可以往上跳\((0,a_i]\)米中的任意一个整数距离,如果你没有跳出去,那么你需要休息,假设你在深度为\(j\)的位置处休息,你就会往下掉\(b_j\)米,现在请问,最少多少步能跳出去,并且输出跳出去的方案。
做法
发现需要输出方案,考虑用\(bfs\)来做,我们发现如果我们跳到了一个位置\(j\),那么深度小于\(j\)的位置我们一定都已经在之前的跳跃过程中枚举过,也就是已经入队了,所以没有再次遍历一遍的需要。所以我们可以维护一个现在已经枚举到的最高位置,然后每次取出队首去扩展位置的时候,我们只需要枚举从之前的最高位置到这个点能到的最高位置即可,这样可以保证每个元素最多被入队一次,保证了时间复杂度为\(O(n)\)。然后对于输出方案,用一种类似链表的方法输出即可。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int qu[N*3],hea=1,tal=0;
int a[N],b[N],ans[N],pre[N],sp[N];
bool used[N];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int j=1;j<=n;j++)
scanf("%d",&b[j]);
used[n]=1;
qu[++tal]=n;
int mins=n;
bool flag=0;
while(hea<=tal){
int j=qu[hea++];
if(j-a[j]<=0){
flag =1;
printf("%d\n",ans[j]+1);
vector<int> otp;
int now=j;
while(now!=n){
otp.push_back(pre[now]);
now=sp[now];
}
for(int i=otp.size()-1;i>=0;i--)
printf("%d ",otp[i]);
printf("%d\n",0);
break;
}
for(int i=mins-1;i>=j-a[j];i--){
if(used[i])
continue;
int dest=i+b[i];
if(used[dest])
continue;
sp[dest]=j;//sp+pre = where
used[dest]=1;
pre[dest]=i;// before dropped
ans[dest]=ans[j]+1;
qu[++tal]=dest;
}
mins=min(mins,j-a[j]);
}
if(!flag)
puts("-1");
return 0;
}