题目描述
给你一个整数数组 bloomDay,以及两个整数 m 和 k 。
现需要制作 m 束花。制作花束时,需要使用花园中 相邻的 k 朵花 。
花园中有 n 朵花,第 i 朵花会在 bloomDay[i] 时盛开,恰好 可以用于 一束 花中。
请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -1 。
示例1:
输入: bloomDay = [1,10,3,10,2], m = 3, k = 1
输出: 3
解释: 让我们一起观察这三天的花开过程,x 表示花开,而 _ 表示花还未开。
现在需要制作 3 束花,每束只需要 1 朵。
1 天后:[x, _, _, _, _] // 只能制作 1 束花
2 天后:[x, _, _, _, x] // 只能制作 2 束花
3 天后:[x, _, x, _, x] // 可以制作 3 束花,答案为 3
示例2:
输入: bloomDay = [1,10,3,10,2], m = 3, k = 2
输出: -1
解释: 要制作 3 束花,每束需要 2 朵花,也就是一共需要 6 朵花。而花园中只有 5 朵花,无法满足制作要求,返回 -1 。
示例3:
输入: bloomDay = [7,7,7,7,12,7,7], m = 2, k = 3
输出: 12
解释: 要制作 2 束花,每束需要 3 朵。
花园在 7 天后和 12 天后的情况如下:
7 天后:[x, x, x, x, _, x, x]
可以用前 3 朵盛开的花制作第一束花。但不能使用后 3 朵盛开的花,因为它们不相邻。
12 天后:[x, x, x, x, x, x, x]
显然,我们可以用不同的方式制作两束花。
示例4:
输入: bloomDay = [1000000000,1000000000], m = 1, k = 1
输出: 1000000000
解释: 需要等 1000000000 天才能采到花来制作花束
示例5:
输入: bloomDay = [1,10,2,9,3,8,4,7,5,6], m = 4, k = 2
输出: 9
提示
- b l o o m D a y . l e n g t h = = n bloomDay.length == n bloomDay.length==n
- 1 < = n < = 1 0 5 1 <= n <= 10^5 1<=n<=105
- 1 < = b l o o m D a y [ i ] < = 1 0 9 1 <= bloomDay[i] <= 10^9 1<=bloomDay[i]<=109
- 1 < = m < = 1 0 6 1 <= m <= 10^6 1<=m<=106
- 1 < = k < = n 1 <= k <= n 1<=k<=n
方法:二分查找
解题思路
由题意得,当需要的天数越多时,则制作的花束就越少;当需要的天数越少时,则制作的花束就越多。因此,本题具有单调性,可以利用二分查找来解题。
首先我们把特别情况直接排除在外:一共要制作 m m m 束花,每束花需要 k k k 朵,如果 m ∗ k > b l o o m D a y . l e n g t h m * k > bloomDay.length m∗k>bloomDay.length,则无法满足制作要求,返回 -1。
二分查找的左边界为数组中的最小值,右边界为数组中的最大值,因为要保证制作出来的花束的数量。
判断
m
i
d
mid
mid 是否符合条件需要辅助函数
c
h
e
c
k
check
check:
使用两个指针
i
i
i 和
j
j
j 遍历数组,
i
i
i 和
j
j
j 指针一前一后构成了滑动窗口,先定位两个指针,然后判断
[
i
,
j
]
[i,j]
[i,j] 区间内的元素是否小于等于
m
i
d
mid
mid:
- 如果区间内有一个 b l o o m D a y [ i ] > m i d bloomDay[i] > mid bloomDay[i]>mid,则 i i i 指针往后移 1 位,对应的 j j j 指针也相对于 i i i 指针往后移 k − 1 k - 1 k−1 位;
- 如果区间内所有 b l o o m D a y [ i ] < = m i d bloomDay[i] <=mid bloomDay[i]<=mid,则 i i i 指针移到 j j j 指针,对应的 j j j 指针也向后移动 k k k 位,此时说明已成功制作 1 束花, c n t cnt cnt 加上 1。
最后,辅助函数 c h e c k check check 计算出在当前 m i d mid mid 天的情况下能制作出的总花数 c n t cnt cnt,如果 c n t > = m cnt >= m cnt>=m,则将二分查找区间缩小为 [ l , m i d ] [l,mid] [l,mid];如果 c n t < m cnt < m cnt<m,则将二分查找区间缩小为 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]。
当 l l l 和 r r r 相等时,二分查找结束,此时的 l l l 就是最少天数。
代码
class Solution {
public:
bool check(vector<int>& bloomDay, int mid, int m, int k) {
int cnt = 0;
int n = bloomDay.size();
for(int i = 0, j = k - 1; i < n && j < n; )
{
while(i <= j)
{
if(bloomDay[i] > mid)
break;
i++;
}
if(i > j) i = j, j += k, cnt++;
else j = i + k, i++;
}
return cnt >= m;
}
int minDays(vector<int>& bloomDay, int m, int k) {
int n = bloomDay.size();
if(m * k > n) return -1;
int l = *min_element(bloomDay.begin(), bloomDay.end()),
r = *max_element(bloomDay.begin(), bloomDay.end()), mid;
while(l < r)
{
mid = (l + r) >> 1;
if(check(bloomDay, mid, m, k))
r = mid;
else
l = mid + 1;
}
return l;
}
};
复杂度分析
- 时间复杂度: O ( n l o g C ) O(nlogC) O(nlogC)。 C C C 为数组元素中的最大值与最小值的差。二分查找的时间复杂度为 O ( l o g C ) O(logC) O(logC),每一轮都需要遍历数组的时间复杂度为 O ( n ) O(n) O(n)。因此,整体时间复杂度为 O ( n l o g C ) O(nlogC) O(nlogC)。
- 空间复杂度: O ( 1 ) O(1) O(1)。