《区间DP》基础概念

一、算法概述

  1. 定义:区间动态规划(Interval Dynamic Programming)是一种处理区间上最优解问题的算法,通过将问题分解为小区间的子问题,逐步合并得到大区间的解。其核心思想是从长度最短的区间开始计算,逐步扩展到整个区间,避免重复计算。
  2. 核心特征
    • 状态定义为区间[i, j]的最优值,如dp[i][j]
    • 状态转移通过枚举区间内的分割点,将大区间拆分为两个小区间的组合。
  3. 应用场景
    • 石子合并问题(求合并石子的最小/最大代价)
    • 矩阵连乘问题(求最小乘法次数)
    • 区间表达式求值(添加运算符使表达式结果最优)

二、算法思路

  1. 状态定义
    • dp[i][j]表示区间[i, j]内的最优值(如最小代价、最大价值等)。
  2. 状态转移方程
    • 对于区间[i, j],枚举分割点ki ≤ k < j),将区间拆分为[i, k][k+1, j],则状态转移方程为:
      dp[i][j] = opt(dp[i][k] + dp[k+1][j] + cost(i, j))
      其中opt为最优操作(如取min/max),cost(i, j)为合并区间的代价。
  3. 初始化
    • 长度为1的区间[i, i]的最优值通常为初始值(如0或元素本身)。
  4. 遍历顺序
    • 先枚举区间长度(从小到大),再枚举区间起点,最后枚举分割点。

三、伪代码实现

1. 数据结构与全局变量

常量 maxn = 210  # 最大区间长度
变量 n  # 元素个数
二维数组 dp[maxn][maxn]  # dp[i][j]存储区间[i,j]的最优值
数组 a[maxn]  # 原始数据数组
数组 sum[maxn]  # 前缀和数组,用于快速计算区间和

2. 核心函数框架

算法:区间动态规划模板
输入:元素个数 n,数组 a[1..n]
输出:区间[1..n]的最优值

函数 IntervalDP_Opt(a, b):
    # 定义最优操作(示例为取最小值)
    return min(a, b)

函数 IntervalDP_ValueInf():
    # 定义无穷大值
    return 1000000000

函数 IntervalDP_ValueInit():
    # 定义初始值
    return 0

函数 IntervalDP_CalcState(l, r):
    # 计算区间[l, r]的最优值
    ans = IntervalDP_ValueInf()
    for k from l to r-1:
        # dp[l][k] + dp[k+1][r]为左右子区间的最优值,sum[r]-sum[l-1]为合并代价
        v = dp[l][k] + dp[k+1][r] + (sum[r] - sum[l-1])
        ans = IntervalDP_Opt(ans, v)
    return ans

函数 IntervalDP_Solve(maxlen, maxr):
    # 求解区间DP问题
    ans = IntervalDP_ValueInf()
    # 1. 枚举区间长度(从小到大)
    for len from 1 to maxlen:
        # 2. 枚举区间起点
        for j from 1 to maxr - len + 1:
            l = j
            r = j + len - 1
            if len == 1:
                # 长度为1的区间初始值
                dp[l][r] = IntervalDP_ValueInit()
            else:
                # 计算区间[l, r]的最优值
                dp[l][r] = IntervalDP_CalcState(l, r)
            if len == maxlen:
                # 记录最大长度区间的最优值
                ans = IntervalDP_Opt(ans, dp[l][r])
    return ans

# 主程序
输入 n
for i from 1 to n:
    输入 a[i]
    sum[i] = sum[i-1] + a[i]  # 计算前缀和
输出 IntervalDP_Solve(n, n)

四、算法解释

1. 前缀和优化

  • sum[i]存储前i个元素的和,sum[r] - sum[l-1]可快速计算区间[l, r]的元素和,避免重复计算。

2. 状态转移过程

  • 枚举区间长度:从长度1开始,逐步扩展到长度n
  • 枚举区间起点:对于每个长度len,计算所有可能的区间[j, j+len-1]
  • 枚举分割点:将区间[l, r]拆分为[l, k][k+1, r],合并两者的最优值并加上区间代价,得到大区间的最优值。

3. 示例场景:石子合并问题

  • 问题描述:有n堆石子排成一列,每次合并相邻两堆石子,代价为两堆石子数量之和,求合并所有石子的最小代价。
  • 状态定义dp[i][j]表示合并区间[i, j]内石子的最小代价。
  • 转移方程dp[i][j] = min(dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]),其中sum[j]-sum[i-1]为合并[i, j]的代价。
  • 初始化dp[i][i] = 0(单堆石子无需合并)。

4. 计算流程示例

  • 输入石子数量:[3, 4, 5, 6]
  • 前缀和:sum = [0, 3, 7, 12, 18]
  • 计算长度为2的区间:
    • dp[1][2] = dp[1][1] + dp[2][2] + (3+4) = 0+0+7=7
    • dp[2][3] = 0+0+9=9
    • dp[3][4] = 0+0+11=11
  • 计算长度为3的区间:
    • dp[1][3] = min(dp[1][1]+dp[2][3], dp[1][2]+dp[3][3]) + (3+4+5) = min(0+9, 7+0) + 12 = 7+12=19
  • 计算长度为4的区间:
    • dp[1][4] = min(dp[1][1]+dp[2][4], dp[1][2]+dp[3][4], dp[1][3]+dp[4][4]) + (3+4+5+6) = min(0+20, 7+11, 19+0) + 18 = 18+18=36

五、复杂度分析

  1. 时间复杂度
    • 三重循环:枚举区间长度O(n)、区间起点O(n)、分割点O(n),总时间复杂度为O(n³)
  2. 空间复杂度
    • 二维DP数组dp[i][j]O(n²)
    • 前缀和数组sum[i]O(n)
    • 总空间复杂度为O(n²)
  3. 优化方向
    • 对于某些问题,若分割点存在单调性(如石子合并问题中的最优分割点随区间增大而单调移动),可使用四边形不等式优化,将时间复杂度降为O(n²)
  4. 适用范围
    • 适用于n ≤ 200的问题,当n > 300时,O(n³)的复杂度可能导致超时(当然如果只有一组数据的场景,那么 n n n 还可以接受再大一点)。

  本文为作者(英雄哪里出来)在抖音的独家课程《英雄C++入门到精通》、《英雄C语言入门到精通》、《英雄Python入门到精通》三个课程的配套文字讲解,如需了解算法视频课程,请移步 作者本人 的抖音直播间。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值