必须倒序遍历重量以确保dp[w]依赖上一轮状态,避免误成完全背包;初始化dp:=make([]int,capacity+1),空输入需提前判;max用内联函数而非math.max或可变参数。

直接用一维滚动数组 + 倒序遍历,别碰二维切片——Go 的切片扩容和 GC 会在大容量背包场景下明显拖慢速度,甚至触发 OOM。
为什么必须倒序遍历重量(01 背包)
因为 dp[w] 依赖的是上一轮(i−1)的状态。如果正序更新,dp[w-weight[i]] 可能已被本轮改过,相当于把“选多次”逻辑误塞进 01 背包里。
- 错误写法:
for w := weight[i]; w → 实际跑成完全背包 - 正确写法:
for w := capacity; w >= weight[i]; w-- - 边界必须卡死:
w >= weight[i],否则dp[w-weight[i]]下标越界
dp 切片怎么初始化才不 panic
别用 make([]int, 0, cap+1) —— 这创建的是长度为 0 的切片,dp[w] 直接越界 panic。也不要用 append 动态加,性能差且不可控。
- 正确初始化:
dp := make([]int, capacity+1)(全 0) - 如果题目要求“必须装满”,则初始化为
math.MinInt64,但注意 Go 没有内置math.MinInt64,得手写或用^uint64(0) >> 1转 int64 再转 int(小容量用-1e9更稳妥) - 空输入要提前判:
if len(weights) == 0 || len(values) == 0,否则weights[0]panic
如何避免 max() 成性能瓶颈
Go 标准库 math.Max 只收 float64,强转不仅慢,大整数(如 > 2⁵³)会丢精度。循环里写 if a > b { a } else { b } 编译器未必内联,万级物品时慢 15%+。
- 定义一次顶层函数:
func max(a, b int) int { if a > b { return a }; return b } - 别封装成
max(...int)可变参数——调用开销翻倍 - 若
value是int64,必须另写max64,混用类型会导致静默溢出
完全背包和多重背包的临界区别
完全背包只差一个遍历方向,但语义天差地别;而多重背包(每物最多 k 次)不能硬套完全背包模板,否则解会被高估。
- 完全背包:内层循环
for w := weight[i]; w ,正序 - 多重背包:要么拆成多个 01 背包(适合 k 小),要么用单调队列优化(k 大时必需)——Go 无现成库,得手撸
- 负权重/负价值必须提前检查:
if weight[i] ,否则状态转移失效
真正容易崩的不是逻辑,是边界:容量为 0 时该返回 0 还是 -inf、空切片是否 nil、value 类型和 dp 类型是否严格一致——这些地方不打日志,线上跑一天都看不出哪一行炸了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











