特殊的背包问题
设 \(n\) 为物品个数,\(m\) 为背包容量,\(k\) 为所有物品中重量最大值。
1. 最优化问题
当 \(m\) 较大时可以应用下面的解法。先贪心,然后扔掉一些,再补上一些。
1.1 完全背包 1
先找出性价比最高的物品,一直填这个物品,直到将要溢出。设这个物品的重量为 \(x\)。
其他物品最多选择 \(x-1\) 个。若选择了 \(x\) 个,根据鸽巢原理,一定有一段的重量和是 \(x\) 的倍数,否则换成性价比最高的物品一定不劣,于是背包容量只需要开到 \(k^2\)。
而重量相同的物品只需要保留价值最大的,所以复杂度 \(O(n+k^3)\)。
1.2 完全背包 2
当保证 \(m>k^2\) 时,有 \(O(nk)\) 的同余最短路做法。
P9140 [THUPC 2023 初赛] 背包 做题记录 - XP3301_Pipi - 博客园 (cnblogs.com)
1.3 完全背包 3
对于一个物品集合,一定存在一种划分方式,使得两部分的重量和之差 \(\le k\)。
所以,我们可以将 \(m\) 分解为 \((\frac m 2-i)+(\frac m 2+i)\),其中 \(0\le i\le \frac k 2\),分成两个独立子问题。
区间初始为 \([m,m]\),变为 \([\frac{m-k} 2,\frac{m+k} 2]\),再变为 \([\frac{m-3k} 4,\frac{m+3k}4]\),一直向下。
考虑 \(k\) 前系数,\(f_0=0,f_i=\frac{f_{i-1}+1}{2}\),不难得到 \(f_i=1-2^{-i}\)。所以区间长度不超过 \(2k\)。
向下递归,直到 \(m\le 2k\),这部分预处理即可。复杂度 \(O(nk+k^2\log m)\)。
1.4 多重背包
将所有物品按性价比从大到小排序,先贪心从前向后选择,直到将要溢出。
将重量为相同的物品分成一类,若该类物品已经被选择 \(p\) 个,那么最终至多扔掉 \(k-1\) 个。
设重量为 \(x\) 的物品被扔掉了 \(k\) 个。那么在后面补上来的物品中,价值和与重量和一定比扔掉的这些大,否则与贪心过程矛盾,于是补上来的物品至少有 \(x\) 个。在这 \(x\) 个物品中,一定有一段的重量和是 \(x\) 的倍数,把这段物品换成被扔掉的一定不劣。于是背包容量只需要开到 \(k^3\)。
每类物品剩下的部分,只需要保留 \(\frac {k^3} p\) 个价值最大的物品。复杂度 \(O(n\log n+k^6\ln k)\)。
2. 子集和问题
只需要知道是否存在一个子集使得重量和为 \(m\),无需考虑价值和。
2.1 01 背包 1
朴素 \(O(nm)\),bitset 优化到 \(O(nm/w)\)。
2.2 多重背包 1
再设一个 \(g_j\) 表示拼出重量和为 \(j\) 的物品最少需要多少第 \(i\) 类物品,当 \(g_j<c_i\) 时才能向后转移。\(O(nm)\)。
2.3 01 背包 2
设 \(\sum w_i=S\)。将 \(w_i\) 相同的分为一组,最多 \(O(\sqrt{S})\) 个非空组。直接应用 2.2 可以做到 \(O(m\sqrt{S})\)。
对每个组进行二进制拆分。设每个组大小为 \(a_i\),则 \(a_i\ge 2^w\) 的个数不会超过 \(O(\sqrt{\frac S {2^w}})\),所以总物品个数不超过 \(O(\sqrt{S})\)。应用 2.1 做到 \(O(m\sqrt{S}/w)\)。
2.4 01 背包 3
先从前向后选,直到将要溢出。设选出的物品重量和为 \(S\),则 \(S\ge m-k\)。
扔掉左边一些物品,再补上右边一些物品。那么把左边物品的重量取反,问题转化为是否有重量和为 \(m-S\) 的子集。
若存在这样一个子集,将其随机打乱后,其前缀和最大值期望 \(O(k\sqrt{n})\)。证明相当复杂,我不会
那么将数组随机打乱后,背包大小只需要 \(k\sqrt{n}\),应用 2.1 做到 \(O(nk\sqrt{n}/w)\)。
2.5 完全背包 2
选择一个物品为基准,以这个物品的重量为模数跑同余最短路,\(O(nk)\)。
还可以求出 \([1,m]\) 中有多少能被拼出来。
2.6 多重背包 2
扩展 2.5 的做法。应用转圈技巧和单调队列优化即可。
2.7 01 背包 4
先从前向后选,直到将要溢出,然后进行调整。调整过程中,可以保证重量和始终在 \([m-k,m+k]\) 内:若 \(S<m\) 则补上右边的,若 \(S<m\) 则扔掉左边的,\(S=m\) 时随意。
设 \(f_{l,r,w}\) 为考虑 \([l,r]\) 中的物品,能否调整到 \(w\),转移枚举是否选择左右端点,复杂度 \(O(n^2k)\)。
当 \(r\) 固定时,\(f_{l,r,w}=1\) 的 \(l\) 一定是一段前缀。更换状态,设 \(f_{r,w }\) 为以 \(r\) 为左端点时,使得调整到 \(w\) 可行的最大的 \(l\)。列出转移:
- \(f_{r-1,w}\rightarrow f_{r,w}\);
- \(f_{r-1,w}\rightarrow f_{r,w+a_r}\);
- \(l\rightarrow f_{r,w-a_l}\),\(l<f_{r,w}\)。
直接做仍然是 \(O(n^2k)\) 的,但第三种转移中只需要考虑 \(f_{r-1,w}\le l<f_{r,w}\) 的 \(l\),因为其他 \(l\) 在 \(f_{r-1,w}\) 处讨论过了,然后经过第一种转移到了 \(f_{r,*}\)。这样就做到 \(O(nk)\) 了。
