二分算法

整数二分

二分的本质不是单调性。
(有单调性一定可以二分,但是二分可以做的题,不一定需要满足单调性。)
二分的本质是二段性
就是有一个分界点O,分界点左边都是状态x,分界点右边都是状态y。
image

通过二分就可以找到红色区域的右边界值或者绿色区域的左边界值
每次保证我们区间内有答案。

O点就是我们想求的答案点,绿色是满足性质的区域,红色是不满足性质的区域。

求右分界点模板

当想找不满足性质的边界值(红色区域的右边界值)
例如:
求递增序列中,求小于10的最大值。
check函数就是:\(mid<10\)

过程:

  1. 找中间值 \(mid =\frac{l+r+1}{2}\) 计算mid时需要加1

  2. if(check(mid))等于true或者是false
    check(m)检查是不是在红色区间。

    • 如果在红色区间(true) 正确区间为:\([mid,r]\) ==> l=mid
    • 否则不在区间返回 : false 正确区间为:\([l,mid-1]\) ==> r=mid-1

为什么需要+1?
原因是如果不加上1,那么mid得到的是下取整的数,那么有可能[m,r]更新过后m会一直等于m(m+1==r的情况)会陷入死循环

代码:

int bsearch(int l, int r)
{
    while(l<r){
        int mid=l+r+1 >>1;
        if(a[mid]<=k) l=mid;
        else r=mid-1;
    }
}

求左分界点模板

当想找满足性质的边界值(绿色区域的左边界值)
例如:
求递增序列中,求大于10的最小值。
check函数就是:\(mid>10\)

过程:

  1. 找中间值 \(mid =\frac{l+r}{2}\)

  2. if(check(mid))等于true或者是false
    check(m)是检查m是在满足绿颜色区间的性质(检查是不是在绿色区间)

    • 如果是true 正确区间:\([l,mid]\) ==> r=mid
    • 如果是false 正确区间:\([mid+1,r]\) ==> l=mid+1

代码:

int bsearch(int l, int r)
{
    while(l<r){
        int mid=l+r >>1;
        if(a[mid]>=k) r=mid;
        else l=mid+1;
    }
}

模板题

题目:
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。

对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。

如果数组中不存在该元素,则返回 -1 -1。

代码:

const int N = 2e6 + 10;
int n,m, a[N];
void solve(){
    cin>>n>>m;
    for(int i=0;i<n;i++)
        cin>>a[i];
    while(m--){
        int k;cin>>k;
        int l,r;
        //找左边界
        l=0,r=n-1;
        while(l<r){
            int mid=l+r >>1;
            if(a[mid]>=k) r=mid;
            else l=mid+1;
        }
        int ans1=r;
        //找右边界
          l=0,r=n-1;
        while(l<r){
            int mid=l+r+1 >>1;
            if(a[mid]<=k) l=mid;
            else r=mid-1;
        }
        int ans2=r;

        if(a[ans1]==k) {
            cout<<ans1<<" "<<ans2<<endl;
        }
        else cout<<"-1 -1"<<endl;
    }
}

浮点数二分

和整数一样,while循环时换成r - l > 精度即可。

而且因为是浮点数,不需要考虑 +1 -1 之类的。

代码:

int bsearch(double l, double r)
{
    while(r - l > 精度){
        double mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid;
    }
}

应用

平均值问题

平均值问题一般都是二分。
问题描述:
给定序列a,判断是否存在一个方案,使其平均值大于等于avg

方法:
二分平均值,使a的所有元素减去avg。
看否是存在长度大于F的一段串的和 \(>=0\)

posted @ 2022-08-06 19:37  kingwzun  阅读(87)  评论(0编辑  收藏  举报