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;
    }
    
  • 相关阅读:
    软件测试中桩模块与驱动模块的概念与区别(转载),打桩
    DataFactory使用和注意,排列组合
    SCWS中文分词,功能函数实例应用
    按指定长度截取中英文混合字符串
    CSS截取中英文混合字符串长度
    使DIV相对窗口大小左右拖动始终水平居中
    浮动5-常用列表显示(案例)
    多选项卡切换原理
    使当前对象相对于上层DIV 水平、垂直居中定位
    使图片相对于上层DIV始终水平、垂直都居中
  • 原文地址:https://www.cnblogs.com/AEMShana/p/14521168.html
Copyright © 2011-2022 走看看