整数二分

138 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

二分的性质

有单调性就一定可以二分,如果没有单调性也可以二分 image.png 本质:左边满足性质,右边不满足,二分可以用来寻找满足的边界跟不满足的边界(针对整数二分)

分析出来有上面这种情况的:只有满足性质跟不满足性质两种,我们就要想到可以用二分来做

情况一(模板一)


二分红色的边界点 image.png

  1. check(mid)用来检测是否满足红颜色的条件
  2. true的条件下,注意:是包含mid的哦,因为mid满足红色的性质
  3. false的条件下,是不包含mid的,又因为整数二分,所以是mid - 1
  4. mid = (l + r + 1)/2

情况二(模板二)


二分绿色的边界点 image.png 这里check(mid)用来检测是否满足绿颜色的条件 image.png

问题:如何选择用哪个模板?

  1. 其实不用过于纠结,方法如下
  2. 先写一个check函数, 也就是思考一下性质是什么
  3. 判断true或false的时候,对应的l跟r的变化,如果true的情况下:
    1. 区间需要更新为l = mid,那么mid = (l + r + 1) / 2
    2. 区间需要更新为r = mid,那么mid = (l + r) / 2

记忆小技巧army:读音与r mid相似,军队理想状态下没有人牺牲,也就是不变,军队的决策正确(true),也就是mid 不用加一,如果false,l = mid + 1

为什么第一个模板mid要加上1

  1. 假设:l = r - 1 , mid 没有加1(mid是向下取整的)
  2. 第二步:true的情况下更新为l = mid , l 还是=l, 区域范围还是没有变化,就会陷入死循环

题目

www.acwing.com/problem/con…

找第一个x的位置

  1. 发现:check函数是q[mid] >= x
  2. 右边所有数都满足>= x这个性质,左边所有数都不满足>=x这个性质

image.png

找最后一个x的位置

  1. 发现:check函数是q[mid] <= x
  2. 左边所有数都满足<= x这个性质,右边所有数都不满足<=x这个性质

image.png

代码

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int q[N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) scanf("%d", &q[i]); // 将数组读进来
    
    while (m --) // m个询问
    {
        int x;
        scanf("%d", &x);
        
        int l = 0, r = n - 1; // 从0开始呀
        while(l < r)
        {
            int mid = l + r >> 1;
            // check()函数
            if (q[mid] >= x) r = mid;
            else l = mid + 1;
        }
        
        // 二分出来的结果:第一个>= x的数,下面为不存在x的判断
        if (q[l] != x) cout << "-1 -1" << endl;
        else
        {
            cout << l << ' '; // 这里输出l跟输出r是一样的,因为while循环结束的时候l=r
            
            // 寻找右边界
            int l = 0, r = n - 1;
            while (l < r)
            {
                int mid = l + r + 1>> 1;
                if (q[mid] <= x) l = mid;
                else r = mid - 1;
            }
             cout << l << endl;
        }
    }
}

总结

  1. 二分的模板是一定有解的,如果无解的话就是跟题目有关的,根据题目来进行判断即可
  2. 二分一定可以二分出来边界
  3. 根据题目+二分出来的边界判断是否有解