OI 中的背包问题
2025.3.18 🍏讲课记录
01 背包
无价值
给定 \(n\) 个物品,每个物品的体积为 \(c_i\),求能否选出一些物品,使得体积和为 \(m\)。
设 \(c = \max c_i\)。
- bitset 优化普通背包,时间复杂度为 \(\mathcal{O}(\frac{nm}\omega)\)
- 分治 FFT,即将每个物品看成一个多项式 \(x^{c_i}+1\),然后全部卷起来,时间复杂度 \(\mathcal{O}(nc\log^2 n)\)。
以上两个做法都可以求出所有 \(m\in [0,\sum c_i]\) 时的答案。然后下面是一个经典做法:
我们先从前往后加入物品,在满足体积和 \(\le m\) 的情况下一直加,假设加了前 \(p\) 个物品,则 \(\sum\limits_{i=1}^p c_i\in [m-c,m]\)。(特判掉所有物品体积和都不超过 \(m\) 的情况)
那么对于最终的答案,一定是在现在的方案上删除一些物品,加入一些物品。那么一定存在一种加删的方式,使得物品的体积和一直保持在 \([m-c,m+c]\) 中。因为可以考虑在当前体积和 \(\le m\) 时就加入物品,否则就删除。
我们设 \(f_{i,j,x}\) 表示考虑了前 \(i\) 个物品,只能删除编号 \(>j\) 的物品(即编号 \(\in[j+1,p]\),也相当于不动前 \(j\) 个物品),能否凑出来体积和为 \(x\),其中 \(x\in[m-c,m+c]\)。我们发现 \(j\) 更小限制一定是更宽的,所以对于特定的 \(i,x\),\(f_{i,j,x}\) 一定是一段前缀为 \(1\),后缀为 \(0\),那么可以记录这个分界点,设 \(g_{i,x}\) 表示考虑了前 \(i\) 个物品,凑出体积和为 \(x\) 的所有方案中,最多能不动前多少个物品。那么有转移:
- 不选当前的物品:\(g_{i,x}\larr g_{i-1,x}\)
- 加入当前物品,显然 \(j\) 不变:\(g_{i,x+c_i}\larr g_{i,x}\)
- 删除前 \(j\) 个物品中的一个,\(\forall j\in [1,g_{i,x}],g_{i,x-c_j}\larr j-1\)
那么最后答案就是 \(g_{n,m}\) 是否非空。但是这样子转移的时间复杂度为 \(\mathcal{O}(n^2c)\) 的,没有优化。
我们发现,每次枚举的 \(j\) 不用这么多,因为 \(g_{i-1,x}\le g_{i,x}\),而在第 \(i\) 位枚举的 \(j\in[1,g_{i,x}]\) 中,所有 \(j\in[1,g_{i-1,x}]\) 已经在 \(i-1\) 中枚举过了,因此只需要枚举 \(j\in (g_{i-1,x},g_{i,x}]\) 中的值来做转移即可。那么对于同一个 \(x\),枚举量只有 \(\mathcal{O}(n)\),总复杂度为 \(\mathcal{O}(nc)\)。
这个做法还能求出体积和不超过 \(m\) 的情况下,体积和最大是多少。
例题:Subset Sum - Problem
有价值
给定 \(n\) 个物品,第 \(i\) 个物品体积为 \(w_i\),价值为 \(v_i\),你需要选出一些物品,体积和不超过 \(m\),求最大价值和。
设 \(w = \max w_i,v = \max v_i\)。首先有复杂度为 \(\mathcal{O}(nm)\) 的暴力。
做法一
跟无价值的做法类似,先找到一个断点 \(p\),使得 \(\sum\limits_{i=1}^p w_i\in [m-w,m]\),然后直接选前 \(p\) 个物品,并将这 \(p\) 个物品的体积和价值取反,这样还是相当于 \(n\) 个物品的背包。根据经典结论,任意一个随机的和为 \(0\) 的由 \(1,-1\) 构成的序列中,前缀最大值期望为 \(\mathcal{O}(\sqrt n)\)。
于是我们先将 \(n\) 个物品随机打乱,做背包的时候只需记录体积和在 \([m-\sqrt nw,m+\sqrt nw]\) 的状态即可。为了提高正确率可以适当扩大点区间。时间复杂度为 \(\mathcal{O}(n\sqrt nw)\)。
似乎第一步选物品时先按性价比排序再选,那么前缀最大值最多为 \(\mathcal{O}(w\sqrt w)\)。(尚未证明,但这样做正确率应该会高点)
做法二
将同一体积的物品一起加入,设 \(f_{i,x}\) 表示加入了体积为 \([1,i]\) 的物品,体积和 \(\le x\) 的最大价值。
如果当前体积 \(i\) 选了 \(j\) 个,那么一定选的是体积为 \(i\) 的物品中价值最大的 \(j\) 个,假设价值和为 \(g_{i,j}\),那么转移为:\(f_{i,x+ik}\larr f_{i-1,x}+g_{i,j}\)。
我们将 \(f_{i-1,x}\) 按 \(x\bmod i\) 分类,每一类内部是做一个 \(\max+\) 卷积,因为 \(g_i\) 是凸的,所以有决策单调性,那么每次做是 \(\mathcal{O}(m\log m)\),总时间复杂度为 \(\mathcal{O}(wm\log m)\)。但是这个做法非常慢,基本被第一个做法偏序。
完全背包
无价值
给定 \(n\) 类物品,第 \(i\) 类物品体积为 \(c_i\),有无限个,求是否能选出一些物品,使得体积和为 \(m\)。
设 \(c=\max c_i,g = \gcd(c_i)\),如果 \(g\nmid m\),那么肯定不行。然后可以发现,如果 \(m\) 足够大,那么肯定可以凑出。根据裴蜀定理,这个界为 \(\mathcal{O}(w^2)\)。那么当 \(m\) 不够大时,有以下做法。另 \(z=\min c_i\),设 \(f_{i,x}\) 表示考虑了前 \(i\) 个物品,\(\bmod z = x\) 的体积中能凑出来的最小的体积和是多少。因为有无限个体积为 \(z\) 的物品,所以所有 \(\ge f_{i,x}\) 且 \(\bmod z = x\) 的体积和都可以凑出来。
初始化为 \(f_{0,0} = 0\),当加入第 \(i\) 个物品时,有转移 \(f_{i,(x+c_i)\bmod z}\larr f_{i-1,x}+c_i\),这个时候可以看成一张 \(z\) 个点的图,然后从 \(0\) 开始跑最短路。或者使用转圈:考虑在 \(f_{i-1}\rarr f_{i}\) 的转移时,按照 \(\bmod \gcd(c_i,z)\) 分类,每一类的转移是一个环,那么从一个点开始转两圈更新即可。容易发现这一定会覆盖到所有转移。
继续分析上界,你会发现这张图一共有 \(z\) 个点,每条边权长度 \(\le c\),于是到每个点的最短路 \(\le zc\),于是有上界 \(zc\)。
有价值
给定 \(n\) 类物品,第 \(i\) 类物品体积为 \(w_i\),价值为 \(v_i\),有无限个,你需要选出一些物品,体积和不超过 \(m\),求最大价值和。
咕咕咕