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

Raphael Liu Lv10

给你一个整数数组 bloomDay,以及两个整数 mk

现需要制作 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

提示:

  • bloomDay.length == n
  • 1 <= n <= 10^5
  • 1 <= bloomDay[i] <= 10^9
  • 1 <= m <= 10^6
  • 1 <= k <= n

方法一:二分查找

每束花需要 k 朵花,需要制作 m 束花,因此一共需要 k \times m 朵花。如果花园中的花朵数量少于 k \times m,即数组 bloomDay 的长度小于 k \times m,则无法制作出指定数量的花束,返回 -1。如果数组 bloomDay 的长度大于或等于 k \times m,则一定可以制作出指定数量的花束。

为了计算制作出指定数量的花束的最少天数,首先需要实现一个辅助函数用于判断在给定的天数内能否制作出指定数量的花束,辅助函数的参数除了 bloomDay、m 和 k 之外,还有一个参数 days 表示指定天数。例如,当 bloomDay}=[1,10,3,10,2]、m=3、k=1 时,如果 days}=3 则辅助函数返回 true,如果 days}=2 则辅助函数返回 false。

对于辅助函数的实现,可以遍历数组 bloomDay,计算其中的长度为 k 且最大元素不超过 days 的不重合的连续子数组的数量,如果符合要求的不重合的连续子数组的数量大于或等于 m 则返回 true,否则返回 false。

当 days 很小的时候,辅助函数总是返回 false,因为天数太少不能收齐 m 个花束;当 days 很大的时候,辅助函数总是返回 true,如果给定序列可以制作出 m 个花束。在 days 慢慢变大的过程中,辅助函数的返回值会从 false 变成 true,所以我们可以认为这个辅助函数是关于 days 递增的,于是可以通过二分查找得到最少天数。在确保可以制作出指定数量的花束的情况下,所需的最少天数一定不会小于数组 bloomDay 中的最小值,一定不会大于数组 bloomDay 中的最大值,因此二分查找的初始值是 low 等于数组 bloomDay 中的最小值,high 等于数组 bloomDay 中的最大值。

当 low 和 high 的值相等时,二分查找结束,此时 low 的值即为最少天数。

[sol1-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public int minDays(int[] bloomDay, int m, int k) {
if (m > bloomDay.length / k) {
return -1;
}
int low = Integer.MAX_VALUE, high = 0;
int length = bloomDay.length;
for (int i = 0; i < length; i++) {
low = Math.min(low, bloomDay[i]);
high = Math.max(high, bloomDay[i]);
}
while (low < high) {
int days = (high - low) / 2 + low;
if (canMake(bloomDay, days, m, k)) {
high = days;
} else {
low = days + 1;
}
}
return low;
}

public boolean canMake(int[] bloomDay, int days, int m, int k) {
int bouquets = 0;
int flowers = 0;
int length = bloomDay.length;
for (int i = 0; i < length && bouquets < m; i++) {
if (bloomDay[i] <= days) {
flowers++;
if (flowers == k) {
bouquets++;
flowers = 0;
}
} else {
flowers = 0;
}
}
return bouquets >= m;
}
}
[sol1-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
int minDays(vector<int>& bloomDay, int m, int k) {
if (m > bloomDay.size() / k) {
return -1;
}
int low = INT_MAX, high = 0;
int length = bloomDay.size();
for (int i = 0; i < length; i++) {
low = min(low, bloomDay[i]);
high = max(high, bloomDay[i]);
}
while (low < high) {
int days = (high - low) / 2 + low;
if (canMake(bloomDay, days, m, k)) {
high = days;
} else {
low = days + 1;
}
}
return low;
}

bool canMake(vector<int>& bloomDay, int days, int m, int k) {
int bouquets = 0;
int flowers = 0;
int length = bloomDay.size();
for (int i = 0; i < length && bouquets < m; i++) {
if (bloomDay[i] <= days) {
flowers++;
if (flowers == k) {
bouquets++;
flowers = 0;
}
} else {
flowers = 0;
}
}
return bouquets >= m;
}
};
[sol1-C#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Solution {
public int MinDays(int[] bloomDay, int m, int k) {
if (m > bloomDay.Length / k) {
return -1;
}
int low = int.MaxValue, high = 0;
int length = bloomDay.Length;
for (int i = 0; i < length; i++) {
low = Math.Min(low, bloomDay[i]);
high = Math.Max(high, bloomDay[i]);
}
while (low < high) {
int days = (high - low) / 2 + low;
if (CanMake(bloomDay, days, m, k)) {
high = days;
} else {
low = days + 1;
}
}
return low;
}

public bool CanMake(int[] bloomDay, int days, int m, int k) {
int bouquets = 0;
int flowers = 0;
int length = bloomDay.Length;
for (int i = 0; i < length && bouquets < m; i++) {
if (bloomDay[i] <= days) {
flowers++;
if (flowers == k) {
bouquets++;
flowers = 0;
}
} else {
flowers = 0;
}
}
return bouquets >= m;
}
}
[sol1-JavaScript]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var minDays = function(bloomDay, m, k) {
if (m > bloomDay.length / k) {
return -1;
}
let low = Math.min.apply(null, bloomDay), high = Math.max.apply(null, bloomDay);
while (low < high) {
const days = Math.floor((high - low) / 2) + low;
if (canMake(bloomDay, days, m, k)) {
high = days;
} else {
low = days + 1;
}
}
return low;
};

