← 回到所有文章

斜率优化总结附2:WQS二分简述

简单的背包问题+结论证明 引入 考虑一个问题:一个集合中有 n 个物品,每个物品有一个价值(可能为负),请你取出 m 个物品,并且使价值之和最大。这种问题一般人选择直接排序,但是如果一个物品的价值计算…

笔记文章的简约插画封面

简单的背包问题+结论证明

引入

考虑一个问题:一个集合中有 nn 个物品,每个物品有一个价值(可能为负),请你取出 mm 个物品,并且使价值之和最大。这种问题一般人选择直接排序,但是如果一个物品的价值计算方式变化了导致不能直接排序取出,甚至没有贪心策略时,应该如何做?如果直接使用二维dp还会超时有什么办法可以快速做?

推结论

实际上这个问题就是WQS问题的典型问题,我们设函数 g⁡(x)\operatorname{g}\left(x\right) 表示取出 xx 个物品时能取到的价值最大值,将每一个 (x,g⁡(x))\left(x,\operatorname{g}\left(x\right)\right) ,在一个平面直角坐标系上面画出来,串起来。先忽略红色直线,大概是这个样子,观察可知其凸性:

a

可以想到的是对于任意一个斜率为 kk 的直线都能在上凸壳上找到一个点使得其直线截距最大。

假设现在已知一个斜率为 kk 的直线,切到了 (x,g⁡(x))\left(x,\operatorname{g}\left(x\right)\right) 这个点,并且截距最大。现在已知 xx (接下来默认 xx 时定值,用 xx 这个记号只是提醒读者有普适性),如何求解 g⁡(x)\operatorname{g}\left(x\right) 。

令 f⁡(x)=g⁡(x)−k×x\operatorname{f}\left(x\right)=\operatorname{g}\left(x\right)-k\times x ,也就是每个物品价值减去 kk 后能取到 xx 个的最大价值。实际上 f⁡(x)\operatorname{f}\left(x\right) 为在没有拿多少这个限制的时候,所有物品的价值减去 kk 后的最优解。

证明如下:由于我们 xx 是定制了,换个记号,令 h⁡(q)\operatorname{h}\left(q\right) 表示每个物品减去 kk 无限制拿 qq 个的最大价值,显然 h⁡(q)=g⁡(q)−k×q\operatorname{h}\left(q\right)=\operatorname{g}\left(q\right)-k\times q ,则有 h⁡(q)+k×q=g⁡(q)\operatorname{h}\left(q\right)+k\times q=\operatorname{g}\left(q\right),也就是 h⁡\operatorname{h} 表示用斜率为 kk 的直线去切 (q,g⁡(q))\left(q,\operatorname{g}\left(q\right)\right) 的截距。用 h⁡\operatorname{h} 去考虑一个每个物品价值减 kk 后的无限制的最优情况,可以分论讨论每一个 qq 的取值,然后找到最小的一个 h⁡\operatorname{h} 就是无限制每个物品价值减 kk 后的无限制的最优情况,但是别忘了 h⁡(q)\operatorname{h}\left(q\right) 本质是用斜率为 kk 的直线去切 (q,g⁡(q))\left(q,\operatorname{g}\left(q\right)\right) 的截距,显然对于这个凸包,切到 (x,g⁡(x))\left(x,\operatorname{g}\left(x\right)\right) 时 h⁡(q)\operatorname{h}\left(q\right) 最大,而恰好此时 h⁡(x)=f⁡(x)\operatorname{h}\left(x\right)=\operatorname{f}\left(x\right) 也就是 f⁡(x)\operatorname{f}\left(x\right) 等价于每个物品价值减 kk 后的无限制的最优情况。这个结论使得我们找到了一个可以快速计算的函数 f⁡(x)\operatorname{f}\left(x\right) ,并且可以转化到 g⁡(x)\operatorname{g}\left(x\right) 上去,从而实现快速求解。

实现

现在回到做题,我们已知了 xx ,并且可以快速计算 f⁡(x)\operatorname{f}\left(x\right) , kk 未知,目标求解 g⁡(x)\operatorname{g}\left(x\right) 。实际上我们只需要找到这个 kk 就可以了,考虑 kk 单调递增变化时,在凸包上切出来的点必定按照 XX 递增。那就好办了,直接二分 kk 直到切到的点的 XX 坐标就是 xx 即可。

实际应用中,对于小数二分常常直接限定循环次数;而二分的 check() 函数就是跑一遍无限制dp只不过转移方程需要减去一个 midmid (二分中的 midmid ) ,然后记录转移次数,并于 xx 比较,根据实际情况去二分左区间还是右区间(上凸壳/下凸壳之分)。

推广

实际上对于其他有限制选定个数的dp题目,都可以用上WQS二分,只需要保证 (打表观察) 其有凸性就可以WQS二分。

谢谢你,读到这里。

本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗

搜索文章

输入关键词,发现一些值得阅读的文字。

探索文字里的小小世界esc 关闭