简单的背包问题+结论证明 引入 考虑一个问题:一个集合中有 n 个物品,每个物品有一个价值(可能为负),请你取出 m 个物品,并且使价值之和最大。这种问题一般人选择直接排序,但是如果一个物品的价值计算…
ZZi_GaoComprehension is compression.
简单的背包问题+结论证明
引入
考虑一个问题:一个集合中有 n 个物品,每个物品有一个价值(可能为负),请你取出 m 个物品,并且使价值之和最大。这种问题一般人选择直接排序,但是如果一个物品的价值计算方式变化了导致不能直接排序取出,甚至没有贪心策略时,应该如何做?如果直接使用二维dp还会超时有什么办法可以快速做?
推结论
实际上这个问题就是WQS问题的典型问题,我们设函数 g(x) 表示取出 x 个物品时能取到的价值最大值,将每一个 (x,g(x)) ,在一个平面直角坐标系上面画出来,串起来。先忽略红色直线,大概是这个样子,观察可知其凸性:
可以想到的是对于任意一个斜率为 k 的直线都能在上凸壳上找到一个点使得其直线截距最大。
假设现在已知一个斜率为 k 的直线,切到了 (x,g(x)) 这个点,并且截距最大。现在已知 x (接下来默认 x 时定值,用 x 这个记号只是提醒读者有普适性),如何求解 g(x) 。
令 f(x)=g(x)−k×x ,也就是每个物品价值减去 k 后能取到 x 个的最大价值。实际上 f(x) 为在没有拿多少这个限制的时候,所有物品的价值减去 k 后的最优解。
证明如下:由于我们 x 是定制了,换个记号,令 h(q) 表示每个物品减去 k 无限制拿 q 个的最大价值,显然 h(q)=g(q)−k×q ,则有 h(q)+k×q=g(q),也就是 h 表示用斜率为 k 的直线去切 (q,g(q)) 的截距。用 h 去考虑一个每个物品价值减 k 后的无限制的最优情况,可以分论讨论每一个 q 的取值,然后找到最小的一个 h 就是无限制每个物品价值减 k 后的无限制的最优情况,但是别忘了 h(q) 本质是用斜率为 k 的直线去切 (q,g(q)) 的截距,显然对于这个凸包,切到 (x,g(x)) 时 h(q) 最大,而恰好此时 h(x)=f(x) 也就是 f(x) 等价于每个物品价值减 k 后的无限制的最优情况。这个结论使得我们找到了一个可以快速计算的函数 f(x) ,并且可以转化到 g(x) 上去,从而实现快速求解。
实现
现在回到做题,我们已知了 x ,并且可以快速计算 f(x) , k 未知,目标求解 g(x) 。实际上我们只需要找到这个 k 就可以了,考虑 k 单调递增变化时,在凸包上切出来的点必定按照 X 递增。那就好办了,直接二分 k 直到切到的点的 X 坐标就是 x 即可。
实际应用中,对于小数二分常常直接限定循环次数;而二分的 check() 函数就是跑一遍无限制dp只不过转移方程需要减去一个 mid (二分中的 mid ) ,然后记录转移次数,并于 x 比较,根据实际情况去二分左区间还是右区间(上凸壳/下凸壳之分)。