一个人初始在井底,距离地面高度为nnn,现在给出两个数组,a[i]a[i]a[i]表示距离地面高度为iii时他能往上走多远(可以移动的距离为[0,a[i]][0,a[i]][0,a[i]]),b[i]b[i]b[i]表示距离地面高度为iii时他要往下掉多远,输出一个数组d[i]d[i]d[i],表示这个人在第iii次跳跃之后距离地面的距离,这个时候他还没往下掉
- 先把样例看懂,之后考虑做法,这道题实际上问的是从nnn到000的最短路,要求跳跃次数最少,我们先看,假设现在所处的位置是uuu,那么我们能够到达哪些位置呢?显然应该是[u−a[u],u][u-a[u],u][u−a[u],u],我们设这个位置是xxx,那么这个xxx还要往下滑,它应该下滑到达v=x+b[x]v=x+b[x]v=x+b[x]这个位置,那么这就构成了一条路径,所以这个问题就转化为最短路了
- 考虑使用bfsbfsbfs求最短路,初始设d[i]d[i]d[i]为从nnn到iii的最短路径长度,d[n]=0d[n]=0d[n]=0,其余都是无穷大,现在我们是从uuu到vvv,那么如果有d[v]>d[u]+1d[v]>d[u]+1d[v]>d[u]+1,那么说明应该更新最短路径,并把这个新的节点入队,同时,类似dijkstradijkstradijkstra思想,这个节点最短路径已经确定,删去
- 参考dls的写法,使用一个set作为初始位置集合,我们往里面填充[0,n+1][0,n+1][0,n+1]这些数字,多一个n+1n+1n+1是为了方便查找,这样我们进行的操作就都是logloglog级别的了
- 最后有一个问题,如何记录路径,这里有三个位置,u,v,xu,v,xu,v,x,uuu表示当前位置,xxx表示跳到的位置,vvv表示从xxx滑到哪个位置,那么要求的答案是尚未滑下的位置,也就是xxx是最终的答案,但我们不能只记录xxx,因为我们还要知道是哪条路径,这路径显然是u→vu\rightarrow vu→v,但是由于是正向遍历,我们要从000递归的往回找路径,这样统计出的路径是逆序,还要把路径反转
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 3e5 + 100;
pair<int, int> PII[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
vector<int> a(n + 1), b(n + 1), d(n + 1, INF);
set<int> s;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=1;i<=n;i++) cin >> b[i];
for(int i=0;i<=n+1;i++) s.emplace(i);
queue<int> q;
d[n] = 0;
q.push(n);
while(!q.empty()){
int u = q.front();
q.pop();
while(1){
int x = *s.lower_bound(u - a[u]);
if(x > u) break;
s.erase(x);
int v = x + b[x];
if(d[v] > d[u] + 1){
d[v] = d[u] + 1;
PII[v] = make_pair(x, u);// x是答案,u是路径上的点
q.push(v);
}
}
}
if(d[0] == INF) cout << -1;
else{
vector<int> ans;
int x = 0;
while(x != n){
ans.emplace_back(PII[x].first);
x = PII[x].second;
}
reverse(ans.begin(), ans.end());
cout << ans.size() << '\n';
for(int i=0;i<ans.size();i++){
if(i > 0) cout << ' ';
cout << ans[i];
}
}
return 0;
}
- 最短路的好题