整数二分的模板
整数二分的难点
下标越界问题
首先考虑这样的例子:
从{0,1}中找到1的下标:通常做法是l、r找到边界值0,1,当然也可以从-1、2开始,这通常不是问题;
在考虑结束条件的时候,往往考虑用l代指最后结果的下标。更新l和r用的是mid的取值,在C++等语言中,默认除2操作采用下取整,这就导致,0+1>>1永远是0,假如在找向右区间的时候,令l=mid,会陷入死循环;假如令l=mid+1,似乎解决了l的赋值问题,但是在找向左区间的时候。令r=mid,一样会面临这样的问题(这是由于这种模板利用除法的不对称性,考虑l、r不对称操作来消除)
但是这种显然增加了记忆的负担,考试的时候,边界考虑很让人抓狂,尤其是对齐问题。
下面的模板采取左右区间对称操作,只有当v[mid]和k相等的时候,才会退出,当然,这种二分找到的是严格递增序列中的下标,假如要求必须是下取整,或者上取整,也可以在后面增加向前遍历的操作。
int l=0,r=n-1;
while(l<=r){
int mid=l+(r-l)/2;
if(v[mid]<k) l=mid+1;
else if(v[mid]>k) r=mid-1;
else{
l=mid;
break;
}
}
if(v[l]!=k) cout<<-1;
至于给出{1,2,2,2,2,2,2,2,2...2,1}总数是10^9这种数据,这样的方法就显然不适用了。这样二分效率不是会很低,但是向前遍历就会很慢了。
于是,解决这一类__下确界__的模板,就变成了,首先,推出条件不在是l>r,而是l>=r,想象l=0,r=1,此时mid=0,当要找1时,主动让l=mid+1;要找0时,主动让r=mid;同时配合mid采用下取整;解决__上确界__当要找1时,主动让l=mid;找0,r=mid-1;配合mid采用上取整;
总之,这个利用上下取整条件的二分模板关键在于,让l自由地指向1的位置,让r指向0的位置,这时再考虑mid是不是要下取整,因为无论去哪里,l和r的分支两端都只差一个下标,这样能够使得__划分完整地遍历到数轴__的每一个值。

浙公网安备 33010602011771号