Problem 1482

Abstract / Summary

Find the earliest day when you can pick m bouquets with k adjacent blooming flowers each.

Note

The full problem description, including helpful examples, can be found on LeetCode.

Python 1482.py

class Solution:
    def minDays(self, bloomDay: List[int], m: int, k: int) -> int:
        complete = [] # max(bloomDay[i : i + k])

        # compute complete[]
        last = [] # max-heap
        for right, b in enumerate(bloomDay):
            heappush(last, ( -b, right )) # negate because max-heap

            left = right - k + 1
            if left < 0:
                continue

            # remove flowers from heap if too far away
            while last[0][1] < left:
                heappop(last)

            complete.append(-last[0][0]) # negate because max-heap

        # greedily count bouquets on a given day
        def bouquets(day):
            nonlocal complete, k, m
            pick = 0
            pos  = 0
            while pos < len(complete):
                if complete[pos] <= day:
                    pos  += k    # skip flowers used for current bouquet
                    pick += 1

                    if pick > m: # early exit: enough bouquets
                        break
                else:
                    pos  += 1
            return pick

        earliest = min(complete)
        latest   = max(complete)
        day = earliest + bisect_left(range(earliest, latest + 1), m, key = bouquets)
        return day if day <= latest else -1
39 lines | 1322 bytes | submitted May 3, 2024 | GitHub   31% comment ratio | tab size 4 | low complexity

Difficulty

This problem is ranked as
easy medium hard

and has an acceptance rate of about 57% (900k+ submissions).

Typical patterns to solve this problem:
  • Array
  • Binary Search
I solved 663 easy, 1250 medium and 309 hard problems

AI Disclaimer

Every single line of code was written by me, 100% from scratch, between August 2023 and August 2024.
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.

See this dedicated page for more details.
LeetCode profile | GitHub repo

Code Walkthrough

Overview

The minDays method determines the earliest calendar day on which it is possible to assemble m bouquets, each consisting of k consecutive flowers, given an array bloomDay that records the day each flower becomes available. The algorithm proceeds in three logical phases: preprocessing the bloom schedule into a sliding-window maximum array, defining a greedy checker that counts how many bouquets can be formed by a particular day, and finally locating the minimal feasible day with a binary-search-style call to bisect_left.

Preprocessing - constructing the complete array

The first phase builds an auxiliary list named complete. For every possible window of length k in bloomDay, complete[i] stores the latest bloom day among the k flowers that would occupy positions i … i+k-1. This value represents the earliest day on which that whole window can be used as a bouquet.

complete = []                     # will hold max(bloomDay[i : i + k])
last = []                         # max-heap (implemented with negative values)

for right, b in enumerate(bloomDay):
    heappush(last, (-b, right))   # store negative bloom day to simulate max-heap

    left = right - k + 1
    if left < 0:
        continue                  # not enough elements yet for a full window

    # discard entries whose index fell out of the current window
    while last[0][1] < left:
        heappop(last)

    complete.append(-last[0][0])  # restore original sign

Key aspects of this fragment:

  • The heap stores pairs (-bloomDay, index). By negating the bloom day, the smallest element in the heap corresponds to the largest original bloom day, giving constant-time access to the window maximum.
  • When the sliding window moves forward, any heap entry whose index is left of the current window (last[0][1] < left) is lazily removed. This avoids rebuilding the heap for each shift.
  • The continue guard skips the first k-1 positions where a full window cannot be formed, ensuring complete only contains valid windows.

Greedy bouquet counting for a fixed day

The nested function bouquets(day) evaluates how many bouquets can be assembled if the current calendar day is day. It scans the complete array from left to right, greedily taking a window whenever its maximum bloom day does not exceed day. Once a window is taken, the scan jumps ahead by k positions, guaranteeing that selected windows are disjoint and respect the required consecutiveness.

def bouquets(day):
    nonlocal complete, k, m
    pick = 0
    pos  = 0
    while pos < len(complete):
        if complete[pos] <= day:
            pos  += k                # skip the k flowers just used
            pick += 1
            if pick > m:            # early exit once enough bouquets are found
                break
        else:
            pos += 1
    return pick

Important behaviors:

  • The early-exit condition if pick > m: break stops the scan as soon as the required number of bouquets is reached, saving work on large inputs.
  • By advancing pos by k after a successful pick, the function enforces the non-overlapping constraint without extra bookkeeping.
  • The comparison complete[pos] <= day directly uses the precomputed window maximum, making the check O(1) per window.

Binary search via bisect_left

Having a monotonic predicate-bouquets(day) >= m-the algorithm searches for the smallest day satisfying it. Instead of writing an explicit binary search loop, it leverages Python’s bisect_left over the integer range [earliest, latest], supplying bouquets as the key function. The call returns the leftmost index where the predicate becomes true.

earliest = min(complete)
latest   = max(complete)
day = earliest + bisect_left(range(earliest, latest + 1), m, key=bouquets)
return day if day <= latest else -1

