題意
你是一只青蛙(霧),現在你掉到了一個\(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;
}