🔗 🟡 1482. Minimum Number of Days to Make m Bouquets 1946

題意

給定一個長度為 nn 的整數陣列 bloomDay\text{bloomDay},一個整數 mm 和一個整數 kk

花園中有 nn 朵花,第 ii 朵花將在 bloomDay[i]\text{bloomDay}[i] 開花。

現在需要製作 mm 個花束,製作一個花束時,需要使用花園中 相鄰kk 朵花。

返回從花園中製作 mm 個花束需要等待的最少天數。如果無法製作 mm 個花束則返回 1-1

約束條件

  • bloomDay.length=n\text{bloomDay.length} = n
  • 1n1051 \leq n \leq 10^5
  • 1bloomDay[i]1091 \leq \text{bloomDay}[i] \leq 10^9
  • 1m1061 \leq m \leq 10^6
  • 1kn1 \leq k \leq n

首先確定思路,顯然要直接計算出最少需要等待的天數是很困難的,但對於給定的天數 dd ,我們可以確定是否可以在 dd 天內製作 mm 個花束,因此可以使用 二分搜尋(Binary Search) 來找到最少需要等待的天數。

定義一個函數 check(d) 來檢查在待定天數 dd 內是否能製作出 mm 個花束,每束花需要使用相鄰的 kk 朵花。

  • 使用 resres 來記錄製作的花束數量,curcur 來記錄相鄰花的數量。
  • 遍歷 bloomDay\text{bloomDay} 陣列中的每個元素 bloom\text{bloom}
    • 如果 bloomd\text{bloom} \leq d,代表這朵花可以被使用,則 curcur 加一。如果 cur=kcur = k,代表有 kk 朵相鄰花可以製作花束,則 resres 加一,curcur 重置為 00
    • 如果 bloomDay[i]>d\text{bloomDay}[i] > d,則代表這朵花在 dd 天時還沒開花,由於使用的花必須是相鄰的,因此需要把相鄰花的數量重置為 00
  • 最後返回 resmres \geq m,即是否可以製作 mm 個花束。

這裡使用左閉右閉區間進行二分搜尋,令 left=min(bloomDay)left = \min(\text{bloomDay})right=max(bloomDay)right = \max(\text{bloomDay}),在 [left,right][left, right] 的範圍內進行二分搜尋,不斷調整左右範圍直到找到最小天數為止。

  • 根據定義的 check 函數,區間的左側為 False\text{False} ,而右側為 True\text{True}
  • 如果 check(mid)=True\text{check}(mid) = \text{True},則將右範圍縮小到 [left,mid1][left, mid-1]
  • 如果 check(mid)=False\text{check}(mid) = \text{False},則將左範圍擴大到 [mid+1,right][mid+1, right]
  • 當區間為空,即 left>rightleft > right 時,終止搜尋。

最終返回 leftleft,即第一個 check 函數返回 False\text{False} 的天數,如果 left>max(bloomDay)left > \max(\text{bloomDay}),則表示無法製作 mm 個花束,返回 1-1

複雜度分析

  • 時間複雜度:O(nlogM)\mathcal{O}(n \log M)。其中 n=bloomDayn = |\text{bloomDay}|M=max(bloomDay)M = \max(\text{bloomDay}),我們需要進行 logM\log M 次二分搜尋。每次搜尋需要遍歷 nn 個元素來檢查是否可以製作 mm 個花束。
  • 空間複雜度:O(1)\mathcal{O}(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
def minDays(self, bloomDay: List[int], m: int, k: int) -> int:

# check if we can make m bouquets with k flowers in d days
def check(d: int):
res = cur = 0 # res: number of bouquets, cur: number of adjacent flowers
for bloom in bloomDay:
if bloom <= d:
cur += 1
if cur == k:
res += 1
cur = 0
else: # not adjacent
cur = 0
return res >= m

left, right = min(bloomDay), max(bloomDay)
while left <= right:
mid = (left + right) // 2
if check(mid):
right = mid - 1
else:
left = mid + 1
return left if left <= max(bloomDay) else -1
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
class Solution {
public:
int minDays(vector<int>& bloomDay, int m, int k) {

// check if we can make m bouquets with k flowers in d days
auto check = [&](int d) {
int res = 0, cur = 0;
for (int bloom : bloomDay) {
if (bloom <= d) {
cur++;
if (cur == k) {
res++;
cur = 0;
}
} else { // not adjacent
cur = 0;
}
}
return res >= m;
};

int mx = INT_MIN, mn = INT_MAX;
for (int bloom : bloomDay) {
mx = max(mx, bloom);
mn = min(mn, bloom);
}
int left = mn, right = mx;
while (left <= right) {
int mid = left + (right - left) / 2;
if (check(mid)) {
right = mid - 1;
} else {
left = mid + 1;
}
}

return left <= mx ? left : -1;
}
};

寫在最後

Cover photo is generated by @たろたろ, thanks for their work!