Minimum Number of Days to Make m Bouquets: Binary Search on Answer
You are given an integer array bloomDay of length n, along with two integers m (number of bouquets) and k (flowers per bouquet). You need to make m bouquets, and each bouquet requires k adjacent flowers from the garden. Each flower blooms on the day specified in bloomDay. Return the minimum number of days you need to wait to make m bouquets. If it is impossible, return -1.
This is LeetCode 1482: Minimum Number of Days to Make m Bouquets, and it is one of the cleanest examples of the binary search on the answer pattern. If you have solved Koko Eating Bananas or Capacity to Ship Packages, you already know the template. This problem is the same skeleton with a different feasibility check.
Why this problem matters
Binary search on the answer is one of the most frequently tested patterns in interviews, and this problem is a textbook application. The trick is recognizing that you are not searching through an array. You are searching through a range of candidate answers (possible days), and for each candidate you check whether it is feasible. Once you internalize this pattern, problems like "minimize the maximum," "find the minimum valid X," and "what is the earliest day" all collapse into the same template.
This problem also reinforces a key sub-skill: writing a clean feasibility function. The feasibility check here requires scanning an array for consecutive runs, which is a useful building block on its own.
The key insight
If you can make m bouquets by day d, you can definitely make them by day d + 1 (more flowers have bloomed, so you have at least as many options). If you cannot make them by day d, you also cannot make them by day d - 1. This monotonic property means the answer space splits neatly into two regions:
- Infeasible: days 1 through
answer - 1 - Feasible: days
answerthroughmax(bloomDay)
Binary search finds the boundary between these two regions in O(log D) steps, where D is the range of bloom days.
The solution
def min_days(bloom_day, m, k):
if m * k > len(bloom_day):
return -1
lo, hi = min(bloom_day), max(bloom_day)
while lo < hi:
mid = lo + (hi - lo) // 2
if can_make(bloom_day, mid, m, k):
hi = mid
else:
lo = mid + 1
return lo
def can_make(bloom_day, day, m, k):
bouquets = 0
consecutive = 0
for d in bloom_day:
if d <= day:
consecutive += 1
if consecutive == k:
bouquets += 1
consecutive = 0
else:
consecutive = 0
return bouquets >= m
Let's walk through what each piece does.
The first check, m * k > len(bloom_day), handles the impossible case up front. If you need more total flowers than exist in the garden, no amount of waiting will help.
The binary search bounds are min(bloom_day) to max(bloom_day). No flower blooms before the minimum day, and by the maximum day every flower has bloomed. The answer must lie within this range.
The main loop is the standard "find the leftmost feasible" template. If the current day works, it might be the answer, but a smaller day might also work, so you set hi = mid. If it does not work, you need a later day, so you set lo = mid + 1.
The can_make function is the feasibility check. It scans the array left to right, counting consecutive bloomed flowers. Every time the count reaches k, one bouquet is formed and the counter resets. If we collect enough bouquets, the day is feasible.
The feasibility check uses a greedy scan: grab bouquets from left to right as soon as you can. This greedy approach is optimal because using flowers earlier in the array never makes it harder to form bouquets later. Each bouquet requires adjacent flowers, so there is no benefit in skipping a valid group.
Visual walkthrough
Let's trace through bloomDay = [1, 3, 2, 5, 4, 3], m = 2, k = 2. The search range is [1, 5]. We need 2 bouquets, each requiring 2 consecutive bloomed flowers.
Step 1: lo=1, hi=5, mid=3. Check day 3.
Day 3: flowers at indices 0, 1, 2, 5 have bloomed. Consecutive runs: 3 and 1. floor(3/2) = 1 bouquet. Need 2. Not feasible. Set lo = 4.
Step 2: lo=4, hi=5, mid=4. Check day 4.
Day 4: flowers at indices 0, 1, 2, 4, 5 have bloomed. Runs: 3 and 2. floor(3/2) + floor(2/2) = 1 + 1 = 2 bouquets. Feasible! Set hi = 4.
Step 3: lo=4, hi=4. lo equals hi. Answer: day 4.
Search complete. The minimum number of days to make 2 bouquets of 2 consecutive flowers is 4.
In just three steps, the binary search narrows the range from [1, 5] down to a single answer: day 4. At day 3, only one bouquet can be formed (not enough). At day 4, two bouquets are possible. The greedy scan picks them up from consecutive runs of bloomed flowers.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Binary search on answer | O(n log D) | O(1) |
Time is O(n log D), where n is the length of bloomDay and D is the difference between the maximum and minimum bloom days. The binary search runs O(log D) iterations, and each iteration calls the feasibility check, which scans the entire array in O(n).
Space is O(1). The feasibility check uses only a few variables. No extra data structures are needed.
The building blocks
1. Binary search on the answer
lo, hi = min(bloom_day), max(bloom_day)
while lo < hi:
mid = lo + (hi - lo) // 2
if is_feasible(mid):
hi = mid
else:
lo = mid + 1
return lo
This is the exact same template you see in Koko Eating Bananas, Capacity to Ship Packages, and Split Array Largest Sum. The only thing that changes between problems is the feasibility function and the bounds. Once you can write this skeleton from memory, you are halfway to solving any binary search on answer problem.
2. Greedy consecutive-run counting
bouquets = 0
consecutive = 0
for d in bloom_day:
if d <= day:
consecutive += 1
if consecutive == k:
bouquets += 1
consecutive = 0
else:
consecutive = 0
This is the feasibility check. It walks through the array once, tracking consecutive bloomed flowers. When you hit k in a row, you have one bouquet and reset the counter. The reset is critical: those k flowers are "used up" and cannot contribute to another bouquet. This greedy left-to-right scan is correct because adjacency is required, so there is no way to rearrange flowers to get a better result.
Edge cases
- Impossible case: if
m * kexceedsn(the array length), return-1immediately. There are not enough flowers in the garden. - All same bloom day: every flower blooms on the same day. The answer is that day (or
-1ifm * k > n). k = 1: every bloomed flower is its own bouquet. The feasibility check simplifies to "are at leastmflowers bloomed by dayd?" The answer is the m-th smallest value in the array.- Single flower:
n = 1,m = 1,k = 1. The answer isbloomDay[0]. - Tight fit:
m * kequalsnexactly. Every flower must be used, and they must formmgroups ofkconsecutive bloomed flowers. The answer ismax(bloomDay)because you need every single flower.
From understanding to recall
You have seen how binary search on the answer works for this problem: define the search range, write a greedy feasibility check, and binary search for the minimum feasible day. The logic is clean and the code is short.
But the details matter under pressure. Is it lo < hi or lo <= hi? Do you set hi = mid or hi = mid - 1? Do you reset consecutive to 0 or to 1 when you form a bouquet? These are small decisions that are easy to get wrong if you have not drilled them.
Spaced repetition closes the gap between understanding and recall. You write the binary search skeleton and the feasibility check from scratch today, then again tomorrow, then in three days. After a few rounds, you see "minimum number of days" in a problem and the entire template flows out without hesitation.
Related posts
- Binary Search - The foundational binary search pattern
- Koko Eating Bananas - Another classic binary search on the answer problem
- Capacity To Ship Packages Within D Days - Same binary search template applied to shipping
CodeBricks breaks this problem into its binary search skeleton and greedy feasibility check, then drills them independently with spaced repetition. You type each piece from scratch until it becomes automatic. When a "find the minimum day" problem shows up in your interview, you do not pause to think about the template. You just write it.