二分

Demo大约 3 分钟

时间复杂度:O(logn)

对于一整个数据,有前一部分的数据满足某种性质,后一部分不满足某种性质,用二分可以找出这个分界线,二分经常处理满足某种条件的最大最小值问题

有整数二分和小数二分两种

整数二分:

假设给出一段区间,前半部分满足某种性质,后半部分不满足某种性质,那么分界点有两个,即满足性质的最后一个res1和不满足性质的第一个res2,如图1:

如果我们想找res1,那么我们可以先找中间值mid=(l+r+1)/2,判断他是否满足性质,如果满足性质,那么答案就在[mid,r]之间,那么就将l设为mid之后重复找,如图:

那么l重设之后,再找mid,如果mid不满足性质,那么答案就在[l,mid-1]之间,那么将r设为mid-1继续进行查找,如图:

一直到l>=r的时候停止查找,那么答案就是a[l]

伪代码如下:

cheek函数是用来检查是否符合要找的答案的性质

while(l<r){
    int mid=(l+r+1)/2;
    if(cheek(mid)) l=mid;
    else r=mid-1;
}

如果我们想找res2,那么我们可以先找中间值mid=(l+r)/2,判断他是否满足性质,如果不满足性质,那么说明答案在[l,mid]之间,那么将r设为mid之后继续上述步骤,如图:

重设之后继续找mid,如果mid满足性质,那么说明答案在[mid+1,r]之间,那么将l设为mid+1,继续上述操作,如图:

一直到l>=r的时候停止查找,那么答案就是a[r]

伪代码如下:

cheek函数是用来检查是否符合要找的答案的性质


while(l<r){
    int mid=(l+r)/2;
    if(cheek(mid))r=mid;
    else l=mid+1;
}

注意两个部分中间值的取值,取值不当会死循环或段错误

例题:

给出一个含有n个数的升序数组,q个查询,每次查询给出一个数x,查询x的起始位置和终止位置,如果不存在输出“-1 -1”

思路:查询x的起始位置,x的前面的数满足a[i]<=x,x的后面满足a[i]>=x,起始位置就是a[i]>=x的第一个数,终止位置是a[i]<=x的最后一个位置,那么我们就可以用二分解决。如果找到的起始位置和终止位置所值的数不是x,那么说明x不在数组里,输出“-1 -1”

#include<iostream>
#include<algorithm>

using namespace std;

const int N=1e5+10;
int q,x;
int a[N];
int n;
int main(){
    cin>>n>>q;
    for(int i=1;i<=n;i++)cin>>a[i];
    while(q--){
        // cout<<"q=="<<q<<endl;
        cin>>x;
        int l=1,r=n,res1,res2;
        while(l<r){
            int mid=(l+r+1)/2;
            if(a[mid]<=x)l=mid;
            else r=mid-1;
        }
        res2=l;
        // cout<<"res2=="<<res2<<endl;
        l=1,r=n;
        while(l<r){
            int mid=(l+r)/2;
            if(a[mid]>=x)r=mid;
            else l=mid+1;
        }
        res1=l;
        // cout<<"res1=="<<res1<<endl;
        if(a[res1]!=x){
            res1=0,res2=0;
        }
        cout<<res1-1<<" "<<res2-1<<endl;
    }
    return 0;
}

小数二分:

小数二分的时候,因为没有整数那种l==r的情况,所以我们认为l和r距离很近的时候(如r-l<1e-7),可以大致认为他们是一个同一个小数,将l或r作为结果,循环结束

例题: 给定一个浮点数n,求它的三次方根,输出数为6位小数

-1000<=n<=1000

#include<iostream>
#include<algorithm>
using namespace std;


int main(){
    double n;
    cin>>n;
    double l=-10000,r=10000;
    while(r-l>1e-7){
        double mid=(l+r)/2;
        // cout<<"l=="<<l<<" r=="<<r<<endl;
        // cout<<"mid=="<<mid<<endl;
        if(n-(mid*mid*mid)>0)l=mid;
        else r=mid;
        // cout<<"-=="<<n-(mid*mid*mid)<<endl;
    }
    printf("%.6f",l);
    return 0;
}
Loading...