BISHI137 【模板】完全背包¶
中等通过率 40.02%python3样例通过牛客 AC
讲解章节:背包问题
一句话
每种物品可取无限件,多组数据。
解题思路¶
这题考什么¶
完全背包的一维写法:容量正序遍历
正序意味着 f[c-w] 可能已经包含了本物品,于是天然允许取多件。 (对比 01 背包的倒序——一个字之差,语义完全不同。)
Python 关键(这题是本批最吃常数的一道)¶
正序循环有串行依赖,没法直接写成一次 map。解决办法是倍增(二进制拆分):
依次用 (w, v)、(2w, 2v)、(4w, 4v)... 各做**一次 01 背包**,
每轮可选可不选,组合起来正好覆盖 0 ~ 2^r-1 件——
只要 2^r·w > m,就覆盖了所有可能的件数。
每一轮都是一次 C 层的 map(max, ...),轮数只有 log2(m/w) 层。
两条必须做的剪枝(否则 T=200 × n=1000 × m=1000 = 2e8,稳挂):
- 按体积去重:同体积只留价值最大的(体积 <= m,最多 m 种);
- 去支配:按体积升序扫,只保留价值严格大于此前所有更小体积物品的。 若 w1 <= w2 且 v1 >= v2,物品 2 永远可以被物品 1 替换掉。
题目的测试点说明里「体积均小于 10」的几个点,去重后只剩 <= 9 件物品,瞬间出解。
数据规模与复杂度¶
T <= 200,n, m <= 1e3,时限「其他语言 10 秒」。 剪枝后单组约 O(Σ_w m·log(m/w)) ≈ 2.4 m^2 次 C 层元素操作。 最坏一档是随机数据(测试点 1-4):去重后仍可能剩近 1e3 种体积, 200 组累计约 5e8 次 C 层元素操作;体积集中的测试点(5-9)去重后只剩个位数件物品。 这份写法用 Python 3 提交即可通过。
两条剪枝不是锦上添花,是通过与否的分界:不去重时 200 组 × 1e3 件物品 各做约 10 轮 O(m) 的整段取 max,总量 2e9,无论怎么压常数都出不来。
坑在哪¶
- 倍增时 (kw, kv) 要同步翻倍,只翻体积不翻价值是常见笔误;
- 循环条件是
kw <= m,不是k <= m//w(后者会多做一轮无用功); - 多组数据每组都要重置 dp。
参考实现¶
[:octicons-arrow-left-16: BISHI136](BISHI136.md) [BISHI138 :octicons-arrow-right-16:](BISHI138.md)