区间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])

五、总结与注意事项

  1. 三层循环结构

    • 外层循环枚举区间长度
    • 中层循环枚举区间起点
    • 内层循环枚举分割点
  2. 环形问题处理

    • 数组长度扩展为2n
    • 最终结果在[0, n-1][n, 2n-1]区间中寻找
  3. 常见优化技巧

    • 前缀和预处理(区间求和问题)
    • 记忆化搜索实现(递归式区间DP)
  4. 时间复杂度

    • 普通区间DP:O(n³)
    • 可通过四边形不等式优化到O(n²)(高级技巧)
posted @ 2025-02-28 18:50  咋还没来  阅读(54)  评论(0)    收藏  举报