看到多重背包 -> 01 背包的转化操作中,使用了二进制拆分。第一眼觉得这种方法的正确性很难判断,就想了个把小时,总算是想明白了,写篇文纪念一下。
为了防止误会,在原问题中的物品不加引号,在转换后的问题中的物品加引号
多重背包问题背景:存在容量为 的背包和 组物品,第 组物品有 个且每个价值都为 ,求背包能装下的物品价值和的最大值。
TODO: 使用二维 dp 写一下思路
可以看到,多重背包化为 01 背包最简单的方法就是把“组”的概念拆掉,把问题化为:存在容量为 的背包和 个"物品",每个"物品"等效于某组物品中的一个物品(不知道怎么严谨的表示价值来着 XD) 那这样的话,可见的是复杂度爆炸,且显然存在子问题重复(多个"物品"的价值相同)。
TODO: 使用 01 背包写一下朴素优化的代码
反过来观察上面的操作,其正确性是显而易见的:上述的拆分操作不会影响到答案。即原先需要选择某个组中的 个物品,在拆分后也会进行这样的选择(选择等效的 个"物品")。而使用某些奇怪的拆分操作,比如二分拆分什么的,很容易增加限制(比如对于某个组,拆分后的"物品"凑不出来正解中选择的物品数量),从而得不到正解。 在这里可以看出一些端倪:如果我们对任意组(假设有 个物品)进行拆分,得到的"物品"们的数量可以凑出来任意 ,那是不是不会增加限制呢?
为了更好地表达,下面使用
"物品"的数量
来表示拆分后的"物品"于原问题中的等效数量
这里就可以尝试用 CS 中常用的二进制拆分操作,即对于某组中的 个物品,我们拆成下面的"物品"集合(下面是不太严谨的表述,意会即可) 在这个集合中,只有 项是比较特殊的,为了方便,在后面称呼 ta 为 ;因为这个值的意义是某个"物品"的数量,后面会使用 "物品"来代指"物品"的数量为 的"物品"。 接下来就应该尝试证明这种拆分能表示的答案区间了。在这里进行分类讨论,分别是一定不选 "物品"的情况和一定选 "物品" 的情况。
可以发现,除去 "物品"后的数量和为 。(提一嘴没啥用的,这能很容易地证明这个集合的数量和为 。) 在理解下面这一句的时候,请放弃十进制的思考方式!使用代数的方法思考!! 在一定不选 "物品"时,根据进制转换相关的常识,剩下的"物品"的数量的子集的和 。 在一定选 物品时,同理,其子集的和 。 我们可以很显然的发现,在这里 和 可以覆盖整个区间,即 而在 01 背包问题中,选择是任意的,所以这两个子问题完全可以合并,得到完整的答案区间(和倍增 ST 表的子问题合并比较接近)。
总结一下,使用这种方法,我们可以做到:
TODO: 写一下代码
本文作者:CornWorld
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!