01.26 背包的拉链坏了

讲真,我觉得我本来想把今天要写在这里的题投联考的。

但是下次联考的我想投的题已经有了,所以就这题就不投联考了!

但是我怎么感觉这题加强比我想投的题更好一些呢。可能是因为强数据更难造吧所以懒了!

1 ARC096F Sweet Alchemy (\(O(n^4)\))

\(n\le 50\) 个物品,物品重量 \(w_i\le 10^9\),物品价值 \(v_i\le n\),每个物品只能选不超过 \(D\le 10^9\) 个,总重量限制 \(\le 10^9\),最大化价值和。

首先,物品重量很大,但是价值很小。很自然地切换价值和重量。我们枚举总价值之和(\(O(n^2)\)),然后求出达到这个总价值的最小总重量。

(但是希望大家不要像我一开始做这题的时候直接上来一个二分凭空多了个 \(\log\)!)

然后注意到物品数量很少,我们很自然地想到可以做部分贪心,然后小范围 DP。

下面给出证明(前提为 \(D\ge n\))(以前从来没仔细证过,但是这种贪心不去仔细证容易出问题)

\(c_i=\frac{w_i}{v_i}\)\(c_i\) 越小,我们称其性价比越好)。考虑取 \(i,j\) 满足 \(c_i<c_j\),则有 \(w_iv_j< w_jv_i\)。于是,假如我们背包中选取的 \(j\) 的数量满足 \(j\ge n\) 且选取 \(i\) 的数量满足 \(i\le D-n\),那么我们一定可以将 \(v_i\)\(j\) 替换为 \(v_j\)\(i\),使得总价值不变,总重量变小。
不断这样调整,最终一定满足按 \(c_i\) 排序后一个前缀选取的个数全部 \(\ge D-n\),且后缀选取的个数全部 \(\le n\)

于是我们可以得到一个基于这个证明的做法:枚举这个前缀,这个前缀全体选 \(D-n\) 个,然后再对总体做一次每个数只能选 \(\le n\) 个的多重背包。

于是总复杂度 \(O(n^4)\)

2 (隐藏霓虹来源)背包计数

\(n\le 50\)\(a_i\le 500\)\(m\le 10^{18}\),问 \(\sum x_ia_i=m\) 的非负整数解组数。

考虑如下的一种有趣的办法:我们首先将 \(m\) 进行拆分,拆分成二进制位。

对于每个 \(x_i\),我们依次按数位 DP 的方式决定其每一位是 \(0\) 还是 \(1\)

考虑如下的 DP 状态:\(f(i,j,0/1)\) 表示考虑了所有 \(x_i\)\([0,i]\) 位,向 \(i+1\) 位的进位有 \(j\),得到的总和的第 \(i\) 位是 \(0/1\)

我们首先可以从 \(f(i-1,j,[2^{i-1}]m)\) 转移给 \(f(i,\lfloor{\frac{j}{2}}\rfloor,j\text{ mod }2)\)。然后我们依次考虑每一个 \(x\),进行转移。这里转移式子不是很方便用 latex 打,但是是容易的。

考虑该算法的时间复杂度:第一维 \(\le \log m\),第二维 \(\le 2\sum a_i\),转移复杂度 \(O(n)\),于是总时间复杂度 \(O(n^2a\log m)\),十分高速。

我们也可以从高位往低位 DP,不过会更加复杂一些:由于 \([0,i-1]\)\(i\) 的进位是 \(\le 2na\) 的,所以我们可以令 \(f(i,j)\) 表示考虑 \([i,\log m]\) 位,得到的和为 \(j\times 2^i\),但是有效的 \(j\) 只处于 \([\frac{m}{2^i}-2na,\frac{m}{2^i}]\)
的。复杂度还是一样的。

3 ARC096F Sweet Alchemy (\(O(n^3\log D)\))

考虑,每个物品可选 \(\le D\) 个,然后直接做背包。

具体而言,我们把 \(D\) 可以进行拆分,用多重背包二进制拆分的办法拆成 \(\le 2\log D\) 个元素。

然后我们按照上述方法(高位往低位)进行数位 DP,状态类似。

所以前面提到的方法也可以做最优化的多重背包。多重背包计数是做不了的,因为会算重。

复杂度是 \(O(n^3\log D)\)。提出这个做法(题解区)的作者表示可以与算法一的一些思想结合达到 \(O(n^3\log n)\),但是和他探讨了一下后我感觉不是很对。这里就不再多说了,如果后续能够继续优化的话就再说了吧。

其实写这篇的时候,我是兴致勃勃地觉得可以优化到 \(O(n^3\log n)\) 的,但是写着写着感觉就不大对劲了。唉,没办法啊!

posted @ 2024-01-27 20:33  LarsWerner  阅读(16)  评论(0编辑  收藏  举报