一维dp数组必须倒序遍历容量,因为正序会导致dp[j−w[i]]被本轮更新过,使同一物品被重复选取,违背0-1背包特性;倒序则确保使用上一轮(i−1)的状态值。

为什么 dp[j] 必须倒序遍历
0-1 背包用二维数组时状态转移是:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])。改成一维后,若正序遍历 j(从 w[i] 到 W),会导致 dp[j-w[i]] 已被本轮更新过——相当于把同一件物品用了多次,退化成完全背包。
倒序遍历(从 W 到 w[i])能保证每次用的都是上一轮(i-1)的值,因为 j-w[i] ,而更大的 <code>j 还没更新。
常见错误现象:dp[W] 值明显偏大,或结果与二维版本不一致,大概率是这里遍历方向错了。
vector<int> dp(W+1)</int> 初始化细节
初始化为全 0 适用于“恰好装满”和“不超过容量”两种场景,但语义不同:
- 若求“最大价值(不要求装满)”,
dp[0..W] = 0正确; - 若求“恰好装满时的最大价值”,应将
dp[0] = 0,其余设为负无穷(如INT_MIN),否则可能用未定义状态参与转移。
注意:C++ 中 vector<int> dp(W+1, 0)</int> 是安全的,但若用 INT_MIN 初始化,需确保后续加法不溢出,尤其当 v[i] 为负时。
物品循环顺序不能颠倒
外层必须是物品循环(i 从 0 到 n-1),内层是容量倒序循环(j 从 W 到 w[i])。如果反过来,就失去了“每件物品只选一次”的约束。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
典型误写:for (int j = W; j >= 0; --j) for (int i = 0; i —— 这会让所有物品对每个 <code>j 都尝试更新一次,逻辑混乱,结果不可预测。
性能影响:一维滚动后空间从 O(nW) 降到 O(W),但时间仍是 O(nW),没有节省计算量,只是省内存。
边界检查和索引越界怎么防
倒序循环里,j 的下界是 w[i],不是 0。如果写成 for (int j = W; j >= 0; --j),遇到 j 时访问 <code>dp[j - w[i]] 就会越界。
正确写法必须带条件判断或调整范围:
-
for (int j = W; j >= w[i]; --j)—— 推荐,简洁且安全; - 或在循环体内加
if (j >= w[i]) dp[j] = max(dp[j], dp[j - w[i]] + v[i]);; - 别忘了
w[i]和v[i]下标要和物品数组一致,常见坑是数组从1开始存,但循环从0开始用。
调试时可临时加 assert(j >= w[i]) 或打印越界值,这类错误往往导致随机崩溃或静默错误。
真正容易被忽略的是:当 w[i] > W 时,整个内层循环直接跳过,这没问题;但如果你手动写了 j >= 0 却没判 w[i],就会崩。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










