文章目录
一、算法概述
- 定义:区间动态规划(Interval Dynamic Programming)是一种处理区间上最优解问题的算法,通过将问题分解为小区间的子问题,逐步合并得到大区间的解。其核心思想是从长度最短的区间开始计算,逐步扩展到整个区间,避免重复计算。
- 核心特征:
- 状态定义为区间
[i, j]的最优值,如dp[i][j]。 - 状态转移通过枚举区间内的分割点,将大区间拆分为两个小区间的组合。
- 状态定义为区间
- 应用场景:
- 石子合并问题(求合并石子的最小/最大代价)
- 矩阵连乘问题(求最小乘法次数)
- 区间表达式求值(添加运算符使表达式结果最优)
二、算法思路
- 状态定义:
dp[i][j]表示区间[i, j]内的最优值(如最小代价、最大价值等)。
- 状态转移方程:
- 对于区间
[i, j],枚举分割点k(i ≤ 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)为合并区间的代价。
- 对于区间
- 初始化:
- 长度为1的区间
[i, i]的最优值通常为初始值(如0或元素本身)。
- 长度为1的区间
- 遍历顺序:
- 先枚举区间长度(从小到大),再枚举区间起点,最后枚举分割点。
三、伪代码实现
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=7dp[2][3] = 0+0+9=9dp[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
五、复杂度分析
- 时间复杂度:
- 三重循环:枚举区间长度
O(n)、区间起点O(n)、分割点O(n),总时间复杂度为O(n³)。
- 三重循环:枚举区间长度
- 空间复杂度:
- 二维DP数组
dp[i][j]:O(n²)。 - 前缀和数组
sum[i]:O(n)。 - 总空间复杂度为
O(n²)。
- 二维DP数组
- 优化方向:
- 对于某些问题,若分割点存在单调性(如石子合并问题中的最优分割点随区间增大而单调移动),可使用四边形不等式优化,将时间复杂度降为
O(n²)。
- 对于某些问题,若分割点存在单调性(如石子合并问题中的最优分割点随区间增大而单调移动),可使用四边形不等式优化,将时间复杂度降为
- 适用范围:
- 适用于
n ≤ 200的问题,当n > 300时,O(n³)的复杂度可能导致超时(当然如果只有一组数据的场景,那么 n n n 还可以接受再大一点)。
- 适用于
本文为作者(英雄哪里出来)在抖音的独家课程《英雄C++入门到精通》、《英雄C语言入门到精通》、《英雄Python入门到精通》三个课程的配套文字讲解,如需了解算法视频课程,请移步 作者本人 的抖音直播间。
1270

被折叠的 条评论
为什么被折叠?



