题意 :
- 给两个长度为n的数组,初始位于索引n,目标是越过索引1,注意不能往后跳,每次可以跳0到 a [ i ] a[i] a[i]米,即,身处索引 i i i,可以跳 [ 0 , a [ i ] ] [0, a[i]] [0,a[i]]中的任意米,但是从i跳到j,会后退b[j]步,问最少多少步可以跳出, n < = 3 e 5 n<=3e5 n<=3e5,并打印每次跳后到达地方的位置坐标(下滑前)
思路 :
- 从n开始bfs,每次将跳到且下滑后的点入队,维护到达这个点的最小步数
- 优化剪枝是维护之前能到的最高(数值最小)的位置,枚举当前点能到达的点,如果低于等于最高位置,说明后面跳的更少的方案都可以被剪枝了(因为对于同一个点下滑的距离是确定的,只是跳的距离可以选择),break;并用当前点能到的最高的点维护最高位置
- 打印路径,由于是输出下滑前的点,而我们入队用的是下滑后的点,所以再开一个ans数组映射
- 打印路径的具体方案 :从终点开始,将点放入栈中,点变为其前驱,直到没有前驱(即前驱为-1),然后再依次取出栈顶即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;
const int N = 3e5 + 10;
int n, mi;
int a[N], b[N];
int pre[N], dist[N], ans[N];
int bfs()
{
mi = n;
queue<int> q;
memset(dist, 0x3f, sizeof dist);
memset(pre, -1, sizeof pre);
memset(ans, 0, sizeof ans);
dist[n] = 0;
q.push(n);
while (q.size())
{
int t = q.front(); q.pop();
if (t == 0) return dist[t];
for (int i = a[t]; i > 0; i -- )
{
int now = t - i; // 下滑前
int temp;
if (now >= mi) break;
if (now <= 0) temp = 0, now = 0;
else temp = now, now = now + b[t - i]; // temp是下滑前,now是下滑后
if (dist[now] > dist[t] + 1)
{
dist[now] = dist[t] + 1;
pre[now] = t;
ans[now] = temp; // 映射
q.push(now);
}
}
mi = min(mi, t - a[t]);
}
return -1;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int _ = 1;
// cin >> _;
while (_ -- )
{
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 1; i <= n; i ++ ) cin >> b[i];
cout << bfs() << endl;
// 打印路径 倒序输出
stack<int> stk;
int x = 0;
while (pre[x] != -1)
{
stk.push(x);
x = pre[x];
}
while (!stk.empty())
{
int now = stk.top(); stk.pop();
cout << ans[now] << ' ';
}
cout << endl;
}
return 0;
}