二分的几个模板及解释
1.查找≥x的数中最小的一个
在单调递增序列a中查找≥x的数中最小的一个(即x或x的后继)
while (l < r)
{
int mid = (l + r) >> 1;
if (a[mid] >= x)
r = mid;
else
l = mid + 1;
}
return l;
在这个代码中,我们以l和r相等的时候作为终止条件,每次选取中间的数进行判断,如果该数字大于等于目标数,那么答案一定在[l,mid],因为mid右侧全是大于等于x的;如果该数小于x,那么答案一定在[mid+1,r],因为mid及mid左侧全部都小于x,所以只能在右侧。在这个代码里面,我们始终保持答案在[l,r]里面。
也有其他的写法
int l = 0, r = n;
while(l+1!=r)
{
int mid = (l + r) / 2;
if(a[mid]>=x)
r = mid;
else
l = mid;
}
return r;
这里我们从1开始存储,我们的终止条件是l和r相邻。因为l和r都是直接取mid,这样我们能一直保证答案在(l,r],只有a[mid]<x的情况下,l才取值为mid,所以左区间是开的,因为l的初值为0,所以可以从一开始的情况下就保证答案在(l,r]。
或者可以直接用STL的写法,lower_bound是直接返回第一个大于等于x的位置
int num=lower_bound(a+1,a+n+1,x)-a;
return num;
2.查找≤x的最大的一个
在单调递增序列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];
如果a[mid]≤x,那么答案就在[mid,r],因为mid左边的更小,如果a[mid]>x,那么答案就在[l,mid-1]。mid之所以取(l+r+1)/2,因为只剩下两个数的时候会取第一个,如果a[l]小于等于x,那么l=mid会死循环。
看第二种写法
int l = 1, r = n+1;
while(l+1!=r)
{
int mid=(l+r)/2;
if(a[mid]<=x)l=mid;
else r=mid;
}
return l;
这里我们始终保持答案在区间[l,r)之间,当最后只剩下两个数,即l和r相邻的时候,l就是答案。
我为什么在(1)和(2)中都没有给a[0]或者a[n+1]赋初值,是因为这两个都不会被取到,就比如在(1)中,如果只剩下0 1 2这三个数,那么我们会对1进行判定,如果结果只剩0 1,我们会直接输出1,1 2同理。在(2)中也同理。
如果不考虑时间和空间的话,我们可以直接l=0,r=1e9,然后给a[0]赋一个极小值,给r[n]之后全部赋极大值,这样最终也会找到答案。
同理我们依然可以使用STL的方法来解决问题。
int num=upper_bound(a+1,a+n+1,x)-a;
return num-1;
因为upper_bound是返回大于x的最小的那一个,那么他前一个就是小于等于x了(当然不嫌麻烦还是可以用lower_bound加上一个判断来做)
5万+

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



