区间DP

讲述

  • 区间动态规划,顾名思义,就是在区间上进行动态规划

  • 通常情况下,这类问题在进行一次决策后,决策区间会由一个大区间分成两个小区间,问题也由一个规模较大问题分解成两个规模较小的问题。

    这里的一次决策,相当于将一个大的集合划分成两个小的集合。

  • 这样,就可以从长度较小的区间长度较长的区间进行递推。

例题1:矩阵最优连乘

在这里插入图片描述


解答
在这里插入图片描述
在这里插入图片描述

个人感觉和【石子合并】是相同的。属于那种比较直观的典型的区间DP问题。

例题2:括号匹配(子序列)⭐⭐⭐⭐⭐

在这里插入图片描述

需要注意上面的第四点,st匹配,我之前忽略了这种情况。

在这里插入图片描述

在这里插入图片描述
“否则”要去掉。

这里的解题思路是根据括号匹配的三种情况而来的。

  • 第一种是空串括号匹配。这个无需多言;
  • 第二种是s括号匹配,则(s)[s]括号匹配。这种情况发生的条件是子序列的左右分别出现[]或者()
  • 第三种是s,t括号匹配,则st括号匹配。这种情况就要枚举所有的可能的相邻匹配了。

其实就是相当于模式匹配,枚举这三种模式匹配的情况!

在这里插入图片描述

例题3:石子合并

在这里插入图片描述
不相邻就用哈夫曼树解题~
还是和矩阵最优连乘一样,你要合并肯定包括最后两堆和合并。那么就可以 枚举分割点 了!
在这里插入图片描述

例题4:环形石子合并

在这里插入图片描述
这里就变成了 O ( ( 2 n ) 3 ) O((2n)^3) O((2n)3) ,只是常数变大了一些而已。妙啊,远远由于 O ( n 4 ) O(n^4) O(n4)

模板总结

区间DP模板是三重循环

  • 一重循环:区间长度
  • 二重循环:区间起点
  • 三重循环:区间分割点

区间 DP 常用模版 所有的区间dp问题,第一维都是枚举区间长度,一般 len = 1 用来初始化,枚举从 len = 2 开始,第二维枚举起点 i (右端点 j 自动获得,j = i + len - 1

// 先初始化区间长度为1的情况
for (int i = 1; i <= n; i++) {
    dp[i][i] = 初始值
}
//区间长度
for (int len = 2; len <= n; len++)
    //枚举起点
    for (int i = 1; i + len - 1 <= n; i++) {
        //区间终点
        int j = i + len - 1;
        for (int k = i; k < j; k++) {
            //枚举分割点,构造状态转移方程
            dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]);
        }
    }

题型训练

  1. ⭐⭐⭐⭐⭐【区间DP】LeetCode 664. Strange Printer
  2. ⭐⭐⭐⭐⭐【区间DP】LeetCode 312. Burst Balloons
  3. 最长回文子序列
  4. ⭐⭐⭐⭐⭐【区间DP】LeetCode 1039. Minimum Score Triangulation of Polygon

参考资料

  1. 视频:西北工业大学ACM2017暑假集训 - 区间DP
  2. 闫式DP分析法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值