二分专题:1482. 制作 m 束花所需的最少天数

192 阅读3分钟

1482. 制作 m 束花所需的最少天数

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述

珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。

珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。  

珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。

返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。

具体题目链接: 题目链接

思路:

这类题的感觉就是:

  • 满足条件的最小或者满足条件的最大,求满足某种条件的最值,这种题能看出二分
  • 答案求的是最小天数 也就是说比答案大的都能满足 比答案小的都不能满足,具有二段性
  • 找二分的上下限 问题问的是一个数字(根据不同的题意,可以是天数,次数等等),也就是求满足条件的最小值或者满足条件的最大值

这个数字 对应的一个check函数的参数,也就是具体的问题,会产生true或者false的情况,同时数字也是具有上下限(也就是left和right)的,可以考虑二分查找,找到一个中间值mid(也就是问题问的这个数字)

1.这个check函数就是判断 mid这个天数能不能完成题意制作m朵花,而且还需要k个相邻都满足

2.具体的check函数怎么设计:看细节

分析:

枚举所有的开花日期,看看能不能满足题意。

从暴力的想法出发,再考虑时间复杂度,进行优化,考虑二分查找,先考虑上下限

1.对于最短的天数,他想找的答案一定在花朵盛开的时间表中

  • 因此他将左边界定义为了数组中的最小值
  • 右边界定义为了数组的最大值

关于mid最小天数的选择

每一次选择其中间值mid,然后判断在这个天数里会有哪些花开,以及这么多花可以制作多少花束。 如果可制作花束少于要求 m 朵, 则他将 left调整指 mid + 1 的天数位置,使其中间值mid变大可以开更多的花,从而能做更多的花束达到 m 朵的要求。 如果可制作花束过多或者刚好等于 m 朵,则他将 right调整指 mid的天数位置, 使其中间值mid变小求需要的最小的天数。

细节:

1.数组中的花朵不够用来制作花束的, 直接返回 -1

2.check函数里面的选择

必须是连续的花朵, 这个可以通过变量 count 来计数是否连续, 一旦不连续就重置为 0 每满足一次连续的 k 朵花, 就可以制作一束花, count 计数重新为 1 开始计数

代码:

var minDays = function(bloomDay, m, k) {
    let n = bloomDay.length;
    //特判一下,这种情况不存在
    if(n < m * k) return -1;

    // check函数用来判断在给定 mid 天的情况,能制作多少朵花,与题目要求的 m 朵花做对比
    function check(mid) {
        let flower = 0;//变量为 在mid天的情况下,能制作多少朵花
        let count = 0;//统计连续的花朵满足开花天数条件下使用花园中相邻的 k 朵花 才使得flower加1

        for(let i=0;i<n;i++) {//遍历开花的日期
            if(bloomDay[i] <= mid) {//说明日期可以摘
                count++;
                if(count == k) {
                    flower++;
                    count = 0;
                }
            } else {//当前数组的需要的最小天数>= mid,就需要暂时不算,说明日期可以摘
                count = 0;
            }
        }
        return flower;
    }

    let left = Math.min(...bloomDay);
    let right = Math.max(...bloomDay);

    while(left < right) {
        //假设尝试 mid 作为最小的天数, 看看能不能制作要求的花束,也就是假设选择等待的最少的天数需要mid天,如果满足条件就去找更少的天数,不满足的话就去找更多的天数
        let mid = Math.floor(left + (right - left)/2);
        // check函数用来判断在给定 mid 天的情况,能制作多少朵花,与题目要求的 m 朵花做对比,上下限分别是left和right,题目要求最小天数,那就是最小满足的天数 和 不满足的天数的边界值
        if(check(mid) >= m) {// m就是题目target,就是比较的对象
            // 如果可以制作花束, 那么可以尝试天数再减小一点, 因此right往左走, 缩小搜索范围
            right = mid;
        }else {
            // 如果无法制作花束, 那么可以尝试天数再增大一点, 因此left往右边走, 区间的左边值加大
            left = mid+1;
        }
    }
    return left;
};
复制代码

最后

这是「二分专题」系列文章的第 No.2 篇题解
也是算法系列文章的No.4 篇题解

大佬们多指正。