注:模板来自 acwing yxc
整数二分模板:
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
二分的疑惑主要是在mid的取值,以及l和r的迭代。
这里先讨论第二个问题,先看模板一,这是在找左边界,由于如果mid这个点满足我们的条件,说明我们要求的边界点在mid点或者mid左边,但是不可能小于l,所以取区间[l,mid];如果mid点不满足条件说明边界点在mid右边,但不可能等于mid或者大于r,但是可能取mid+1,所以我们取[mid+1,r];然后看模板二,这是在找右边界,如果mid满足条件,那么右边界可能在mid点或者大于mid小于等于r,所以我们取[mid,r];如果mid不满足条件,说明边界点在mid左边,所以取[l,mid-1];
第一个问题:首先明确两点:
①mid值不同是为了防止死循环,而死循环的条件只有两次迭代后,l或者r的值仍不改变。
②l,mid,r都是整数,说明如果存在l<mid<r这种类似的关系,不可能通过l或者mid加上一个小数而使得其中某一个<号变成=号。
而我们知道(l+r>>1)和(l+r+1>>1)不可能小于l或者大于r,且第一种情况满足的是l<=mid<r,但我们每次迭代l的时候取的是mid+1,说明两次迭代后的l不可能相等,因为l<mid+1嘛,又因为mid<r,而迭代r的方式是r=mid,说明r每次都在变小,不可能陷入死循环。第二种情况满足的是l<mid<=r,我们迭代l的时候,令l=mid这样l一定变大,令r=mid-1这样r一定变小,不会陷入死循环。
这两种mid取值的根本原因是c++的向下取整属性,即int(l+r)/2<double(l+r)/2;