Leetcode 动态规划 模板

写这类题型时候,要跟着下面步骤:

  1. 定义状态 :dp[i](或 dp[i][j])表示什么?------这是最关键的一步,定义错了后面全错。

  2. 转移方程 :dp[i] 由哪些更小的状态推来?(选 / 不选、从哪一步转移)

  3. 初始化 :边界 dp[0] 是多少?非法状态填什么(求最大填 -INF,求方案数填 0)?

  4. 遍历顺序:先算的必须先于依赖它的状态(决定正序还是倒序)。

要点:状态定义是 DP 的灵魂。90% 的 DP bug 来自「状态含义模糊」

java 复制代码
线性DP
// dp[i]:偷到第 i 间房的最大金额。选 i 就不能选 i-1
int rob(int[] nums) {
    int n = nums.length;
    // 滚动变量代替数组:f0=dp[i-2], f1=dp[i-1]
    int f0 = 0, f1 = 0;
    for (int x : nums) {
        int cur = Math.max(f1, f0 + x);  // 不偷 f1 / 偷 f0+x
        f0 = f1; f1 = cur;
    }
    return f1;
}

0/1 背包
// dp[j]:容量 j 下的最优值。物品遍历在外,容量倒序在内
boolean canPartition(int[] nums) {
    int sum = 0; for (int x : nums) sum += x;
    if (sum % 2 != 0) return false;
    int target = sum / 2;
    boolean[] dp = new boolean[target + 1];
    dp[0] = true;
    for (int x : nums)                       // 每个物品
        for (int j = target; j >= x; j--)    // 容量【倒序】:保证每件只用一次
            dp[j] = dp[j] || dp[j - x];
    return dp[target];
}

完全背包
// 
int coinChange(int[] coins, int amount) {
    int[] dp = new int[amount + 1];
    Arrays.fill(dp, amount + 1);   // 用 amount+1 当 INF
    dp[0] = 0;
    for (int c : coins)                    // 每个硬币
        for (int j = c; j <= amount; j++)  // 容量【正序】:允许重复使用
            dp[j] = Math.min(dp[j], dp[j - c] + 1);
    return dp[amount] > amount ? -1 : dp[amount];
}

最长递增子序列 LIS
// O(n^2) 版本:dp[i]=以 i 结尾的 LIS 长度
int lengthOfLIS(int[] nums) {
    int n = nums.length, ans = 1;
    int[] dp = new int[n];
    Arrays.fill(dp, 1);
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < i; j++)
            if (nums[j] < nums[i]) dp[i] = Math.max(dp[i], dp[j] + 1);
        ans = Math.max(ans, dp[i]);
    }
    return ans;
}

状态机 DP
// 通用:dp[i][持有/不持有],每天在两个状态间转移
int maxProfit(int[] prices) {   
    int hold = Integer.MIN_VALUE, free = 0;  // 持有 / 不持有 的最大利润
    for (int p : prices) {
        free = Math.max(free, hold + p);   // 今天卖出
        hold = Math.max(hold, free - p);   // 今天买入
    }
    return free;   // 最后一定不持有
}


区间 DP
// dp[i][j]:区间 [i,j] 内的最优解。按区间长度从小到大枚举
int minCost / longestPalindromeSubseq(String s) {
    int n = s.length();
    int[][] dp = new int[n][n];
    for (int i = 0; i < n; i++) dp[i][i] = 1;   // 长度 1 的区间
    for (int len = 2; len <= n; len++)          // 枚举区间长度
        for (int i = 0; i + len - 1 < n; i++) {
            int j = i + len - 1;                 // 区间右端
            // 转移:由更短的子区间 [i+1][j] / [i][j-1] / [i+1][j-1] 推来
            dp[i][j] = s.charAt(i) == s.charAt(j)
                    ? dp[i+1][j-1] + 2
                    : Math.max(dp[i+1][j], dp[i][j-1]);
        }
    return dp[0][n-1];
}
相关推荐
Wang's Blog1 小时前
Java 项目实战: 交友项目 - JWT 概述:为什么要用 Token 换掉 Session
java·ui·交友
SL_staff2 小时前
低代码选型避坑指南:为什么‘能拖拽’不等于‘能交付’?——基于JVS八大引擎的四维交付能力评估框架
java·低代码·spring cloud
All for pursuit.2 小时前
【位运算-3】136.只出现一次的数字
数据结构·c++·算法·leetcode
MoRanzhi12032 小时前
106.正则问题(分治)
python·算法·正则表达式·蓝桥杯·递归·分治算法·字符串处理
李福春2 小时前
技术思考问题6:技术演讲该晒成功架构,还是交代失败与取舍?
java·开发语言·架构·computer use·深圳同盟·腾讯云架构师技术同盟·反演思维
suhan2 小时前
【数据结构】五、树:7.哈夫曼树、哈夫曼编码
数据结构·算法·排序算法
用户7771298509122 小时前
equals和==的区别
java