二分答案的使用

什么时候用二分答案

题目要求一个解,通常是最优解,比如求满足某个条件下的最大值或最小值。
当求解比较困难时,可以思考是否能转换为判定问题,即给定一个解,判定它是否满足该条件。通常情况下,判定逻辑清晰明了,但带来的问题是需要枚举整个解空间,复杂度为 O ( n ) O(n) O(n)。如果发现解空间具有单调性,就可以将解空间一分为二,每次缩小 1 / 2 1/2 1/2,从而将时间复杂度从 O ( n ) O(n) O(n)降到 O ( l o g n ) O(logn) O(logn)。这就是通常说的"二分+判定",是复杂题目里进行算法优化的常用方法。

几个关键点

判定算法/函数:
给定一个解 x x x,判定是否满足题目的条件。一般判定算法会用到贪心,复杂度控制在 O ( n ) O(n) O(n)
解空间具有单调性:
如果判定函数 c h e c k ( x ) check(x) check(x)关于 x x x具有单调性,以 力扣1011为例,当运载能力 x x x ≥ X \ge X X能运完, < X <X <X运不完,我们就说解空间具有单调性。
判定问题的转化:判定是否满足题目的条件等价于写出满足题意的不等式。
二分:
当解空间具有单调性时,枚举 x x x时就可以用二分了。

例题

力扣1011
直接求解无从下手,转换为判定问题:当运载能力为 m i d mid mid时,是否能在days天内将货物运完。
解空间是否单调性:当 x ≥ m i d x\ge mid xmid时,肯定能运完,当 x < m i d x<mid x<mid时,肯定运不完,因此解空间单调。
1.写出判定函数:

    bool check(vector<int>& weights, int days, int cap) {
        int current=0;
        int day=1;
        for(auto weight:weights) {
            if(weight+current<=cap) { 
                current+=weight;
            }else {
                day++;
                current=weight;
            }
        }
        return day<=days;//判定是否满足题目的条件等价于写出满足题意的不等式
    }
  1. 套用二分模板
    2.1 将判定函数放到 i f if if条件判断.
    2.2 根据是求最大值还是最小值, i f if if里修改 l e f t left left r i g h t right right:
    最大值/前驱 ≤ \le l e f t = m i d left=mid left=mid
    最小值/后继 ≥ \ge r i g h t = m i d right=mid right=mid
    2.3 e l s e else else里写另一半:
    r i g h t = m i d + 1 right=mid+1 right=mid+1
    l e f t = m i d − 1 left=mid-1 left=mid1 //这种情况下 m i d = ( l e f t + r i g h t + 1 ) / 2 mid=(left+right+1)/2 mid=(left+right+1)/2
    int shipWithinDays(vector<int>& weights, int days) {
        int left=0,right=0;
        for(auto weight:weights) {
            left=max(left, weight);
            right+=weight;
        }
        while(left<right) {
            int mid=(left+right)/2;
            if(check(weights, days, mid)) { //最小值/后继
                right=mid;
            } else {
                left=mid+1;
            }
        }
        return right;
    }
  1. 无解情况可以加哨兵,如前驱时 l e f t = − 1 left=-1 left=1表示无解,后继时 r i g h t = n right=n right=n表示无解
  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值