• 算法录 之 二分和三分


      二分:

        二分不是二分,是二分。就是一分为二的二分。

        先来一个例子:

        现在有一个递增的序列 a(1), a(2)...a(n),然后让你查找 x 在不在这个序列里面?

        显然最简单的做法就是一个for循环,从1到n,看看有没和x相等的。。。

        这样确实不错,但是太慢了。。。需要n次才能找到。有没更好的做法呢?

        有(要是没有的话我说这个干什么),那就是二分查找了。

        首先判断 a(n/2) 和 x 谁大谁小,如果 x 大的话,那么显然x可能在 a(n/2+1) 到 a(n) 这个范围里面,而一定不会在 a(1) 到 a(n/2) 这个范围里面,因为递增嘛。。。然后再按同样的方法递归查找后半个区间就行了。

        如果x小的话同理找前半个区间。

        这样每次把区间变成原来的一半,那么就是 logN 级别的时间复杂度,对于长度 n=100000 的序列,只需要不到20次就能判断x了。。。

        具体代码如下:

    复制代码

    // 查找A数组是否存在x。
    bool find(int A[],int N,int x)
    {
        int L=0,R=N-1,M;            // left right middle
    
        while(R>L)
        {
            M=(L+R)/2;
    
            if(A[M]==x) return 1;
            else if(A[M]>x) R=M-1;
            else L=M+1;
        }
    
        if(A[L]==x) return 1;
        return 0;
    }

    递增

    double calc(double n)
    {
        return ;
    }
     
    double solve(double L, double R, double v)
    {
        double M;
        while (L + EPS < R)
        {
            M = (L + R) / 2;
            if (calc(M) > v)
                R = M;
            else
                L = M;
        }
        return R;
    }

    递减

        double calc(double n)
        {
            return ;
        }
         
        double solve(double L, double R, double v)
        {
            double M;
            while (L + EPS < R)
            {
                M = (L + R) / 2;
                if (calc(M) < v)
                    R = M;
                else
                    L = M;
            }
            return R;
        }

        通俗的说,其实二分是针对一个单调函数,在这个函数的一个区间中查找,每次取区间的中点去判断,然后根据大小把区间变成原来的一半。。。然后一直这样下去就好。

        二分的应用可以说是很广很广。

        有一个典型的应用,就是最大值最小化问题,这类问题一般是要求一个方案让所有的数里面最大的那个最小,然后求这个最小数是多少。

        如果是直接硬解的话一般会很困难。所以需要转换思路。

        用二分来试一下。如果先给出一个上限M之后,要求所有数都不大于M,然后如果存在一种方案的话,说明最后的答案一定比这个数要小于等于,所以二分下去一次次逼近答案就OK了。

        题目的话 UVA 714 就是经典的二分+贪心问题。

      三分:

        如果说二分针对的是单调函数,那么三分针对的是双调函数。也就是下面这样的函数。

        三分是求这样先增后减或者是先减后增的函数的极值的。

        具体步骤就是:对于区间 (L,R),先求出 1/3 处的 M1 和 2/3 处的 M2,然后比较 M1 和 M2 处的大小,如果 M1 处的值大于 M2 处的值,那么极值一定在(M1,R)这个区间范围内,否则一定在 (L,M2)这个范围内。

        因为如果 M1处大于M2处的话,M1不可能在极值点的右边。。。所以。。。就是这样了。。。每次变成原来区间的2/3大小。。。

        二分三分的用处很广很广,比如对一个符合的序列,或者是在计算几何中用来求一些不好算的值等等。。。

        比如求点到椭圆的最近距离的题目,就可以通过三分一次次的逼近。

        const double EPS = 1e-10;
         
        double calc(double n)
        {
            return;
        }
         
        double solve(double L, double R)
        {
            double M, RM;
            while (L + EPS < R)
            {
                M = (L + R) / 2;
                RM = (M + R) / 2;
                if (calc(M) < calc(RM)) //计算最小值
                    R = RM;
                else
                    L = M;
            }
            return L;
        }

  • 相关阅读:
    SpringMVC 数组类型的参数: Cannot generate variable name for non-typed Collection parameter type
    java.lang.NoClassDefFoundError: org/springframework/boot/context/embedded/FilterRegistrationBean
    Optional 的基本用法
    反序列化失败Failed to deserialize --- local class incompatible: stream classdesc serialVersionUID
    xxx.jar 中没有主清单属性
    低级sql语法错误: BadSqlGrammarException
    Apollo配置中心
    ELK+Beats日志分析系统部署
    Openface 入门
    spring boot 之 spring security 配置
  • 原文地址:https://www.cnblogs.com/zcy19990813/p/9702740.html
Copyright © 2020-2023  润新知