[模板]整数二分

整数二分有两个模板。

int bsearch_1(int l, int r) {
      while(l < r) {
            int mid = l + 1 >> 1;
            if(check(mid)) r = mid;
            else l = mid + 1;
      }
      return l;
}

这种写法每次把区间压缩到[l, mid]或[mid + 1, 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;
}

这种写法每次把区间压缩到[l, mid - 1]或[mid, r]

如何确定用哪个模板?
取决于check的逻辑,如果我们发现我们需要更新区间为[l, mid]或[mid + 1, r],就用模板1.
如果我们发现我们需要更新区间为[l, mid - 1]或[mid, r],就用模板2.

来看一道题,acwing789.数的范围

寻找数x的起始位置时,需要把q[mid]和x进行比较,如果q[mid] >= x,说明x的位置,要么在mid的左边,要么就是mid,
因为我们要找的是x的最靠左的位置(起始位置),所以我们要往左边搜索,因为q[mid]有可能和x相等,所以也不能把mid跳过,因此更新区间为r = mid;
如果q[mid] < x,说明x的位置在mid的右边(不包括mid),所以往右边搜索,l = mid + 1;
这两个更新的区间,就决定了我们用的是模板1。
实际上,有了r = mid; 就不用判断l了,肯定是l = mid + 1;

寻找x的终止位置时,我们也是把q[mid]和x进行比较,如果q[mid] <= x,说明我们要找的位置肯定在mid的右边(或者就是mid),
我们需要往右边搜索,由于有可能是mid,所以我们不能把mid跳过,因此有l = mid;
如果q[mid] > x,则往左边搜素,r = mid - 1;
这两个更新区间,决定了我们用模板二。

模板一和模板二的主要区别在于,mid = l + r >> 1还是mid = l + r + 1 >> 1,这个主要和整除有关,整除是下取整,所以如果更新的区间是l = mid, 就要加一,是r = mid,就不用加一,就这么记就行了~

这道题的代码如下;

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int q[N];

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; ++i) {
        scanf("%d", &q[i]);
    }
    while(m--) {
        int x;
        scanf("%d", &x);
        int l = 0, r = n - 1;
        while(l < r) {
            int mid = l + r >> 1;      //找x的起始位置,由于更新区间时r=mid,所以用模板1, 所以这里mid定义为l + r >> 1
            if(q[mid] >= x) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        if(q[l] != x) {                  //如果不存在x,直接返回-1
            printf("-1 -1\n");
        } else {                        //否则,我们还需要找x的终止位置
            printf("%d ", l);
            int l = 0, r = n - 1;
            while(l < r) {
                int mid = l + r + 1 >> 1;      //找x的终止位置,由于更新区间时l = mid,所以这里mid定义为l + r + 1 >> 1
                if(q[mid] <= x) {            
                    l = mid;
                } else {
                    r = mid - 1;
                }
            }
            printf("%d\n", l);
        }
    }
    return 0;
}
posted @ 2020-08-11 16:45  machine_gun_lin  阅读(168)  评论(0编辑  收藏  举报