const canMake = (bloomDay, days, m, k) => {
let bouquets = 0;
let flowers = 0;
const length = bloomDay.length;
for (let i = 0; i < length && bouquets < m; i++) {
if (bloomDay[i] <= days) {
flowers++;
if (flowers == k) {
bouquets++;
flowers = 0;
}
} else {
flowers = 0;
}
}
return bouquets >= m;
}
[sol1-Golang]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func minDays(bloomDay []int, m, k int) int {
if m > len(bloomDay)/k {
return -1
}
minDay, maxDay := math.MaxInt32, 0
for _, day := range bloomDay {
if day < minDay {
minDay = day
}
if day > maxDay {
maxDay = day
}
}
return minDay + sort.Search(maxDay-minDay, func(days int) bool {
days += minDay
flowers, bouquets := 0, 0
for _, d := range bloomDay {
if d > days {
flowers = 0
} else {
flowers++
if flowers == k {
bouquets++
flowers = 0
}
}
}
return bouquets >= m
})
}
[sol1-Python3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution:
def minDays(self, bloomDay: List[int], m: int, k: int) -> int:
if m > len(bloomDay) / k:
return -1

def canMake(days: int) -> bool:
bouquets = flowers = 0
for bloom in bloomDay:
if bloom <= days:
flowers += 1
if flowers == k:
bouquets += 1
if bouquets == m:
break
flowers = 0
else:
flowers = 0
return bouquets == m

low, high = min(bloomDay), max(bloomDay)
while low < high:
days = (low + high) // 2
if canMake(days):
high = days
else:
low = days + 1
return low
[sol1-C]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
bool canMake(int* bloomDay, int bloomDaySize, int days, int m, int k) {
int bouquets = 0;
int flowers = 0;
int length = bloomDaySize;
for (int i = 0; i < length && bouquets < m; i++) {
if (bloomDay[i] <= days) {
flowers++;
if (flowers == k) {
bouquets++;
flowers = 0;
}
} else {
flowers = 0;
}
}
return bouquets >= m;
}

int minDays(int* bloomDay, int bloomDaySize, int m, int k) {
if (m > bloomDaySize / k) {
return -1;
}
int low = INT_MAX, high = 0;
for (int i = 0; i < bloomDaySize; i++) {
low = fmin(low, bloomDay[i]);
high = fmax(high, bloomDay[i]);
}
while (low < high) {
int days = (high - low) / 2 + low;
if (canMake(bloomDay, bloomDaySize, days, m, k)) {
high = days;
} else {
low = days + 1;
}
}
return low;
}

复杂度分析

  • 时间复杂度:O(n \log (\textit{high} - \textit{low})),其中 n 是数组 bloomDay 的长度,high 和 low 分别是数组 bloomDay 中的最大值和最小值。
    需要遍历数组 bloomDay 得到其中的最大值 high 和最小值 low,遍历的时间复杂度是 O(n)。
    得到最大值 high 和最小值 low 之后,二分查找的迭代次数是 O(\log (\textit{high} - \textit{low})),每次判断是否能制作规定数量的花束的时间复杂度是 O(n),因此二分查找的总时间复杂度是 O(n \log (\textit{high} - \textit{low}))。
    整个算法的时间复杂度是 O(n)+O(n \log (\textit{high} - \textit{low}))=O(n \log (\textit{high} - \textit{low}))。

  • 空间复杂度:O(1)。

 Comments
On this page
1482-制作 m 束花所需的最少天数