# 问题引入
如果你遇到了一类这样的问题:
有 个物品,你必须在遵守一定限制的前提下从中选出 个物品,最大 / 小化这 个物品的总价值。
你可能会想到动态规划,但是这样动态规划时间复杂度是 起步的。
但如果这个问题去掉 个物品的要求,你可以直接用一维动态规划,或许会更容易解决;而且你发现,如果要求选 个物品的情况下最大 / 小价值为 ,函数 具有上凸 / 下凸的性质(实际上大多数这类问题都有这个性质),那么你可以用到一个利器来优化朴素的二维 : 二分。
# 算法介绍
我们设 表示要求选的物品个数, 表示选出 个物品所能得到的最大 / 小价值,以 为横轴,以 为纵轴,可以画出这样的图:
凸包有什么性质?斜率单调递减!如果我们考虑用一条直线去切这个凸包:
我们总能切到一个最高点,设这个点是 ,直线斜率为 ,那么有如下等式成立:
即:
那么 有什么实际含义吗?我们可以理解为 把每个物品的价值都减去一个 ,则我们选出的 个物品总价值就正好减去了 !
如果我们一开始设定了这条直线的斜率为 ,然后我们把每个物品的价值都减去 ,进行一次不限定物品个数的一维 ,能找到一个最大值,这个最大值就对应着这条相切直线的截距,如果我们在 的过程中还记录下我们选了多少个物品,就能很容易地反推出 是多少!
于是我们的优化思路就出来了:我们二分这条直线的斜率,找到一个斜率使直线能和我们需要的 相切,我们就能反推出 是多少!
关于斜率的上下界,我们找到 之间的斜率和 之间的斜率就可以确定下来。
大体思路就是这样,时间复杂度为 ,其中 为物品个数(此处我们假定一维 是线性可做的), 是价值的值域。但我们还有一个细节问题需要说明。如果我们的图长这样:
也就是说存在一条直线和图像的多个点相切的话,我们该如何判断当前斜率是大了还是小了?我们不妨把切到的这个 永远设定为最小的那个 ,我们在二分的过程中就能处理这个问题了。
由此可见, 二分使用的关键在于证明答案的凹凸性。一般的证明思路是 或 ,即后面越选物品能获得的价值越大 / 小。
废话不多说,上例题!
# 例题
# 例题 1. 朝日捡石头
C6 - 2024级程序设计基础第六次上机赛 J. 朝日捡石头
这是一道 二分的模板题。