#LC1011、LC875、 LC1723、LC1482:二分查找

以前对二分查找的认识不够,更多的是知道折半排序,这几个题就很好的总结了二分查找。
他们的共同特点都是在一个范围内查找答案,可以根据当前查找结果判断去左区间还是右区间,二分的效率还是很高的。1e9的数据也就三十几次就解决了。

LC1011:在 D 天内送达包裹的能力

求最低运载能力的最大运载量,包裹是按顺序放上的,我们发现最大运载量的最小值是包裹的最大值,最大值不超过包裹的总量,就在这之间寻找答案,我们选定每个区间的mid作为最大运载量,看在这个限制下需要多少天day运完,如果day超过D,那说明我们的最大运载量太低了,就选择右区间继续,如果小于等于D,说明我们的运载量可以,那我们看看能不能在笑一些,就选择左区间,直到left>right.

其中对于每一个最大运载量计算需要多少天day,因为是顺序的,超过了就放到下一个就可以了。

class Solution {
public:
    int shipWithinDays(vector<int>& weights, int D) {
        int high = accumulate(weights.begin(), weights.end(), 0);
        int low = 0;
        for(int i : weights) low = max(low, i);
        while(low < high)
        {
            int mid = (low + high) / 2;
            //判断
            int cnt = 0;
            for(int i = 0; i < weights.size();)
            {
                int s = 0;
                while(i < weights.size() && s + weights[i] <= mid)
                {
                    s += weights[i];
                    i++;
                }
                cnt++;
            } 
            if(cnt <= D)
                high = mid;
            else low = mid + 1;
        }
        return low;
    }
};

LC875:爱吃香蕉的珂珂

一样的,这次求最小速度下的每次吃的最多数量
这次的边界是粗略的,不过没关系,差不了太多。也是一样的,选择一个最大吃的数量判断此情况下的最小速度,根据结果选择左区间还是右区间

class Solution {
public:
    int minEatingSpeed(vector<int>& piles, int h) {
        int low = 1;
        int high = 1e9;
        while(low < high)
        {
            int mid = (low + high) / 2;
            int cnt = 0;
            for(int i : piles)
                cnt += i / mid + (i % mid == 0 ? 0 : 1);
            if(cnt <= h)    high = mid;  //mid == 2 cnt < h but mid == 1 cnt > h
            else low  = mid + 1;
        }
        return low;
    }
};

LC1723:完成所有工作的最短时间

这个是昨天的每日一题,都是差不多的,求分配方案中尽可能最小最大工作时间,也是在一个范围内选择答案,但是这道题不同于上面两个的是他不是把东西顺序分配的,也就是说可以随便分配,所以加上dfs搜索+剪枝

剪枝
  • 边界剪枝:可以将最小值放到工作的最大值,最大值是所有工作之和
  • 优先分配工作量大的任务,一开始分配小任务量可能导致后面大的进不来,所以对工作量进行排序
  • 如果当前第i个工人 + 第j个工作正好就等于我们设定的最大工作时间而且后面工作的dfs失败,那就不用再处理后面的情况了,比如说当前第i个工人的工作时间是8,又加入第j个工作,时间是5正好达到最大工作时间13,后面处理j之后的工作发现dfs失败,本来应该将这个5放到j之后再看看能不能成功,但其实不用。你放到后面,那就意味着第j个工人再分配肯定是小于等于5的(总共13),既然j承担8 + 5都不能使后面dfs成功,那么8 + 小于等于5的肯定也不能使后面dfs成功。
  • 还是如果后面dfs失败而且当前第j个工人分配时间为0,那么你再把第j个工作调整到后面的工人也是没有用的,因为第j个工人和j后面之后的工人都是0,没有什么区别
class Solution {
public:
    bool dfs(int ma, int j, vector<int>& jobs, vector<int>& workload)
    {
        if(j == jobs.size())
            return true;
        for(int i = 0; i < workload.size(); i++)
        {
            if(workload[i] + jobs[j] <= ma)
            {
                workload[i] += jobs[j];
                if(dfs(ma, j + 1, jobs, workload))
                    return true;
                workload[i] -= jobs[j];
                //剪枝
                if(workload[i] == 0 || workload[i] + jobs[j] == ma)
                    break;
            }
        }
        //全都加不进去了而且还有工作没分配完
        return false;
    }
    int minimumTimeRequired(vector<int>& jobs, int k) {
        int right = accumulate(jobs.begin(), jobs.end(), 0);
        int left = 0, ans = right;
        sort(jobs.begin(), jobs.end(), greater<int>());
        left = jobs[0];
        while(left < right)
        {
            int mid = (left + right) / 2;
            vector<int> workload(k, 0);
            int tmp = 0;
            if(dfs(mid, 0, jobs, workload))
            {
                for(int i : workload)
                    tmp = max(tmp, i);
                ans = min(tmp, ans);
                right = mid;
            }
            else
                left = mid + 1;
        }
        return ans;
    }
};

LC1482:制作 m 束花所需的最少天数

祝我最爱的妈妈母亲节快乐 !
一开始想到了二分,但是觉得头铁先写个暴力,果然超时了,还是继续写二分

class Solution {
public:
    int minDays(vector<int>& bloomDay, int m, int k) {
        int n = bloomDay.size(), left = 1, right = 1e9;
        if(m * k > n)   return -1;
        while(left < right)
        {
            int mid = (left + right) / 2;
            int c = m, cnt = 0;
            for(int i = 0; i < n; i++)
            {
                if(bloomDay[i] <= mid)
                {
                    cnt++;
                    if(cnt >= k)
                    {
                        c--;
                        cnt = 0;
                    }
                    
                }
                else cnt = 0;
            }
            if(c > 0)
                    left  = mid + 1;
                else right = mid;
        }
        return left;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值