区间DP
一、区间DP基本概念
区间DP是一种动态规划策略,用于解决具有区间性质的问题。通常定义dp[i][j]表示处理区间[i,j]的最优解,通过合并子区间的解来推导更大区间的解。
二、普通区间DP模板(Java)
1. 通用模板结构
int n = arr.length;
int[][] dp = new int[n][n];
// 初始化长度为1的区间
for (int i = 0; i < n; i++) {
dp[i][i] = 初始值;
}
// 枚举区间长度(从2开始)
for (int len = 2; len <= n; len++) {
// 枚举起点i
for (int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1; // 终点j
// 初始化分割前的状态
dp[i][j] = 初始极大值或极小值;
// 枚举分割点k
for (int k = i; k < j; k++) {
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k+1][j] + 合并代价);
// 或Math.max()根据问题类型
}
}
}
return dp[0][n-1];
2. 经典示例:石子合并
问题描述:将N堆石子合并为一堆,每次合并相邻两堆,代价为两堆石子之和,求最小总代价。
public int stoneMerge(int[] stones) {
int n = stones.length;
int[] prefix = new int[n+1];
for (int i = 0; i < n; i++) {
prefix[i+1] = prefix[i] + stones[i];
}
int[][] dp = new int[n][n];
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len <= n; i++) {
int j = i + len - 1;
dp[i][j] = Integer.MAX_VALUE;
for (int k = i; k < j; k++) {
int cost = dp[i][k] + dp[k+1][j] + prefix[j+1] - prefix[i];
dp[i][j] = Math.min(dp[i][j], cost);
}
}
}
return dp[0][n-1];
}
三、环形区间DP模板
1. 处理环形问题的技巧
将数组复制一份接在原数组后,形成2n长度的新数组,在新数组上进行线性区间DP计算,最终结果在dp[i][i+n-1]中取最优。
2. 环形示例:环形石子合并
public int circularStoneMerge(int[] stones) {
int n = stones.length;
int[] arr = new int[2*n];
for (int i = 0; i < 2*n; i++) {
arr[i] = stones[i % n];
}
int[][] dp = new int[2*n][2*n];
int[] prefix = new int[2*n+1];
for (int i = 0; i < 2*n; i++) {
prefix[i+1] = prefix[i] + arr[i];
}
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len <= 2*n; i++) {
int j = i + len - 1;
dp[i][j] = Integer.MAX_VALUE;
for (int k = i; k < j; k++) {
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k+1][j] + prefix[j+1] - prefix[i]);
}
}
}
int res = Integer.MAX_VALUE;
for (int i = 0; i < n; i++) {
res = Math.min(res, dp[i][i + n -1]);
}
return res;
}
四、其他经典问题示例
1. 最长回文子序列(普通区间DP)
状态转移方程:
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i+1][j-1] + 2;
} else {
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
2. 能量项链(环形DP)
将环形问题转化为链式处理,状态转移方程:
dp[i][j] = max(dp[i][k] + dp[k][j] + head[i]*head[k]*tail[j])
五、总结与注意事项
-
三层循环结构:
- 外层循环枚举区间长度
- 中层循环枚举区间起点
- 内层循环枚举分割点
-
环形问题处理:
- 数组长度扩展为
2n - 最终结果在
[0, n-1]到[n, 2n-1]区间中寻找
- 数组长度扩展为
-
常见优化技巧:
- 前缀和预处理(区间求和问题)
- 记忆化搜索实现(递归式区间DP)
-
时间复杂度:
- 普通区间DP:O(n³)
- 可通过四边形不等式优化到O(n²)(高级技巧)

浙公网安备 33010602011771号