0-1背包应根据场景选择二维dpi或一维dp[w]:小数据量用二维更清晰,内存受限时用一维但须倒序遍历;注意vector初始化大小与零值、价值溢出用long long、重量为0需预处理。

0-1 背包用 dp[i][w] 还是 dp[w]?
绝大多数初学者卡在状态定义上:二维数组看着直观,但空间浪费大;一维滚动数组效率高,却容易写错遍历方向。关键不是“哪个更好”,而是“你当前场景要不要省空间”。
常见错误现象:dp[w] 里结果比预期大——多半是内层循环正向遍历了 w,导致一个物品被重复选多次(实际成了完全背包)。
使用场景:
• 小数据量(比如 weight 总和 ≤ 1e4,物品数 ≤ 100)直接用二维 dp[i][w],逻辑清晰不易错
• 内存受限或需要优化时,改用一维 dp[w],但必须倒序遍历 w 从 W 到 weight[i]
• 不要试图在 dp[w] 版本里复原路径(选了哪些物品),它天然丢弃了阶段信息
vector<vector>></vector> 初始化大小写错会怎样?
动态规划数组初始化不对,轻则答案为 0,重则越界访问或未定义行为。尤其 C++ 默认初始化是未定义值,不是 0。
参数差异:
• dp(i+1, vector<int>(W+1, 0))</int> —— 正确:行数 = 物品数 + 1,列数 = 容量 + 1,全初始化为 0
• dp(i, vector<int>(W))</int> —— 错误:少一行、少一列,dp[0][0] 都可能越界
• dp(i+1, vector<int>(W+1))</int> —— 危险:第二维没显式初始化,元素值随机
性能影响:初始化开销很小,别为了省几毫秒跳过 0 初始化;现代编译器对 vector 的零初始化优化很好
为什么用 int 存最大价值可能溢出?
题目没说价值范围,但实际输入中单个 value[i] 可达 1e9,100 个加起来就超 int 上限(约 2e9)。运行时不会报错,但结果突然变负或离谱。
容易踩的坑:
• 把 dp 数组类型设成 int,却没检查题目约束
• 用 INT_MAX 做“无穷大”标记,但后续做加法时直接溢出
• 比较时写 if (dp[i-1][w] > dp[i-1][w-wt] + val),若右边溢出,比较失效
实操建议:
• 看清输入范围,价值总和可能到 1e5 × 1e4 = 1e9,保险起见用 long long
• “不可达”状态别用 INT_MAX,改用 -1 或单独布尔数组 valid[i][w]
• 所有涉及加法的右侧表达式,先判断左操作数是否有效再算
物品重量为 0 怎么处理?
标准教材常假设重量 > 0,但真实 OJ 题(如 LeetCode 416、494)可能出现 weight[i] == 0。这时无论容量多少,该物品都能无限次“免费放入”,逻辑立刻崩坏。
使用场景:
• 如果题目明确说“所有重量为正整数”,可跳过此检查
• 否则必须预处理:把 weight[i] == 0 的物品单独拎出来,它们的价值直接加到最终答案上(因为不占容量)
常见错误现象:
• 循环里 w - weight[i] 变成负数,导致数组越界或逻辑跳过
• dp[w] 更新时除零或死循环(虽然这里不除零,但下标计算失效)
兼容性提醒:C++ 标准不禁止 vector 下标为负,但行为未定义;务必在访问前校验 w >= weight[i],且 weight[i] 为 0 时跳过更新逻辑
立即学习“C++免费学习笔记(深入)”;
背包问题最麻烦的从来不是状态转移方程本身,而是边界条件怎么兜住、数据类型怎么选、以及——有没有人告诉你输入里藏着 weight=0 的坑。









