文章目录
二分(算法竞赛进阶指南笔记)
作用:
①二分的基础用法是在单调序列或单调函数中进行查找.
②当问题的答案具有单调性时,就可以通过二分把求解转化为判定(根据复杂度理论,判定的难度小于求解)。
③还可以扩展到通过三分法去解决单峰函数的极值以及相关问题。
一、整数二分
1.模板:
二分的写法保证最终答案处于闭区间[l,r]以内,循环以l=r结束,每次二分的中间值mid会归属于左半段与右半段二者之一。
(1)在单调序列a中查找>=x的数中最小的一个(即x或x的后继):
while (l < r)
{
int mid = (l + r) >> 1;
if (a[mid] >= x)
r = mid;
else
l = mid + 1;
}
return a[l];
(2)在单调序列a中查找<=x的数中最大的一个(即x或x的前驱):
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (a[mid] <= x)
l = mid;
else
r = mid - 1;
}
return a[l];
2.理解:
在第一段代码中,若a[mid]>=x,则根据序列a的单调性,mid之后的数会更大,所以>=x的最小的数不可能在mid之后,可行区间应该缩小为左半段。因为mid也可能是答案,故此时应该取r=mid。同理,若a[mid]<x,取l=mid+1。
在第二段代码中,若a[mid]<=x,则根据序列a的单调性,mid之前的数会更小,所以<=x的最大的数不可能在mid之前,可行区间应该缩小为右半段。因为mid也可能是答案,故此时应该取l=mid。同理,若a[mid]>x,取r=mid-1。
3.注意:
①首先聊聊两者mid取法的区别:
第一种要求>=x的数最小的,所以mid要尽可能小,不+1,第二种要求<=x的数里面最大的,要从大了往小了搜,+1。
如上两种代码,这两种二分写法有两种形式:
- 缩小范围时,r = mid, l = mid + 1,取中间值时,mid = (l + r) >> 1
- 缩小范围时,l = mid, r = mid - 1,取中间值时,mid = (l + r + 1) >> 1
r和l的分支方法 与 mid的取法是配套的。下面给出简单证明:
如果取l = mid, r = mid - 1时mid = (l + r) >> 1。当r - l = 1时,mid = 2l,所以下一轮收缩范围,如果是l变,就变成l = l死循环,如果是r变,就会发生r < l,与我们While循环以l == r结束不符。
由此同样可证其余搭配,记住:l = mid的时候,mid的取法要+1
②>>和直接 /2 的区别:
右移运算是向下取整,而整数除法是向零取整,在二分值域包含负数时后者不能正常工作。
③仔细分析这两种mid的取法,我们还发现:mid=(l+r)>>1不会取到r这个值,mid=(l+r+1)>>1不会取到l这个值。我们可以利用这一性质来处理无解的情况,把最初的二分区间[1,n]分别扩大为[1,n+1]和[0,n],把a数组的一个越界的下标包含进来。如果最后二分终止于扩大后的这个越界下标上,则说明a中不存在所求的数。
4.使用流程:
(1)分析问题,左右半段哪个是可行区间,mid归属哪半段
(2)根据分析结果,

本文详细介绍了二分查找算法,包括整数二分、实数域上的二分、三分法求单峰函数极值以及如何将二分应用于求解问题。通过实例解析了二分查找在单调序列中的应用,讨论了不同情况下的二分模板选择,并展示了如何将二分转化为判定问题,以解决实际竞赛中的复杂问题。此外,还提到了精度控制和无解情况的处理策略。
最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



