动态规划是解决01背包问题最可靠的方法,通过清晰的状态定义(一维dp[j]或二维dpi)、正确的状态转移方程(拿或不拿的最大值)、倒序遍历(一维)或正序遍历(二维)、以及自然的边界初始化(容量或物品为0时价值为0)来确保正确性。

动态规划是解决01背包问题最可靠的方法,它不靠猜测、不靠贪心,而是通过系统填表,把每个子问题的最优解稳稳存下来,最终推出全局最优解。关键不在代码多复杂,而在状态定义是否清晰、转移逻辑是否闭环、遍历顺序是否正确。
明确 dp 数组的实际含义
这是动规第一步,也是最容易出错的地方。对01背包来说,推荐两种常用定义方式:
- 一维数组 dp[j]:表示容量为 j 的背包能装下的最大价值。简洁高效,空间复杂度 O(W)。
- 二维数组 dp[i][j]:表示前 i 个物品、在容量不超过 j 的前提下能获得的最大价值。语义更直观,适合初学理解,空间复杂度 O(n×W)。
无论选哪种,dp 的下标必须和现实意义严格对应——比如 dp[5] 就是“背包刚好/最多装5单位时的价值”,不能模糊成“大概能装5左右”。
写出准确的状态转移方程
核心逻辑就一句话:面对第 i 个物品,你只有两个选择——拿,或者不拿。
- 不拿:价值继承上一轮,即 dp[i-1][j] 或 dp[j](保持不变);
- 拿(前提是 w[i] ≤ j):腾出 w[i] 容量,加上当前物品价值,即 dp[i-1][j−w[i]] + v[i] 或 dp[j−w[i]] + v[i];
- 最终取两者较大值:dp[i][j] = max(dp[i−1][j], dp[i−1][j−w[i]] + v[i])。
注意:这个“拿”的前提是容量够——如果 w[i] > j,那就只能不拿,直接复制上一行值。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
控制好遍历顺序与方向
顺序错了,结果就全错。关键有两点:
- 外层遍历物品:从第 1 个到第 n 个,确保每个物品只考虑一次;
- 内层遍历容量要倒序(仅限一维优化):从 W 到 w[i],防止同一个物品被重复使用(这是一维写法避免“完全背包”错误的关键);
- 二维写法则正序即可,因为 dp[i][j] 只依赖上一行数据,天然隔离。
举例:重量数组为 [2,3,4],背包容量 5。若内层正序遍历 j=2→5,当处理完 j=2 后,j=4 会误用刚更新的 dp[2],导致物品被多次选取。
初始化与边界处理要自然
不需要强行“设初值”,而是让定义本身决定边界:
- 容量为 0 时,无论多少物品,最大价值都是 0 → dp[0] = 0(一维)或 dp[i][0] = 0(二维);
- 没有物品时,任何容量下价值也为 0 → dp[0][j] = 0(二维),一维中这一行天然隐含;
- 实际编码中,int[] dp = new int[W+1] 默认全为 0,已满足要求,无需额外循环赋 0。
真正要检查的是每次访问 dp[j−w[i]] 前是否越界——加个 if (j >= w[i]) 判断,比 try-catch 更干净利落。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










