一个人初始在井底,距离地面高度为 n n n,现在给出两个数组, a [ i ] a[i] a[i]表示距离地面高度为 i i i时他能往上走多远(可以移动的距离为 [ 0 , a [ i ] ] [0,a[i]] [0,a[i]]), b [ i ] b[i] b[i]表示距离地面高度为 i i i时他要往下掉多远,输出一个数组 d [ i ] d[i] d[i],表示这个人在第 i i i次跳跃之后距离地面的距离,这个时候他还没往下掉
- 先把样例看懂,之后考虑做法,这道题实际上问的是从 n n n到 0 0 0的最短路,要求跳跃次数最少,我们先看,假设现在所处的位置是 u u u,那么我们能够到达哪些位置呢?显然应该是 [ u − a [ u ] , u ] [u-a[u],u] [u−a[u],u],我们设这个位置是 x x x,那么这个 x x x还要往下滑,它应该下滑到达 v = x + b [ x ] v=x+b[x] v=x+b[x]这个位置,那么这就构成了一条路径,所以这个问题就转化为最短路了
- 考虑使用 b f s bfs bfs求最短路,初始设 d [ i ] d[i] d[i]为从 n n n到 i i i的最短路径长度, d [ n ] = 0 d[n]=0 d[n]=0,其余都是无穷大,现在我们是从 u u u到 v v v,那么如果有 d [ v ] > d [ u ] + 1 d[v]>d[u]+1 d[v]>d[u]+1,那么说明应该更新最短路径,并把这个新的节点入队,同时,类似 d i j k s t r a dijkstra dijkstra思想,这个节点最短路径已经确定,删去
- 参考dls的写法,使用一个set作为初始位置集合,我们往里面填充 [ 0 , n + 1 ] [0,n+1] [0,n+1]这些数字,多一个 n + 1 n+1 n+1是为了方便查找,这样我们进行的操作就都是 l o g log log级别的了
- 最后有一个问题,如何记录路径,这里有三个位置, u , v , x u,v,x u,v,x, u u u表示当前位置, x x x表示跳到的位置, v v v表示从 x x x滑到哪个位置,那么要求的答案是尚未滑下的位置,也就是 x x x是最终的答案,但我们不能只记录 x x x,因为我们还要知道是哪条路径,这路径显然是 u → v u\rightarrow v u→v,但是由于是正向遍历,我们要从 0 0 0递归的往回找路径,这样统计出的路径是逆序,还要把路径反转
#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;
}
- 最短路的好题