动态规划进阶(五):区间DP原理详解

目录

一、区间DP的本质与核心思想

二、区间DP的三大特征

三、区间DP的经典问题类型

四、区间DP解题四步法

1. 状态定义

2. 状态转移方程

3. 初始化

4. 遍历顺序

五、经典案例:石子合并问题

1. 问题描述

2. 输入示例

3. Java实现

4. 复杂度分析

六、优化技巧:四边形不等式

1. 核心思想

2. 优化后代码(部分)

七、其他经典问题实现

1. 最长回文子序列

2. 戳气球

八、区间DP的常见陷阱

九、LeetCode实战训练

十、总结与提升


一、区间DP的本质与核心思想

        区间动态规划(Interval DP) 是处理序列型问题的利器,其核心思想是将问题分解为连续子区间,通过合并相邻区间的解来推导更大区间的解。这类问题的典型特征是:

  • 操作对象为连续区间(如字符串、数组、序列)

  • 决策影响具有传递性(当前选择影响后续状态)

  • 状态转移依赖区间划分(将大区间拆分为小区间组合)


二、区间DP的三大特征

  1. 状态定义dp[i][j] 表示处理区间 [i,j] 的最优解

  2. 递推方向:从短区间向长区间扩展(长度优先遍历)

  3. 决策点枚举:需要枚举区间分割点 k 进行状态合并


三、区间DP的经典问题类型

问题类型典型问题状态转移关键点
合并类问题石子合并、能量项链合并代价计算与区间划分
回文类问题最长回文子序列、分割回文串首尾字符比较与区间收缩
匹配类问题括号匹配得分、正则表达式匹配匹配规则与区间配对
结构构造类问题最优二叉搜索树、矩阵链乘法分割点选择与权值计算

四、区间DP解题四步法

1. 状态定义

  • 明确语义dp[i][j] 表示区间 [i,j] 的最优解

  • 示例:石子合并问题中,dp[i][j] 表示合并第i到第j堆石子的最小代价

2. 状态转移方程

  • 枚举分割点k:将区间 [i,j] 分解为 [i,k] 和 [k+1,j]

  • 合并子结果:根据问题规则合并两个子区间的解

  • 示例dp[i][j] = min(dp[i][k] + dp[k+1][j] + cost(i,j))

3. 初始化

  • 基础情况:长度为1的区间(i=j时)

  • 示例dp[i][i] = 0(单堆石子无需合并)

4. 遍历顺序

  • 长度优先:外层循环枚举区间长度 len

  • 起点遍历:内层循环枚举区间起点 i,计算终点 j = i + len - 1


五、经典案例:石子合并问题

1. 问题描述

        有 N 堆石子排成一排,每堆石子重量为 w[i]。每次可以合并相邻两堆,合并代价为两堆重量之和。求将所有石子合并成一堆的最小总代价。

2. 输入示例

输入:w = [4, 2, 5, 3]  
输出:34  
解释:
1. 合并前两堆:4+2=6 → [6,5,3] 代价6  
2. 合并后两堆:5+3=8 → [6,8] 代价8  
3. 合并最后两堆:6+8=14 → [14] 代价14  
总代价:6 + 8 + 14 = 28(实际最优解更小)

3. Java实现

public class StoneMerge {
    public static int minCost(int[] w) {
        int n = w.length;
        int[] prefix = new int[n+1];
        for (int i = 1; i <= n; i++) {
            prefix[i] = prefix[i-1] + w[i-1]; // 前缀和预处理
        }
        
        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;
                int sum = prefix[j+1] - prefix[i]; // 当前区间总重量
                for (int k = i; k < j; k++) { // 枚举分割点
                    dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k+1][j] + sum);
                }
            }
        }
        return dp[0][n-1];
    }

    public static void main(String[] args) {
        int[] stones = {4, 2, 5, 3};
        System.out.println(minCost(stones)); // 输出34
    }
}

4. 复杂度分析

  • 时间复杂度:O(n³)

  • 空间复杂度:O(n²)


六、优化技巧:四边形不等式

1. 核心思想

通过数学证明确定最优分割点 k 的单调性,将内层循环从 O(n) 优化到 O(1)

2. 优化后代码(部分)

int[][] s = new int[n][n]; // 记录最优分割点
for (int len = 2; len <= n; len++) {
    for (int i = 0; i + len <= n; i++) {
        int j = i + len - 1;
        int left = s[i][j-1], right = s[i+1][j];
        dp[i][j] = Integer.MAX_VALUE;
        for (int k = left; k <= right; k++) {
            if (dp[i][k] + dp[k+1][j] < dp[i][j]) {
                dp[i][j] = dp[i][k] + dp[k+1][j];
                s[i][j] = k;
            }
        }
        dp[i][j] += prefix[j+1] - prefix[i];
    }
}
 

七、其他经典问题实现

1. 最长回文子序列

public int longestPalindromeSubseq(String s) {
    int n = s.length();
    int[][] dp = new int[n][n];
    for (int i = n-1; i >= 0; i--) {
        dp[i][i] = 1;
        for (int j = i+1; j < n; j++) {
            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]);
            }
        }
    }
    return dp[0][n-1];
}
 

2. 戳气球

public int maxCoins(int[] nums) {
    int n = nums.length;
    int[] arr = new int[n+2];
    System.arraycopy(nums, 0, arr, 1, n);
    arr[0] = arr[n+1] = 1;
    
    int[][] dp = new int[n+2][n+2];
    for (int len = 1; len <= n; len++) {
        for (int i = 1; i + len <= n + 1; i++) {
            int j = i + len - 1;
            for (int k = i; k <= j; k++) {
                dp[i][j] = Math.max(dp[i][j], 
                    dp[i][k-1] + arr[i-1]*arr[k]*arr[j+1] + dp[k+1][j]);
            }
        }
    }
    return dp[1][n];
}
 

八、区间DP的常见陷阱

  1. 循环顺序错误:必须先处理短区间再处理长区间

  2. 前缀和未预处理:重复计算区间和导致超时

  3. 分割点范围错误k 应满足 i ≤ k < j

  4. 状态初始化遗漏:忘记处理长度为1的基础情况


九、LeetCode实战训练

  1. 基础练习

  2. 进阶挑战


十、总结与提升

区间DP的解题套路可归纳为:

1.定义区间状态 → 2. 预处理前缀和 → 3. 枚举分割点 → 4. 合并子区间解

高阶优化方向

  • 记忆化搜索实现

  • 四边形不等式优化

  • 状态压缩(如滚动数组)

  • 问题转换(将环形问题转为线性)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值