zoukankan      html  css  js  c++  java
  • [模板]整数二分

    整数二分有两个模板。

    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
    ");
            } 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
    ", l);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    CruiseControl.NET 三言两语
    在MFC程序中增加控制台
    VS2005 编译Release版本出现清单文件的错误
    内存泄露问题
    软件设计原则
    boost::shared_ptr 分析与实现
    corelDraw 的CDR格式解析
    13. 量词操作符—【LINQ标准查询操作符】
    SQL SERVER执行查询的顺序
    CSLA命令对象的简单封装
  • 原文地址:https://www.cnblogs.com/linrj/p/13476612.html
Copyright © 2011-2022 走看看