一维DP数组需倒序遍历,因正序会重复使用本轮更新的值,使0-1背包退化为完全背包;dp[j]定义为容量不超过j时的最大价值,故初始化全为0。

为什么一维 dp 数组要倒序遍历
因为正序会重复使用刚更新的值,把 0-1 背包变成完全背包。二维转一维时,dp[j] 依赖的是上一轮的 dp[j - weight[i]],如果正序更新,j - weight[i] 可能已被本轮修改过,导致物品被多次选取。
实操建议:
- 内层循环必须从
capacity到weight[i]倒序写:for j in range(capacity, weight[i] - 1, -1) - 初始化
dp = [0] * (capacity + 1),不需要额外哨兵 - 如果误写成
range(weight[i], capacity + 1)(正序),结果会偏大,且和完全背包输出一致——这是最常见误判点
dp[j] 的定义到底是什么
它不是“容量恰好为 j 时的最大价值”,而是“容量不超过 j 时的最大价值”。这个细微差别直接影响初始化和转移逻辑:不需要强制装满,所以全初始化为 0 即可;若题目要求“恰好装满”,才需初始化为负无穷(-float('inf')),仅 dp[0] = 0。
常见错误现象:
立即学习“Python免费学习笔记(深入)”;
- 用“恰好装满”思路解普通 0-1 背包,结果全为 0 或报错
- 把
dp[j]当作“最后一件选的是什么”,试图回溯路径——一维数组不保留决策信息,回溯必须用二维或额外记录 - 混淆
dp[j]和dp[i][j]的语义,导致边界条件写错(比如多减 1 或少减 1)
空间优化后怎么处理物品顺序和重复问题
一维写法本身不关心物品顺序,但必须确保每件物品只参与一次状态更新——这靠外层遍历物品、内层倒序更新来保证。一旦嵌套关系写反(比如外层枚举容量、内层枚举物品),就彻底失去 0-1 约束。
实操建议:
- 外层一定是
for i in range(len(weights)):,内层是倒序for j in ... - 不能在循环中修改
weights或values列表,否则索引错位;想跳过某物品,用continue,别删元素 - 如果输入含 0 权重物品,倒序循环可能陷入死循环(
j不变),需提前过滤:if weight[i] == 0: continue
Python 里 range 和负索引容易踩的坑
倒序写 range(capacity, weight[i] - 1, -1) 时,终点是 weight[i] - 1,不是 weight[i];因为 range 右边界不包含。写成 range(capacity, weight[i], -1) 会漏掉 j == weight[i] 这一关键状态,导致该容量下无法放入当前物品。
另一个隐蔽问题:当 weight[i] > capacity,倒序循环直接跳过,没问题;但如果误用了 max(0, weight[i]) 之类逻辑,可能让 j 变成负数,触发 Python 列表负索引(访问末尾),造成静默错误。
建议始终加一层防护:
if weight[i] > capacity: continue- 或用
for j in range(capacity, weight[i] - 1, -1)前先判断weight[i] - 不要依赖负索引做逻辑,
dp[-1]在这里毫无意义
真正难的不是推导公式,是每次手写倒序 range 时,盯着那个 -1 多看两眼。










