像潮落潮涌,送我奔向自由。|

寂静的海底

园龄:3年2个月粉丝:58关注:15

子集和加总问题(从洛谷博客同步)

给出 {a1n},找出一个子集和为 0

这是 NPC 的,当 |ai|n 的时候可以 n3 背包,当然地可以使用 bitset 压位至 n3w

值域还是太难受了,考虑怎么压下来值域,因为和为 0,值域又是 n,通过调整顺序总是存在一种方案使得值域在 [n,n]。但是我们不知道这个顺序,但感觉存太大值域又没用。

所以随机一个顺序,可以证明这个时候值域期望是不大于 O(n1.5) 的,复杂度可以做到 O(n2.5w)

还能再给力一点吗?

时间复杂度 O(nA),空间线性的 01 背包可行性及方案构造(Pisinger, 1999)

因为在很多情况下值域开大到 nA 是很浪费的,中途偏差其实和 s 差距不会太大——也总是存在一种顺序使得中途偏差不超过 A,前面考虑找到这样的顺序,但是发现不太现实,所以现在来考虑另一种:边加边删。

我们先随便找到一个和与 s 差不超过 A 的集合,然后接下来 加入其它数或者删除已经加入的数,那么这样我们考虑的值域就是 O(A) 的了。

但是删除确实是一件很困难的事情,并不是所有数都能删,我们不能删除之前没加进来的数,我们也不能记录目前集合。

但是如果只在 dp 里面放一个可行性,那属实是有点浪费我们这么小的值域,而且如果要把可删除的信息放在状态里,也没法压缩复杂度。

下面是 Pisinger 最天才的想法:

于是我们来考虑记录可以删除的前缀。

fi,j 表示前 i 个数,和 j 的最大可删除前缀(保证是满的)的长度!

(若和无法为 j 则设为 1)

设初始集合为 a1ax

初始值为

fx,ak=x

考虑转移:

加入一个数

fi,j=max{fi1,j,fi1,jai}

这很好理解——加入一个数就是普通的背包,求 max 形式。

fi,jak=max{fi,jak,k1} (1kfi,j)

循环顺序为从大到小做(删了这个数还可能删下一个数)。

虽然有些抽象,但是还是比较好理解:枚举一个可以删去的数字——然后删掉,转移。

但是这样还是 O(n2A) 的啊——确实,因为要删很多不同的数。

所以我们给 k 设置下界 fi1,j<kfi,j

因为之前能删除的已经考虑过了——删除某些数后转移到了另一个值,所以我们只用考虑之前没考虑过的值即可了!因为 f 值对于每一个固定的和都是不减的,所以复杂度为 O(nA)

删前缀为什么对?

考虑最终状态下的后半部分选入的集合一定被 dp 决策考虑了,而前面每步一定删除到了能删除的最大位置(这个前缀一定能被拆成若干段删除或不删除的区间),如果存在合法方案,一定会被这个过程包含在内。

想看正确性严谨的证明可以去翻论文。

代码并不复杂,即使需要构造方案也可以在 20 行之内完成,在 OI 中可能有用。


参考

posted @   寂静的海底  阅读(34)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起