美文网首页
二分法问题合集

二分法问题合集

作者: 恰似一碗咸鱼粥 | 来源:发表于2019-01-18 14:38 被阅读0次

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

    POJ 1064

    #include <iostream>
    #include <vector>
    #define INF 10000
    using namespace std;
    int N, K;
    vector<double> ropes;
    
    bool C(double x) {
        int num = 0;
        for (int i = 0; i < N; i++) {
            num += (int)(ropes[i] / x);
        }
        return num >= K;
    }
    
    void slove() {
        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_s("%.2f\n", floor(ub * 100) / 100);
    }
    
    int main()
    {
        cin >> N >> K;
        double length;
        for (int i = 0; i < N; i++) {
            cin >> length;
            ropes.push_back(length);
        }
        slove();
        return 0;
    }
    

    二分法的结束判定:
    在输出小数的问题中,一般会指定输出中小数点后面的位数,所以有必要设置合理的结束条件来控制精度,上面的题中达到了10^{-30}的精度范围,也可以设置类似ub-lb>EPS这样的条件。

    2.最大化最小值(二分+贪心)

    POJ 2456

    N间小屋,M头牛,使得牛跟牛之间的距离最远,以防止牛打架。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #define MAX_N 100001
    #define INF 20000
    using namespace std;
    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() {
        int lb = 0, ub = INF;
        while (ub - lb > 1) {
            int mid = (lb + ub) / 2;
            if (C(mid)) {
                lb = mid;
            }
            else {
                ub = mid;
            }
        }
        printf("%d\n", lb);
    }
    
    int main()
    {
        scanf("%d %d", &N, &M);
        for (int i = 0; i < N; ++i) {
            scanf("%d", &x[i]);
        }
        sort(x, x + N );
        solve();
        return 0;
    }
    

    不断缩小可用值的范围,选取最大的值,贪心法+二分法

    POJ 3258

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <cstdio>
    #define Max_N 50000
    using namespace std;
    
    int L, N, M;
    vector<int> stones(Max_N);
    
    bool C(int mid) {
        int last = 0;
        for (int i = 1; i < N +2 - M; ++i) {
            int current = last + 1;
            while (current<N+2&&stones[current]-stones[last]<mid)
            {
                ++current;
            }
            if (current == N+2) {
                return false;
            }
            last = current;
        }
        return true;
    }
    
    void solve() {
        sort(stones.begin(), stones.begin() + N+2);
        int lb = 0, ub = L+1;
        while (ub - lb > 1) {
            int mid = (ub + lb) / 2;
            if (C(mid)) {
                lb = mid;
            }
            else {
                ub = mid;
            }
        }
        printf("%d\n", lb);
    }
    
    int main()
    {
        scanf("%d %d %d", &L, &N, &M);
        for (int i = 1; i <= N; i++) {
            scanf("%d", &stones[i]);
        }
        stones[0] = 0;
        stones[N+1] = L;
        solve();
        return 0;
    }
    

    POJ 3273

    #include <iostream>
    #include <cstdio>
    #define Max_N 100000
    using namespace std;
    int Days[Max_N];
    int N, M;
    
    bool C(int mid) {
        int pig = 0;
        int t = 0;
        for (int i = 0; i < N; ) {
            t++;
            while ((i<N)&&(pig + Days[i] <= mid)) {
                pig += Days[i];
                i++;
            }
            pig = 0;
            if (t > M ) {
                return false;
            }
        }
        return true;
    }
    
    void solve() {
        int lb = 0, ub = 50000;
        while (ub - lb > 1) {
            int mid = (ub + lb) / 2;
            if (C(mid)) {
                ub = mid;
            }
            else {
                lb = mid;
            }
        }
        printf("%d\n", ub);
    }
    
    int main()
    {
        scanf("%d %d", &N, &M);
        for (int i = 0; i < N; ++i) {
            scanf("%d", &Days[i]);
        }
        solve();
        return 0;
    }
    

    POJ 3104

    #include <iostream>
    #define Max_N 10000000
    #define INF 1000000000
    using namespace std;
    
    int n, k;
    int a[Max_N];
    
    //mid为时间
    bool C(long long mid) {
        int wind = 0;
        for (int i = 0; i < n; ++i) {
            if (a[i] > mid) {
                int t = 0;
                do {
                    t++;
                    wind++;
                } while (mid+t*(k-1)<a[i]&&(wind<=mid));
                if (wind > mid)
                    return false;
            }
        }
        return true;
    }
    
    void solve() {
        long long lb = 1, ub = INF;
        while (ub - lb > 1) {
            long long mid = (ub + lb) / 2;
            if (C(mid)) {
                ub = mid;
            }
            else {
                lb = mid;
            }
        }
        cout << ub << endl;
    }
    
    int main()
    {
        cin >> n;
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
        }
        cin >> k;
        solve();
        return 0;
    }
    

    3.最大化平均值

    有n个物品的重量和价值分别是WiVi。从中选出k个物品使单位重量的价值最大。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #define Max_N 1000000
    #define INF 10010
    using namespace std;
    
    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] - w[i] * x;
        }
        sort(y, y + n);
    
        double sum = 0;
        //计算从大到小k个数的和,若差小于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\n", ub);
    }
    
    int main()
    {
        scanf("%d %d", &n, &k);
        for (int i = 0; i < n; ++i) {
            scanf("%d %d", &w[i], &v[i]);
        }
        solve();
        return 0;
    }
    

    一般先想到对物品按照单位价值排序,但是根据样例,这是不可行的。
    解决方法变为\sum_{i=0}^n {Vi}/\sum_{i=0}^n {Wi}>=x,其中x为最大的平均单位价值,化简得\sum_{i=0}^n {(Vi-x*Wi)}>=0,因此对这个式子的值进行贪心的由大到小选取k个。

    相关文章

      网友评论

          本文标题:二分法问题合集

          本文链接:https://www.haomeiwen.com/subject/cjjndqtx.html