Mechanics of this step:

  • earliest and latest bound the feasible search space because any bouquet must wait at least for the earliest window to finish blooming and cannot exceed the latest window’s finish day.
  • range(earliest, latest + 1) creates a virtual sorted list of candidate days. bisect_left performs a binary search on this virtual list, invoking bouquets only for the midpoints it examines.
  • The final conditional translates the result of the bisect into the required output, returning -1 when no day within the bounds can satisfy the bouquet requirement.

Edge-case handling

The implementation gracefully addresses several boundary conditions:

  • When bloomDay is shorter than k, the preprocessing loop never appends to complete, leaving it empty. Consequently earliest and latest raise a ValueError on min/max, but the surrounding logic implicitly treats the situation as impossible, leading to a -1 return.
  • The left < 0 guard ensures that windows are only considered once they contain exactly k elements, preventing out-of-range indexing.
  • The lazy removal loop (while last[0][1] < left) guarantees that stale heap entries from earlier windows never influence the current maximum, even when multiple entries share the same bloom day.
  • The early exit in bouquets prevents unnecessary scanning once the target number of bouquets is already achieved, which is especially beneficial when m is small relative to the total possible bouquets.
  • The final comparison day <= latest distinguishes between a successful find and the case where the binary search overshoots the maximum possible day, correctly yielding -1 for infeasible inputs.

Performance considerations

The code incorporates several optimizations that keep the overall runtime efficient:

  • Using a max-heap with lazy deletions provides an amortized logarithmic cost per element while maintaining constant-time access to the current window’s maximum. This avoids the linear scan that a naïve sliding-window maximum would require.
  • The greedy bouquets routine works in a single pass over complete, and the early-exit condition reduces the number of iterations when the required bouquet count is reached early.
  • Delegating the binary search to bisect_left leverages a highly optimized C implementation, minimizing Python-level loop overhead. The key function is invoked only O(log (range)) times, each call performing a linear scan of complete but terminating early when possible.
  • By precomputing complete once, the algorithm eliminates repeated recomputation of window maxima during the binary search, turning what could be a quadratic process into a combination of a single O(n log k) preprocessing step and a series of O(n) checks bounded by the logarithmic number of binary-search steps.
written by openai gpt

ELI5 - Simplified Explanation

Imagine a garden full of flower buds that are waiting for their turn to open and turn into beautiful colorful petals. Each bud needs a certain number of days pass before it decides to bloom.

The code starts by looking at groups of flowers that sit right next to each other. It finds the slowest flower in every small group, because the whole group is only ready when the last stubborn flower finally decides to open up. It keeps a list of these bloom times to remember exactly when each little patch of flowers becomes ready to be picked.

To figure out the best day to pick all the flowers needed, the code plays a guessing game. It picks a middle day and checks to see if enough bouquets can be made by that time. If it finds enough, it knows it can try an even earlier day. If there are not enough flowers ready, it knows it must wait until later in the week. By constantly guessing and checking, it quickly narrows down the search until it finds the very first day that has enough open flowers to make all the bouquets required.

written by google gemini

Code Quality

Several LLMs ranked my code quality on a scale from 1 (very poor) to 10 (outstanding):
n = 27 | median = 6 | μ = 5.37 | σ = 1.64

Complexity Analysis

Time: loglinear
As a rule of thumb, the maximum input size for one second execution time is
  • factorial: up to 10 elements
  • exponential: up to 20 elements
  • polynomial: a few thousand elements
  • loglinear: up to 100,000 elements
  • linear: a few million elements
  • logarithmic: a billion elements
  • constant: practically unlimited
generated automatically

Other Solutions

I wrote a basic crawler to find other solutions to that problem.
Obviously, I did that after I solved the problem myself.

In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.

And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.

(click on a column header to sort the table)
author
author solutions
www AlgoMonster C++ Java Python TS
WalkCCC C++ Java Python
leetcode.ca C++ Java Python Go TS
Progiez C++ Java Python
github black-shadows C++
charles-wangkai Java
everthis JS
FelixFan1122 C#
fsq C++
halfrost Go
JoshCrozier JS
keineahnung2345 C++
suxrobGM C#
trung-hn Python
varunu28 Java
wisdompeak C++
AlgoMonster C++, Python, Java, TypeScript
WalkCCC C++, Python, Java
leetcode.ca C++, Go, Java, Python, TypeScript
Progiez C++, Java, Python
black-shadows C++, C++
charles-wangkai Java
everthis JavaScript
FelixFan1122 C#
fsq C++
halfrost Go
JoshCrozier JavaScript
keineahnung2345 C++
suxrobGM C#
trung-hn Python
varunu28 Java
wisdompeak C++
27 links | 16 authors | 7 languages

Videos

5 videos   did my crawler come up with invalid videos ? drop me a line.

Similar Problems

2 similar problems

Solved On The Same Day

11 solutions   May 3, 2024

Extra

There are a few extra pages giving you some insights into my setup:
  • Tech Stack - my development environment and tools
  • AI - how AI helped me create this website (NOT solving the problems)
  • Progress - progression over time
  • Quality - code quality