应使用一维滚动数组,因二维易内存超限且Go中切片扩容与GC压力大;倒序遍历重量是为避免误用本轮更新值,确保01背包语义正确。

01 背包:用 dp[i][w] 还是滚动数组?
直接上二维数组容易内存超限,尤其当物品数多、背包容量大时。Golang 中切片扩容和 GC 压力会明显拖慢速度,所以生产环境几乎都用一维滚动数组。
关键不是“能不能写对”,而是“为什么必须倒序遍历重量”——因为 dp[w] 依赖的是上一轮(i−1)的状态,如果正序更新,dp[w−weight[i]] 可能已被本轮改过,相当于误用了完全背包逻辑。
- 初始化
dp为长度capacity + 1的切片,全填0 - 外层遍历物品,内层从
w = capacity到weight[i]倒序更新:dp[w] = max(dp[w], dp[w-weight[i]] + value[i]) - 别用
make([]int, 0, cap+1)初始化,要make([]int, cap+1),否则索引越界 panic
完全背包:正序遍历就对了,但得防重复计数
和 01 背包只差一个遍历方向,但语义完全不同:正序意味着每个物品可多次选,dp[w−weight[i]] 是本轮已更新过的值,天然支持复用。
不过要注意,如果你的需求其实是「每种物品最多用 k 次」(多重背包),不能直接套完全背包模板,否则会高估解。Golang 里没有内置的多重背包快捷写法,得手动拆分或用单调队列优化。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 内层循环从
w = weight[i]到capacity正序遍历 - 状态转移仍是
dp[w] = max(dp[w], dp[w-weight[i]] + value[i]) - 如果输入含负权重或负价值,先检查并提前 return,Golang 不会自动跳过非法状态
max() 函数得自己写,别依赖 math.Max
Golang 标准库的 math.Max 只接受 float64,而背包问题几乎全是整型。硬转类型不仅慢,还可能因精度丢值(比如大整数转 float64 后变 0)。
更麻烦的是,有些同学用 if a > b { a } else { b } 写在循环里,编译器未必能内联,小数据量看不出,但万级物品时性能差距能到 15% 以上。
- 定义一次
func max(a, b int) int { if a > b { return a }; return b },放包顶层 - 别用
...int可变参数封装,调用开销大 - 如果 value 是
int64,就另写一个max64,混用类型会触发隐式转换,Go 编译器不报错但结果不可靠
边界 case 容易崩:容量为 0 或物品为空怎么办
很多实现看到 capacity == 0 就直接 return 0,看起来合理,但漏掉了「物品价值全为负」的场景——此时最优解可能是不装任何东西,即 0;但如果题目允许负价值且要求必须装满,则应初始化为 -inf 并特判。
更隐蔽的是空切片输入:weights = []int{} 或 values = nil,Golang 不会 panic,但 len(weights) 是 0,外层 for 直接跳过,返回全零 dp,看似正常,实则掩盖了输入校验缺失。
- 函数开头加
if len(weights) != len(values) || capacity ,直接 panic 或 return error - 若题目明确「可以不装满」,
dp初始全 0 即可;若「必须装满」,除dp[0] = 0外其余设为math.MinInt64 - 别用
dp := make([]int, capacity),少了个 +1,dp[capacity]会越界

















