cf1602 D. Frog Traveler(BFS+dp)
题意:
青蛙在深度为 \(n\) 的井底。位于深度 \(i\) 时可以向上跳 \([0,a_i]\) 米,跳到位置 \(k\) 时要下滑 \(b_k\) 米。问至少跳几次能跳到地面(深度为0),并输出每次跳到的高度(未下滑)。
思路:
\(dp[i]\) 表示从深度 \(n\) 开始至少跳几次能跳到深度 \(i\)(下滑后),则 \(dp[n]=0\),\(n\) 入队。
从 \(n\) 开始跳,能跳到 \(\forall i\in [n-a_n,n-1]\)(下滑前),那么可以更新 \(dp[i+b_i]= dp[n]+1\) ,这些 \(i+b_i\) 入队。
注意每个 \(i\) 第一次被更新后即为最小值。维护已经被更新的下滑前深度 \([lim,n]\) 的边界 \(lim\),每次只需更新小于 \(lim\) 的(下滑前)深度。
接下来每次取队首 \(t\),\(t\) 能跳到 \(\forall i\in [t-a_t,t-1]\)(下滑前),只用更新 \(i \in [t-a_t, lim -1]\) 。所有 \(dp[i+b_i]=dp[t]+1\) 并入队 \(i+b_i\) 。
路径:记录前驱点和前驱点一步跳到的高度(未下滑)
const int N = 3e5 + 5;
int n, a[N], b[N], dp[N];
PII fa[N]; //i,t,t为前驱点,i为t跳到的点(未下滑)
signed main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
memset(dp, -1, sizeof dp);
dp[n] = 0;
queue<int> q; q.push(n);
int lim = n;
while(q.size())
{
int t = q.front(); q.pop();
for(int i = t - a[t]; i < lim; i++)
if(dp[i + b[i]] == -1)
{
dp[i + b[i]] = dp[t] + 1;
q.push(i + b[i]);
fa[i + b[i]] = {t, i};
}
lim = min(lim, t - a[t]);
}
if(dp[0] == -1) return puts("-1"), 0;
printf("%d\n", dp[0]);
vector<int> ans;
int p = 0; while(p != n)
ans.pb(fa[p].se), p = fa[p].fi;
reverse(all(ans));
for(int i : ans) printf("%d ", i);
return 0;
}

浙公网安备 33010602011771号