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 篇题解
大佬们多指正。