整数二分
整数二分最重要的就是处理好边界问题。
以下两种模板就可以基本解决所有此类问题。
首先我们可以制定一个标准,将一个答案区间分为两个部分。这个判断标准一般称之为check()函数。(比如这里的标准可以确定为 当前数是大于等于x还是小于等于x //x为所求数)
接下来需要思考所求的答案是1(左边区间的右边界)还是2(右边区间的左边界)。
模板1
如果所求为1
则check函数应指向绿色区间。
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
我们的目标是找到数轴上的x具体等于几,就需要让l和r不断地逼近这个x,直到l==r
。
如果当前数mid满足check,就说明mid在这个绿色区间内,此时就可以让答案区间的左边界收拢到mid,也就是让l=mid
;
如果当前数mid不满足check,即说明mid不在这个绿色区间内,此时就可以让答案区间的右边界收拢到mid-1,即r=mid-1
。(因为mid不符合答案的条件,且答案区间[l,r]是闭区间,我们必须保证这个区间里的数都是满足当前check的结果,所以此处需要减1)
这里的 mid 为什么定义成 l+r+1 呢
如果不加1的话,当l=r-1时,mid=l+r>>1=l,并且此时不幸地,mid符合check条件,那么执行l=mid,相当于没改,就造成了死循环。
如果加1,则当l=r-1时,mid=r,执行l=mid之后,l=r,循环停止。
模板2
如果所求2
则check函数应指向红色区间。
while(l<r){
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
此时同样的,也是让l和r逼近x。
如果mid满足check,则mid在红色区间内,答案右区间收拢到mid,即r=mid
;
如果mid不满足check,即其不在红色区间内,则答案左区间收拢到mid+1,即l=mid+1
。
在实际使用当中
在彻底理解了上述原理之后,我们其实不需要每次都复杂地全想一遍。
实际上只需要随便写一个check,然后画一下数轴,思考这个区间应该如何收拢(l=mid 还是 r=mid)。
如果是l=mid, 那么接下来就是r=mid-1,而且前面的mid要补加1;
如果是r=mid,那么接下来就是l=mid+1。
浮点数二分
浮点数二分没有多少需要注意的细节,只要根据check吧 确定好收拢的是左边界还是右边界就好了。
while(r-l>1e-8){
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
顺便提一下while的判定条件,因为double的精度问题,要写成while(r-l>1e-x)
的形式,x最好比题目要求的精度多两位。