• 区间dp


    概念

    所谓区间dp,顾名思义就是在一段区间上的动态规划。它既要满足dp问题的最优子结构和无后效性外,还应该符合在区间上操作的特点。我的理解是往往会对区间进行合并操作。抑或是单个元素(可看成一个小区间)跨区间进行操作。例如括号匹配问题,石子合并问题(通过多次的相邻合并,最后实质上会产生跨区间的合并,如果你把其中的石子看作参考系的话就很容易感觉出来),还有在整数中插入运算符号的问题(利用运算符的优先级以及交换律可看出)

    这样以来,如果我们要得知一个大区间的情况,由于它必定是由从多个长度不一的小区间转移而来(转移情况未知),我们可以通过求得多个小区间的情况,从而合并信息,得到大区间。

    对于一个长度为n的区间,确定它的子区间需要首尾两个指针,显然子区间数量级为n2,那区间dp的复杂度也就为n2

    模板

    for (int len = 1; len < n; len++) { //操作区间的长度
            for (int i = 0, j = len; j <= n; i++, j++) { //始末
                //检查是否匹配(非必须)
                for (int s = i; s < j; s++) {
                    //update
                }
            }
        }

    经典实例

    1、石子问题

    有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

    #include <cstdio>
    #define min(x, y) (x > y ? y : x)
    #define INF 0x3f3f3f3f
    using namespace std;
    
    const int maxn = 210;
    int dp[maxn][maxn];
    int sum[maxn];
    int a[maxn];
    
    int main(int argc, const char * argv[]) {
        
        int n;
        while (~scanf("%d", &n)) {
            for (int i = 1; i <= n; i++) {
                scanf("%d", &a[i]);
                sum[i] = sum[i - 1] + a[i];
            }
            for (int len = 1; len < n; len++) { //操作区间的长度
                for (int i = 1, j = len + 1; j <= n; i++, j++) { //始末
                    //检查是否匹配(非必须)
                    dp[i][j] = INF;
                    for (int s = i; s < j; s++) {
                        dp[i][j] = min(dp[i][j], dp[i][s] + dp[s + 1][j] + sum[j] - sum[i - 1]);
                    }
                }
            }
            printf("%d
    ", dp[1][n]);
        }
        return 0;
    }

    2、括号匹配

    题目大意:给一个括号序列,问序列中合法的括号最多有多少个,若A合法,则[A],(A)均合法,若A,B合法则AB也合法
    题目分析:和POJ 1141那道经典括号匹配类似,这题更简单一些,想办法把问题转化,既然要求最大的括号匹配数,我们考虑加最少的括号,使得整个序列合法,这样就转变成1141那题,开下脑动类比二分图最大匹配的性质,最大匹配+最大独立集=点数,显然要加入最少的点使序列合法,则加的最少的点数即为|最大独立集|,我们要求的是原序列的|最大匹配|,下面给出转移方程,和1141一模一样
    dp[i][i] = 1;
    然后枚举区间长度
    1)外围匹配:dp[i][j] = dp[i + 1][j - 1];
    2)外围不匹配,枚举分割点:dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]); (i <= k < j)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int maxn = 105;
    char str[maxn];
    int dp[maxn][maxn];
    
    bool ck(int i, int j) {
        if ((str[i] == '(' && str[j] == ')') || (str[i] == '[' && str[j] == ']')) {
            return true;
        } else {
            return false;
        }
    }
    
    int main(int argc, const char * argv[]) {
        while (~scanf("%s", str)) {
            if (str[0] == 'e') break;
            
            int len;
            len = strlen(str);
            memset(dp, 0, sizeof(dp));
            for (int l = 1; l < len; l++) {  //len =  j - i 为当前区间长度
                for (int i = 0, j = l; j < len; i++, j++) { // i++, j++
                    if (ck(i, j)) { // 匹配
                        dp[i][j] = dp[i + 1][j - 1] + 2;
                    }
                    // 讨论区间合并情况,求最大值
                    for (int pos = i; pos < j; pos++) {
                        dp[i][j] = max(dp[i][j], dp[i][pos] + dp[pos + 1][j]);
                    }
                }
            }
            printf("%d
    ", dp[0][len - 1]);
            
        }
        return 0;
    }
  • 相关阅读:
    Java -- Map
    Bootstrap -- 标签属性
    SQLServer -- 竟然默认不区分大小写
    ThinkPHP -- 问题
    ThinkPHP
    MVC-内容详情页显示内容
    未能加载文件或程序集“Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed”或它的某一个依赖项。
    Random.Next获取随即数
    Razor语法小记
    VisualStudio自定义代码段_方法二
  • 原文地址:https://www.cnblogs.com/lxqiaoyixuan/p/9738748.html
Copyright © 2020-2023  润新知