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;
}

posted @ 2022-01-21 11:32  Bellala  阅读(47)  评论(0)    收藏  举报