Codeforces Round #751 (Div. 2) D题

这篇博客讨论了一个数学问题,即一个人从井底开始,每次跳跃后会下滑,目标是找出用最少跳跃次数到达地面的最短路径。通过将问题转化为最短路问题,博主提出使用广度优先搜索(BFS)配合优先级队列来解决。在过程中,利用动态规划更新最短路径,并记录跳跃路径。最后,博主给出了完整的C++代码实现,并指出代码中如何存储和逆序输出路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个人初始在井底,距离地面高度为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次跳跃之后距离地面的距离,这个时候他还没往下掉

  • 先把样例看懂,之后考虑做法,这道题实际上问的是从nnn000的最短路,要求跳跃次数最少,我们先看,假设现在所处的位置是uuu,那么我们能够到达哪些位置呢?显然应该是[u−a[u],u][u-a[u],u][ua[u],u],我们设这个位置是xxx,那么这个xxx还要往下滑,它应该下滑到达v=x+b[x]v=x+b[x]v=x+b[x]这个位置,那么这就构成了一条路径,所以这个问题就转化为最短路了
  • 考虑使用bfsbfsbfs求最短路,初始设d[i]d[i]d[i]为从nnniii的最短路径长度,d[n]=0d[n]=0d[n]=0,其余都是无穷大,现在我们是从uuuvvv,那么如果有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,xuuu表示当前位置,xxx表示跳到的位置,vvv表示从xxx滑到哪个位置,那么要求的答案是尚未滑下的位置,也就是xxx是最终的答案,但我们不能只记录xxx,因为我们还要知道是哪条路径,这路径显然是u→vu\rightarrow vuv,但是由于是正向遍历,我们要从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;
}
  • 最短路的好题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值