C++怎么实现背包问题_C++01与完全背包【DP】

7次阅读

01背包需倒序遍历容量j以确保每物只选一次,状态转移为dp[j]=max(dp[j],dp[j−w[i]]+v[i]);完全背包则正序遍历,允许重复选取。

C++怎么实现背包问题_C++01与完全背包【DP】

01背包:状态转移写错就全错

核心是 dp[j] = max(dp[j], dp[j - w[i]] + v[i]),且必须倒序遍历容量 j。正序会重复选同一物品,变成完全背包行为。

常见错误现象:dp[10] 算出来比手算大很多,或者小数据对、大数据错——大概率是循环方向反了。

  • 倒序从 Ww[i](含),保证每个物品只用一次
  • dp 数组大小至少为 W + 1,下标 0 表示容量为 0 的最大价值
  • 初始化全为 0 即可(求最大价值,非恰好装满);若要求“恰好装满”,除 dp[0] = 0 外其余设为负无穷

完全背包:顺序遍历才是关键

状态转移式看起来一样:dp[j] = max(dp[j], dp[j - w[i]] + v[i]),但内层循环必须正序,让已更新的 dp[j - w[i]] 参与后续计算,从而允许重复使用当前物品。

使用场景:硬币找零(最少硬币数)、无限供应的物品组合优化。

立即学习C++免费学习笔记(深入)”;

  • 正序从 w[i]W,每轮都复用本物品的更新结果
  • 如果想压空间又不想混淆,建议先写二维版 dp[i][j] 理清逻辑,再滚动优化
  • 注意:和 01 背包共用同一份代码却只改循环方向,极易漏掉边界检查(比如 j - w[i] 时跳过)

初始化与边界:不是所有题都从 0 开始

很多题不满足“容量为 0 时价值为 0”这个默认假设。比如要求“总重量恰好等于 W”,那 dp[0] = 0 是对的,但 dp[1..W] 初始值不能是 0,否则会误把未达成的状态当有效解。

  • 恰好装满类问题:用 INT_MIN 或一个极小负数初始化 dp[1..W],避免无效状态参与转移
  • 最大价值类问题(不要求装满):全初始化为 0 即可
  • c++vector<int>(W + 1, 0)</int>vector<int>(W + 1, INT_MIN)</int> 差异直接影响答案正确性

空间优化后调试困难?加个打印点就行

一维 dp 数组省空间,但失去中间过程,出错时很难定位哪一轮、哪个物品搞错了。别硬扛,临时加一行输出就能省半天。

  • 在每次外层循环(即处理第 i 个物品)结束后,打印前几个 dp 值:cout
  • 对比手算的小样例(比如 2 个物品、容量 5),一眼看出哪次更新异常
  • 上线前删掉即可,不影响逻辑——这比翻十遍状态转移方程快得多

真正卡住的往往不是 DP 思路,而是初始化含义没吃透,或循环方向和依赖关系对不上。多打一行日志,比重写三遍更可靠。

text=ZqhQzanResources