• 折半枚举(双向搜索)


    1.

      

     

      从 4 个数列中选择的话总共有 n4 种情况,所以全都判断一遍不可行。不过将它们对半分成 AB 和 CD 再考虑,就可以快速解决了。从两个数列中选择的话只有 n2 种组合,所以可以进行枚举。先从 A、B中取出 a、b 后,为了使总和为 0 则需要从 C、D中取出 c + d =  - a - b。因此先将从 C、D 中取数字的 n2 种方法全部都枚举出来,将这些和排好序,这样就可以运用二分搜索。这个算法的复杂度是 O(n2 log n)。

      有时候,问题的规模比较大,无法枚举所有元素的组合,但能够枚举一半元素的组合。此时,讲问题拆分两半后分别枚举,再合并它们的结果这一方法往往非常有效。

    int n;
    int A[MAX_N], B[MAX_N], C[MAX_N], D[MAX_N];
    
    int CD[MAX_N * MAX_N]; // C和D中数字的组合方法
    
    void solve() {
        //枚举 C和D中取出数字的所有方法
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                CD[i * n + j] = C[i] + D[j];
        sort(CD, CD + n * n);
        
        long long res = 0;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++) {
                int cd = -(A[i] + B[j]);
                res += upper_bound(CD, CD + n * n, cd) - lower_bound(CD, CD + n * n, cd);
            }
        printf("%lld
    ", res);
    } 

     2.

      

      

      这个问题显然是背包问题。不过这次价值和重量都非常大,相比之下 n 比较小。使用 DP 求解背包问题的复杂度为 O(n W),因此不能用来解决这里的问题。此时我们应该利用 n 比较小的特点来寻找其他办法。

      挑选物品的方法总共有 2n 种,所有不能直接枚举,但是像前面一样拆成两半之后再枚举的话,因为每个部分只有 20 个所以是可行的。利用拆成两半后的两部分的价值和重量,我们能求处原先的问题吗?我们把前半部分中的选取方法对应的重量和价值总和记为 w1、v1.这样在后半部分寻找总重 w2 <=  W - w1 时使 v2 最大的选取方法就好了。

      因此,要思考从枚举得到的(w2,v2)的集合中高效寻找 max{v2 | w2 ≤ W' } 的方法。首先,显然我们可以排除所有 w2[ i ] ≤ w2[ j ] 并且 v2[ i ] ≥ v2[ j ] 的 j。这一点可以按照 w2、v2 的字典序排序后简单做到。此后剩余的元素都满足 w2[ i ] < w2[ j ] <=> v2[ i ] < v2[ j ],要计算 max{ v2 | w2 ≤ W' }的话,只需要寻求满足 w2[ i ] ≤ W' 的最大的 i 就可以了。这可以用二分搜索完成,剩余的元素个数为 M 的话,一次搜索需要 O(log M)的时间。因为 M ≤ 2(n / 2),所以这个算法的复杂度是O(2(n / 2)n)。

    typedef long long ll;
    
    int n;
    ll w[MAX_N], v[MAX_N];
    ll W;
    
    pair<ll, ll> pa[1 << (MAX_N / 2)];  // (重量,价值)
    
    void solve() {
        //枚举前半部分
        int n2 = n / 2;
        for (int i = 0; i < 1 << n2; i++) {
            ll sw = 0, sv = 0;
            for (int j = 0; j < n2; j++)
                if (i >> j & 1) {
                    sw += w[j];
                    sv += v[j];
                }
            ps[i] = make_pair(sw, sv);
        } 
        // 去除多余元素
        sort(ps, ps + (1 << n2));
        int m = 1;
        for (int i = 1; i < 1 << n2; i++)
            if (ps[m - 1].second < ps[i].second) 
                ps[m++] = ps[i];
        // 枚举后半部分并求解
        ll res = 0;
        for (int i = 0; i < 1 << (n - n2); i++) {
             ll sw = 0, sv = 0;
             for (int j = 0; j < n - n2; j++)
                 if (i >> j & 1) {
                     sw += w[n2 + j];
                     sv += v[n2 + j];
                }
            if (sw <= W) {
                ll tv = (lower_bound(ps, ps + m, make_pair(W - sw, INF)) - 1)->second;
                res = max(res, sv + tv);
            }
        }
        printf("%lld
    ", res); 
    } 

    枚举方面:

    #include<iostream>
    #include<algorithm>
    using namespace std;
    int main() {
        for (int i = 0; i < 1 << 4; i++)
            for (int j = 0; j < 4; j++)
                printf("%d%c", i >> j & 1, j == 3 ? '
    ' : ' ');
        return 0;
        
    }

  • 相关阅读:
    Android UI开发第十四篇——可以移动的悬浮框
    Android UI开发第八篇——ViewFlipper 左右滑动效果
    Android: Trusting SSL certificates
    Customize Android Fonts
    Android UI开发第三篇——popupwindow
    Android UI开发第四篇——实现像handcent sms或者chomp sms那样的气泡短信样式
    Android UI开发第一篇——android的九宫格式实现
    Android UI开发第十一篇——右上角带个泡泡
    Android开发之系统信息——获取Android手机中SD卡内存信息
    Android自动测试之MonkeyRunner之MonkeyImage
  • 原文地址:https://www.cnblogs.com/astonc/p/10859302.html
Copyright © 2020-2023  润新知