题目是LeetCode第193场周赛的第三题,链接:1482. 制作 m 束花所需的最少天数。具体描述为:给你一个整数数组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
先给出一种无脑模拟的方法,需要按照花的盛开日期排个序,然后直接从第m*k
朵花盛开的那天开始,判断是否能制作m
束花,是的话可以结束返回,否则进入下一朵花。时间复杂度为 O ( n 2 ) O(n^{2}) O(n2),空间复杂度为 O ( n ) O(n) O(n)。
JAVA版代码如下:
class Solution {
private int flower(boolean[] bloom, int k) {
int count = 0;
int prevIdx = 0;
boolean prevBloom = bloom[0];
for (int i = 1; i < bloom.length; ++i) {
if (bloom[i]) {
if (!prevBloom) {
prevIdx = i;
prevBloom = true;
}
}
else {
if (prevBloom) {
count += (i - prevIdx) / k;
prevBloom = false;
}
}
}
if (prevBloom) {
count += (bloom.length - prevIdx) / k;
}
return count;
}
private int moreFlower(boolean[] bloom, int k, int idx) {
int count = 0;
int left = 0, right = 0;
if (idx == 0) {
int i = 1;
while (i < bloom.length && bloom[i]) {
++count;
++i;
}
right = count;
}
else if (idx == bloom.length - 1) {
int i = idx - 1;
while (i >= 0 && bloom[i]) {
++count;
--i;
}
left = count;
}
else {
int i = idx + 1;
while (i < bloom.length && bloom[i]) {
++count;
++i;
}
right = count;
count = 0;
i = idx - 1;
while (i >= 0 && bloom[i]) {
++count;
--i;
}
left = count;
}
if (left == 0) {
if ((right + 1) % k == 0) {
return 1;
}
return 0;
}
else if (right == 0) {
if ((left + 1) % k == 0) {
return 1;
}
return 0;
}
else {
return (left + right + 1) / k - left / k - right / k;
}
}
public int minDays(int[] bloomDay, int m, int k) {
int n = bloomDay.length;
if (m * k > n) {
return -1;
}
DayAndIdx[] dai = new DayAndIdx[n];
for (int i = 0; i < n; ++i) {
dai[i] = new DayAndIdx(bloomDay[i], i);
}
Arrays.sort(dai, new Comparator() {
@Override
public int compare(DayAndIdx d1, DayAndIdx d2) {
return d1.day - d2.day;
}
});
boolean[] bloom = new boolean[n];
for (int i = 0; i < m * k; ++i) {
bloom[dai[i].idx] = true;
}
int count = flower(bloom, k);
if (count >= m) {
return dai[m * k - 1].day;
}
for (int i = m * k; i < n; ++i) {
bloom[dai[i].idx] = true;
count += moreFlower(bloom, k, dai[i].idx);
if (count >= m) {
return dai[i].day;
}
}
return -1;
}
}
class DayAndIdx {
int day;
int idx;
public DayAndIdx(int d, int i) {
day = d;
idx = i;
}
}
提交结果如下:
接着是二分法,二分的对象是天数,初始化最小天数left
为1
,最大天数right
为max(bloomDay)
,接着在left
和right
之间不断二分middle=(left+right)/2
,当找到某个天数middle
满足可以获得k
束花的时候缩小区间right=middle
,否则left=middle+1
,最终的结果就是right
。时间复杂度为 O ( n l o g m ) O(nlogm) O(nlogm)(m
为最大天数),空间复杂度为 O ( 1 ) O(1) O(1)。
JAVA版代码如下:
class Solution {
private Set set;
private boolean isValid(int[] bloomDay, int day, int m, int k) {
int count = 0;
int curSum = 0;
for (int bd : bloomDay) {
if (bd <= day) {
++curSum;
}
else {
curSum = 0;
}
if (curSum >= k) {
if (++count >= m) {
break;
}
curSum = 0;
}
}
return count >= m;
}
public int minDays(int[] bloomDay, int m, int k) {
int n = bloomDay.length;
if (m * k > n) {
return -1;
}
int maxDay = 0;
for (int bd : bloomDay) {
maxDay = Math.max(maxDay, bd);
}
int left = 1, right = maxDay;
while (left < right) {
int middle = left + (right - left) / 2;
if (isValid(bloomDay, middle, m, k)) {
right = middle;
}
else {
left = middle + 1;
}
}
return right;
}
}
提交结果如下:
Python版代码如下:
class Solution:
def minDays(self, bloomDay: List[int], m: int, k: int) -> int:
if m * k > len(bloomDay):
return -1
def isValid(day):
count = 0
cursum = 0
for bd in bloomDay:
if bd <= day:
cursum += 1
else:
cursum = 0
if cursum == k:
count += 1
cursum = 0
if count >= m:
break
return count >= m
left, right = 0, max(bloomDay)
while left < right:
middle = (left + right) // 2
if isValid(middle):
right = middle
else:
left = middle + 1
return right
提交结果如下: