当前位置:首页 > 算法 > 正文内容

【算法】动态规划—区间DP问题

亿万年的星光1周前 (06-12)算法40

一、定义

区间DP是动态规划的一种特殊形式,主要用于解决涉及区间操作的问题,比如合并区间、分割区间、区间匹配等。

其核心思想是通过枚举区间的划分点,将大区间的问题分解为小区间的子问题,逐步求解并保存中间结果。



二、基本特征


  • 问题定义:通常涉及一个序列或区间,如数组、字符串等。

  • 状态表示:一般用 dp[i][j] 表示区间 [i, j] 的最优解。

  • 转移方程:通过枚举区间内的某个分割点 k,将 [i, j] 拆分为 [i, k] 和 [k+1, j],然后合并子问题的解。

  • 初始化:通常 dp[i][i] 表示单个元素的最优解(如 dp[i][i] = 0 或 dp[i][i] = cost[i])。

  • 遍历顺序:一般采用区间长度从小到大的顺序计算,确保子问题先被求解。


三、区间DP模板

for (int len = 1; len <= n; len++) {          // 枚举区间长度
    for (int i = 0; i + len - 1 < n; i++) {   // 枚举起点i
        int j = i + len - 1;                  // 计算终点j
        if (len == 1) {                       // 初始化
            dp[i][j] = ...;
            continue;
        }
        for (int k = i; k < j; k++) {         // 枚举分割点k
            dp[i][j] = min/max(dp[i][j], dp[i][k] + dp[k+1][j] + cost);
        }
    }
}


四、例题:石子合并


题目:【题解】石子合并 - 青少年编程知识记录


问题描述:有 n 堆石子排成一排,每次只能合并相邻的两堆,合并的代价是两堆石子的总数。求将所有石子合并成一堆的最小总代价。

输入: stones = [3, 1, 2, 4]
输出: 20
解释:
1. 合并 [1, 2] → 代价=3, 剩余 [3, 3, 4]
2. 合并 [3, 3] → 代价=6, 剩余 [6, 4]
3. 合并 [6, 4] → 代价=10, 总代价=3+6+10=19


(1) 状态定义

  • dp[i][j]:表示合并区间 [i, j] 的所有石子堆的最小总代价。

  • 初始化:

    • 如果 i == j(即区间长度为1),dp[i][j] = 0(因为不需要合并)。

    • 否则 dp[i][j] = +∞(初始化为极大值,方便后续取最小值)。

(2) 状态转移方程

  • 对于区间 [i, j],枚举所有可能的分割点 ki ≤ k < j),将其分成两个子区间:

    • [i, k] 和 [k+1, j]

  • 合并这两个子区间的总代价是:

    • dp[i][k](合并 [i, k] 的代价)

    • dp[k+1][j](合并 [k+1, j] 的代价)

    • sum(i, j)(当前合并的代价,即区间 [i, j] 的石子总重量)。

  • 因此,状态转移方程为:

    dp[i][j]=minik<j(dp[i][k]+dp[k+1][j]+sum(i,j))

(3) 计算顺序

  • 按区间长度从小到大计算:

    • 先计算所有长度为2的区间,再计算长度为3的区间,依此类推,直到计算整个区间 [0, n-1]

  • 前缀和优化:

    • 为了快速计算 sum(i, j),可以预先计算前缀和数组 prefix

      sum(i,j)=prefix[j+1]prefix[i]

过程解释:

(注意分割点K)


(1) 初始化

  • 石子数组:[3, 1, 2, 4]n = 4

  • 前缀和 prefix

    • prefix[0] = 0

    • prefix[1] = 3

    • prefix[2] = 3 + 1 = 4

    • prefix[3] = 4 + 2 = 6

    • prefix[4] = 6 + 4 = 10

  • dp 表初始化为 ,对角线 dp[i][i] = 0

i \ j0123
00
1-0
2--0
3---0

(2) 计算长度为2的区间

  • 区间 [0, 1]:

    • dp[0][0] + dp[1][1] + sum(0,1) = 0 + 0 + (prefix[2]-prefix[0]) = 4

    • 分割点 k = 0

    • dp[0][1] = 4

  • 区间 [1, 2]:

    • dp[1][1] + dp[2][2] + sum(1,2) = 0 + 0 + (prefix[3]-prefix[1]) = 3

    • 分割点 k = 1

    • dp[1][2] = 3

  • 区间 [2, 3]:

    • dp[2][2] + dp[3][3] + sum(2,3) = 0 + 0 + (prefix[4]-prefix[2]) = 6

    • 分割点 k = 2

    • dp[2][3] = 6

更新后的 dp 表:

i \ j0123
004
1-03
2--06
3---0


(3) 计算长度为3的区间

  • 区间 [0, 2]:

    • dp[0][1] + dp[2][2] + sum(0,2) = 4 + 0 + 6 = 10

    • dp[0][0] + dp[1][2] + sum(0,2) = 0 + 3 + (prefix[3]-prefix[0]) = 9

    • 分割点 k = 0

    • 分割点 k = 1

    • dp[0][2] = min(9, 10) = 9

  • 区间 [1, 3]:

    • dp[1][2] + dp[3][3] + sum(1,3) = 3 + 0 + 7 = 10

    • dp[1][1] + dp[2][3] + sum(1,3) = 0 + 6 + (prefix[4]-prefix[1]) = 13

    • 分割点 k = 1

    • 分割点 k = 2

    • dp[1][3] = min(13, 10) = 10

更新后的 dp 表:

i \ j0123
0049
1-0310
2--06
3---0


(4) 计算长度为4的区间(最终结果)

  • 区间 [0, 3]:

    • dp[0][2] + dp[3][3] + sum(0,3) = 9 + 0 + 10 = 19

    • dp[0][1] + dp[2][3] + sum(0,3) = 4 + 6 + 10 = 20

    • dp[0][0] + dp[1][3] + sum(0,3) = 0 + 10 + (prefix[4]-prefix[0]) = 20

    • 分割点 k = 0

    • 分割点 k = 1

    • 分割点 k = 2

    • dp[0][3] = min(20, 20, 19) = 19

最终 dp 表:

i \ j0123
004919
1-0310
2--06
3---0

最小总代价:dp[0][3] = 19




扫描二维码推送至手机访问。

版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。

分享给朋友:
返回列表

上一篇:【算法】归并排序

没有最新的文章了...

相关文章

【算法】前缀和与差分(2)一 一维数组差分

【算法】前缀和与差分(2)一 一维数组差分

一、差分:一维数组的差分可看作是一维数组前缀和的逆运算。二、差分数组首先给定一个原数组a:   a[1]、a[2]、a[3]、......然后构造一个数组b: b[1]、b[2]、...

【贪心】----基本概念

一、基本概念所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,它所做出的仅仅是在某种意义上的局部最优解。贪心算法没有固定的算法框架,算法设计的关键是贪...

【排序】----冒泡排序

【排序】----冒泡排序

1.基本思想两个数比较大小,较大的数下沉,较小的数冒起来。2.过程·每次比较相邻的两个数,如果第二个数小,就交换位置。(升序或降序)·然后两两比较,一直到比较最后的数据。最终最小(大)数被交换到开始(...

【算法】二叉树(1):二叉树及其编号

【算法】二叉树(1):二叉树及其编号

0.前言        二叉树(Binary Tree)的递归定义如下:二叉树要么为空,要么由根结点(root)、左子树(left...

【贪心】最大子矩阵

【题目描述】已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1X1)子矩阵。比如,如下4 x 4的矩阵0    -2&...

【算法】博弈论——取石子游戏

【题目描述】有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取...