首页 > 其他 > 详细

[动态规划]Codeforces 1453F Even Harder

时间:2021-03-12 00:24:37      阅读:39      评论:0      收藏:0      [点我收藏+]

题目大意

给定数组 \(a\)\(a_i\)? 表示从 \(i\) 能走到 \([i+1,i+a_i]\),问至少需要把几个 \(a_i\)? 改成 \(0\),才能使得 \(1\)\(n\) 有且仅有一条路径。\(n \leq 3000\)

题解

这题的难度不至于2700吧...

首先容易想到我们可以设 \(dp[i]\) 表示使得从 \(1\)\(i\) 有且仅有一条路径至少需要置0的数量,那么 \(dp[i]\) 肯定是从 \(dp[j](1\leq j< i)\) 转移过来的,并且对于所有的 \(k\in[j+1,i)\)\(a[k]+k<i\),不然就无法满足从 \(j\)\(i\) 只有一条路径,我们可以先从 \(j\) 跳到 \(k\),再从 \(k\) 跳到 \(i\)

然后发现这样设有点问题,我们是从上一个状态 \(j\) 跳到 \(i\),设 \(j\) 的上一个状态是 \(k\),我们转移时只保证了从 \(k\) 跳到 \(j\) 只有一条路径,并且 \(k\)\(j\) 之间的位置无法跳到 \(j\),但是若 \(k\) 能直接跳到 \(i\),那么从 \(1\) 跳到 \(i\) 就不止一种方案:先跳到 \(k\),再跳到 \(j\),再跳到 \(i\);先跳到 \(k\),再直接跳到 \(j\)。我们必须想办法保留下这个信息。所以我们设 \(dp[i][j]\) 表示从 \(1\) 跳到 \(i\) 只有一条路径,并且从转移到 \(i\) 的上一个位置最远能越过 \(i\) 跳到 \(j\),最少需置0的数量,那么有 \(dp[i][j+a[j]]=min(dp[i][j+a[j]],dp[j][k]+cnt),(j\in[1,i-1],k\in[j,n])\),其中 \(cnt=\sum_{p=j+1}^{i-1}[p+a[p]>i]\)。我们只需让 \(j\)\(i-1\)\(1\) 循环,不断更新 \(cnt\),同时维护后缀最小值 \(min\{dp[j][k]\},k\in[j,n]\),即可以 \(O(n^2)\) 的时间复杂度解决此题。

Code

#include <bits/stdc++.h>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType& T) {
    elemType X = 0, w = 0; char ch = 0;
    while (!isdigit(ch)) { w |= ch == ‘-‘;ch = getchar(); }
    while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
    T = (w ? -X : X);
}

int dp[3001][3001], minn[3001][3001], a[3001];
int T, N;

int main() {
    Read(T);
    while (T--) {
        Read(N);
        for (int i = 1;i <= N;++i) {
            Read(a[i]);
            for (int j = 1;j <= N;++j)
                dp[i][j] = minn[i][j] = 0x3f3f3f3f;
        }
        dp[1][1] = 0;
        for (int i = 1;i <= N;++i)
            minn[1][i] = 0;
        for (int i = 2;i <= N;++i) {
            int cnt = 0;
            for (int j = i - 1;j >= 1;--j) {
                if (j + a[j] < i) continue;
                dp[i][j + a[j]] = min(dp[i][j + a[j]], minn[j][i - 1] + cnt);
                ++cnt;
            }
            minn[i][i] = dp[i][i];
            for (int j = i + 1;j <= N;++j)
                minn[i][j] = min(minn[i][j - 1], dp[i][j]);
        }
        printf("%d\n", dp[N][N]);
    }
    return 0;
}

[动态规划]Codeforces 1453F Even Harder

原文:https://www.cnblogs.com/AEMShana/p/14521168.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!