• 二分搜索


      二分搜索法是通过不断缩小可能存在的范围,从而求得问题最优解的方法。

    1. 从有序数组中查找某个值

      给定长度为 n 的单调非递减数列 a0......an,和一个数 k,求满足 ai ≥ k 条件的最小的 i,不存在的情况下输出n。

      

    int n, k;
    int a[MAX_N];
    // STL
        void solve() {
            int ans = *lower_bound(a, a + n, k);
            printf("%d
    ", ub);
        }
    //
    void solve() {
        //初始化解的范围
        int lb = -1, ub = n;
        //重复循环,直到解的范围不大于一
        while (ub - lb > 1) {
            int mid = (lb + ub) / 2;
            // 如果mid满足条件,则解的存在范围变为(lb, mid] 
            if (a[mid] >= k)
                ub = mid;
            // 如果不满足,则解的范围变为(mid, ub] 
            else
                lb = mid;
        } 
        printf("%d
    ", ub);
    } 

      求满足某个条件C(x)的最小的 x 这一问题。对于任意满足C(x) 的 x, 如果所有的 x' ≥ x 也满足C(x')的话,我们就可以用二分搜索来求得最小的 x。首先将区间的左端点初始化为不满足C(x)的值,右端点初始化为满足C(x) 的值,然后每次取中点 mid = (lb + ub)/ 2,判断 C(mid)是否满足并缩小范围,直到 ( lb,  ub ] 足够小为止。最后 ub 就是需要求的最小值。

      最大化的问题也可以用同样的方法求解。

    2. 假定一个解并判断是否可行

      

      

      这个问题用二分搜索可以非常容易地求得答案。让我们套用二分搜索的模型来解决这个问题。

      令: 条件 C(x) := 可以得到 K 条长度为 x 的绳子

      则问题就变成了求满足 C(x)条件的最大的 x。在区间初始化时,只需要用充分大的数 INF 作为上界即可: lb = 0,  ub = INF.

      现在的问题是是否可以高效地判断 C(x)。由于长度为 Li的绳子最多可切出 floor(Li / x) 段长度为 x 的绳子,因此:

        C(x) = (floor(Li / x)的总和是否 ≥ K)

        复杂度为 O(N)

    int N, K;
    double L[MAX_N];
    //判断是否满足条件 
    bool C(double x) {
        int num = 0;
        for (int i = 0; i < N; i++)
            num += (int) (L[i] / x);
        return num >= K;
    }
    
    void solve() {
        //初始化解的范围
        double lb = 0, ub = INF;
        for (int i = 0; i < 100; i++) {
            double mid = (lb + ub) / 2;
            if (C(mid))
                lb = mid;
            else
                ub = mid;
        } 
        printf("%.2f
    ", floor(ub * 100) / 100);
    }

      像这样,如果在求解最大化或最小化问题中,能够比较简单地判断条件是否满足,那么使用二分搜索法就可以很好地解决问题。

    二分搜索法的结束判定

      在输出小数的问题中,一般都会指定允许的误差范围或者是指定输出中小数点后面的位数。因此在使用二分搜索法时,有必要设置合理的结束条件来满足精度要求。在上面的程序中,我们指定了循环次数作为终止条件。一次循环可以把区间范围缩小一半,也可以把终止条件设为像 (ub - lb ) > EPS 这样,指定一个区间大小。在这种情况下,如果 EPS 取值太小,就可能会因为浮点数精度的原因导致陷入死循环,要小心。

     3. 最大化最小值

       

      

      类似的最大化最小值或者最小化最大值的问题,通常用二分搜索就可以很好地解决。

      定义: C(d) := 可以安排牛的位置使得最近的两头牛的距离不小于 d

      那么问题就变成了求满足 C(d)的最大的 d。另外,最近的间距不小于 d 也可以说成是所有牛的间距都不小于 d,因此就有

      C(d)= 可以安排牛的位置使得任意的牛的间距都不小于 d

      这个问题的判断可以使用贪心法就可以非常任意地求解:

        对牛舍的位置 x 进行排序;

        把第一头牛放进 x0 的牛舍;

        如果第 i 头牛放入了 xj 的话,第 i + 1 头牛就要放入满足 xj + d ≤ xk 的最小的 xk 中;

      每一次判断对每头牛最多进行一次处理,因此复杂度为 O(N)。

      

    int N, M;
    int x[MAX_N];
    
    bool C(int d) {
        int last = 0;
        for (int i = 1; i < M; i++) {
            int crt = last + 1;
            while(crt < N && x[crt] - x[last] < d)
                crt++;
            if (crt == N) return false;
            last = crt;
        }
        return true;
    } 
    
    void solve() {
        sort(x, x + N);
        int lb = 0, ub = INF;
        while (ub - lb > 1) {
            int mid = (lb + ub) / 2;
            if (C(mid)) lb = mid;
            else ub = mid;
        }
        printf("%d
    ", lb);
    }

    4. 最大化平均值

      

      

      

      一般最先想到的方法是把物品按照单位价值排序,从小到大贪心地进行选取。但是这个方法是不可行的。实际上用二分搜索可以很好地解决。

      定义: 条件 C(x) := 可以选择使得单位重量的价值不小于 x

      因此原问题就变成求满足 C(x)的最大的 x。那么该如何判断 C(x)是否可行?假设我们选了某个物品的集合 S,那么它们的单位重量的价值是

       

      因此就变成了判断是否存在 S 满足下面条件

       变形得

      因此,可以对(vi - x * wi)的值进行排序贪心地进行选取。因此就变成了

        C(x)= ((vi - x * wi)从大到小排列中的前 k 个和不小于 0)

      每次判断的复杂度是O(n log n)。

    int n, k;
    int w[MAX_N], v[MAX_N];
    double y[MAX_N]; // v - x * w
    
    bool C(double x) {
        for (int i = 0; i < n; i++)
            y[i] = v[i] - x * w[i];
        sort(y, y + n);
        double sum = 0;
        for (int i = 0; i < k; i++)
            sum += y[n - i - 1];
        return sum >= 0;
    }
    void solve() {
        double lb = 0, ub = INF;
        for (int i = 0; i < 100; i++) {
            double mid = (lb + ub) / 2;
            if (C(mid)) lb = mid;
            else ub = mid;
        }
        printf("%.2f
    ", ub);
    }
  • 相关阅读:
    eclipse技巧总结
    java中的全等和相似
    curl命令
    tr命令
    Ubuntu下安装支付宝安全控件
    Firefox about
    Ubuntu Terminal Shortcut
    ulimit
    ajax post(copy part)
    getopt
  • 原文地址:https://www.cnblogs.com/astonc/p/10846561.html
Copyright © 2020-2023  润新知