zoukankan      html  css  js  c++  java
  • [动态规划]Codeforces 1453F Even Harder

    题目大意

    给定数组 (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](1leq j< i)) 转移过来的,并且对于所有的 (kin[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),(jin[1,i-1],kin[j,n])),其中 (cnt=sum_{p=j+1}^{i-1}[p+a[p]>i])。我们只需让 (j)(i-1)(1) 循环,不断更新 (cnt),同时维护后缀最小值 (min{dp[j][k]},kin[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
    ", dp[N][N]);
        }
        return 0;
    }
    
  • 相关阅读:
    BEC listen and translation exercise 44
    中译英12
    BEC listen and translation exercise 43
    中译英11
    BEC listen and translation exercise 42
    中译英10
    BEC listen and translation exercise 41
    中译英9
    BEC listen and translation exercise 40
    中译英8
  • 原文地址:https://www.cnblogs.com/AEMShana/p/14521168.html
Copyright © 2011-2022 走看看