题目介绍 & 无端联想
一句话说:
min-max是为了压制优化目标中表现最突出的成分,降低最恶劣情况的影响
max-min为了提升优化目标中表现最差的成分,增加整体解决方案均衡性
本题求最大甜蜜度,属于最大最小问题,从礼盒商的角度来说应是为了增加礼盒均衡性,避免有的用户买到觉得自己亏了。
此类题目常见的形式是:给出一个序列,从 n 个里面选 k 个,对这个组合求某值,再从全部组合的角度对该某值求最大最小。
由于是求最值,解法上多少有点逆向思维,正向思维是求出每一种组合的值,比较得出最大的。逆向思维是推导这个值的上下限,二分夹出来这个值。由一个遍历问题,转化为一个二分判断是否成立的问题。
要取得最大的甜蜜值,理论上的上限,是最大价格和最小价格的绝对差。理论上的下限,是两个相同的价格。根据二分法的需要,首先判断中间值 mid 是否成立,即当最大甜蜜度 = mid 时,能否找出 k 个差价都大于 mid。如果找到了 >= k 个差价,那么提升下限,找更大的甜蜜度;如果只找到了 < k 个差价,那么减低上限,找更小的甜蜜度。
这里面有一点小小的转换,如果超过 k 个差价大于当前的甜蜜度,就认为还有更大的甜蜜度可以成立。
类似题目
LC 2064。这个是求最小值,也就是压制可能取得最大商品数的店铺。用上面的思路和套路,很快做出来了
一开始时间损耗极大,因为我翻了个错误,懒省事用逐个减的方式看 sum 里面有几个 k,其实用除法运算就一步到位了。
二分法
二分法老是出问题。
- 首先理想状态下,应该是 left == right 退出循环。因此循环的条件是 left < right,潜台词认为这是一个左闭右开区间 [left, right)。
- 其次在相加 left 和 right 的时候,为了防止整型溢出,可以写成 mid = left + (right - left) / 2,或用 long long。
- 接着 (left+right+1) >> 1 这种向高位取整的做法,退出循环的条件依然是 left == right,但得出的结果 left 会比正确答案小 1。
- 最后,需要保证上限 right = mid -1,具体原因参见第三篇参考文献。
while (left < right) {
int mid = (left+right+1) >> 1;
if (check(price, k, mid)) {
left = mid;
} else {
right = mid - 1;
}
}
因为 mid 已经 check 了一次,如果不能从搜索区间里面踢出去,还能对他重复 check 的话,就不保证不会死循环了,所以新的界限不能包含 mid。
记住当前是什么区间,如果是左闭右开区间,right = mid 的时候,区间内天然已经没有了 mid 这个值,不需要额外加一减一,如果是左闭右闭区间,left 和 right 都需要加一减一才能排除 mid 这个元素。