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\)

  • 相关阅读:
    Python并发(一)
    Python协程详解(二)
    Python协程详解(一)
    Python装饰器
    ●BZOJ 3676 [Apio2014]回文串
    ●POJ 3974 Palindrome(Manacher)
    ●BZOJ 1692 [Usaco2007 Dec]队列变换
    ●BZOJ 4698 Sdoi2008 Sandy的卡片
    ●BZOJ 4516 [Sdoi2016]生成魔咒
    ●BZOJ 3238 [Ahoi2013]差异
  • 原文地址:https://www.cnblogs.com/nofind/p/15725689.html
Copyright © 2011-2022 走看看