zoukankan      html  css  js  c++  java
  • 二分法

    一、算法介绍:

    在中学我们曾经学过求方程近似解用过二分,其中心思想可概括为:在单调序列或者单调函数中进行查找时,通过不断将答案区间减半来最终确定答案。
    上述仅为思想,实际上二分的应用是很广泛的,以下将列举其几种经典应用。

    注:二分的写法有很多,也需要注意相当多的细节,我们只需要保证自己的写法正确即可。

    算法复杂度:\(O(logn)\)

    二、算法应用:

    1.在单调递增(递减)数列中查找\(\geq x\)\(\leq x\))的数中最小(大)的一个。

    这可以说是二分法最基础的应用,以下举两个典型例子即可。
    (1)在单增序列\(a\)中查找\(\geq x\)的数中最小的一个(即\(x\)或者\(x\)的后继):

    #include <iostream>
    using namespace std;
    int main()
    {
        int a[] = {233333, 112, 211, 322, 421, 512, 632, 745, 832, 1349, 2330};
        int l = 1, r = 10,x=345;
        while (l < r)
        {
            int mid=(l+r)>>1;
            if(a[mid]>=x)r=mid;//答案在[l,mid]区间。
            else l=mid+1;//a[mid]<x,答案在[mid+1,r]区间。
        }
        cout<<a[l]<<endl;
        return 0;
    
    }
    

    (2)在单调递增序列\(a\)中查找\(\leq x\)的数中最大的一个(即\(x\)或者\(x\)的前驱):

    #include <iostream>
    using namespace std;
    int main()
    {
        int a[] = {233333, 112, 211, 322, 421, 512, 632, 745, 832, 1349, 2330};
        int l = 1, r = 10,x=345;
        while (l < r)
        {
            int mid=(l+r+1)>>1;//注意这里。
            if(a[mid]<=x)l=mid;//答案在[mid,r]区间。
            else r=mid-1;//a[mid]>x,则答案在[l,mid-1]区间。
        }
        cout<<a[l]<<endl;
        return 0;
    
    }
    

    我们可以注意到第二种写法中是\(mid=(l+r+1)>>1\),而非\(mid=(l+r)>>1\),这是因为当\(l+1=r\)\(mid=l\),接下来若进入\(l=mid\)分支则区间并未缩小,而进入\(r=mid-1\)分支会使得\(l>r\)

    根据上面的讨论我们容易注意到第一种写法中\(mid\)无法取到\(r\),第二种写法中\(mid\)无法取到\(l\),我们可以借此处理无解的情况,即将最初的答案区间扩大为\([0,n]\)或者\([1,n+1]\),若二分最终停止在越界的下标上,则证明无解。

    综上,正确二分需注意以下几点:
    (1)判断左右哪一个是可行区间,注意\(mid\)的归属问题。
    (2)根据需要选择是采取第一种还是第二种写法。
    (3)二分终止条件为\(l==r\)

  • 相关阅读:
    [翻译] FreeStreamer 在线流媒体播放
    [转] 每个程序员都必须遵守的编程原则
    iOS7以下设备获取mac地址
    iOS中alloc与init
    ON、WHERE、HAVING的区别
    在SQL语言中,join什么时候用,什么时候不用啊?请高手举例解释一下。谢谢
    你能识别这些科技公司的真假logo吗?
    in 和 exist 区别
    union和union all的区别
    Mysql避免全表扫描sql查询优化 .
  • 原文地址:https://www.cnblogs.com/nofind/p/15725689.html
Copyright © 2011-2022 走看看