2136. Earliest Possible Day of Full Bloom
Problem Description
You have n
flower seeds that need to be planted and grown. Each seed must be planted first before it can start growing and eventually bloom.
For each seed i
:
plantTime[i]
represents the number of full days required to plant that seed. You can only work on planting one seed per day, and you don't need to plant the same seed on consecutive days - you just need to accumulateplantTime[i]
total days of work for that seed to be fully planted.growTime[i]
represents the number of full days the seed needs to grow after being completely planted. Once the growth period is complete, the flower blooms and remains bloomed forever.
Starting from day 0
, you can choose to plant the seeds in any order you want. Your goal is to find the earliest possible day when all flowers have bloomed.
For example, if you have a seed with plantTime = 3
and growTime = 2
, and you start planting it on day 0, you'll finish planting on day 3 (after working days 0, 1, and 2). The seed will then grow for 2 more days and bloom on day 5.
The key insight is that while you're planting seeds sequentially (one at a time), previously planted seeds can grow in parallel. This means the order in which you plant seeds matters - you want to minimize the overall time until the last flower blooms.
The solution uses a greedy approach: sort seeds by their growth time in descending order and plant them in that sequence. This ensures that seeds with longer growth times start growing earlier, minimizing the total time needed for all flowers to bloom.
Intuition
Let's think about what happens when we plant seeds. Since we can only plant one seed at a time, the total planting time is fixed - it's always the sum of all plantTime[i]
. What we can control is the order of planting, which affects when each seed starts and finishes growing.
Consider two seeds A and B:
- Seed A:
plantTime = 2
,growTime = 5
- Seed B:
plantTime = 3
,growTime = 1
If we plant A first then B:
- A finishes planting on day 2, blooms on day 2 + 5 = 7
- B finishes planting on day 2 + 3 = 5, blooms on day 5 + 1 = 6
- All flowers bloom by day 7
If we plant B first then A:
- B finishes planting on day 3, blooms on day 3 + 1 = 4
- A finishes planting on day 3 + 2 = 5, blooms on day 5 + 5 = 10
- All flowers bloom by day 10
The first order is better! Why? Because seed A has a longer growth time, and by planting it first, we allow it to start its long growth period earlier while we're still planting other seeds.
This reveals the key insight: seeds with longer growth times should be planted first. While we're busy planting the remaining seeds, the early-planted seeds with long growth times are already growing in parallel. This minimizes the "tail" time - the growth period of the last seed that determines when all flowers have bloomed.
Think of it like cooking multiple dishes: you'd start with the dish that takes longest to cook first, so it can be cooking while you prepare the others. The same principle applies here - start the seeds with longest growth times first so they can grow while you're still planting others.
Therefore, the optimal strategy is to sort seeds by growTime
in descending order and plant them in that sequence. For each seed, we track when it will bloom (plantingEndTime + growTime
) and keep the maximum bloom time across all seeds.
Solution Approach
Based on our intuition that seeds with longer growth times should be planted first, we implement a greedy algorithm with sorting.
Step 1: Sort the seeds
We pair each seed's plantTime
and growTime
together and sort them by growTime
in descending order. This is achieved using:
sorted(zip(plantTime, growTime), key=lambda x: -x[1])
The zip
function pairs corresponding elements, and key=lambda x: -x[1]
sorts by the second element (growTime) in descending order.
Step 2: Simulate the planting process We iterate through the sorted seeds and track two key variables:
t
: The current day, representing when we finish planting the current seedans
: The earliest day when all planted seeds so far will have bloomed
For each seed with planting time pt
and growth time gt
:
- We update the current day:
t += pt
(we spendpt
days planting this seed) - We calculate when this seed will bloom:
t + gt
(finish planting day + growth time) - We update our answer:
ans = max(ans, t + gt)
(keep track of the latest bloom time)
Why this works:
- By planting seeds with longer growth times first, we ensure they start growing while we're still planting other seeds
- The
max
operation ensures we track the latest blooming flower, which determines when all flowers have bloomed - Each seed's bloom time is its planting completion time plus its growth time
Time Complexity: O(n log n)
due to sorting, where n
is the number of seeds.
Space Complexity: O(n)
for storing the zipped pairs during sorting.
The algorithm guarantees the minimum possible bloom time for all flowers by prioritizing seeds that need more time to grow, allowing them to grow in parallel while we plant the remaining seeds.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with 3 seeds:
- Seed 0:
plantTime = 1
,growTime = 4
- Seed 1:
plantTime = 2
,growTime = 3
- Seed 2:
plantTime = 3
,growTime = 1
Step 1: Sort by growth time (descending)
After sorting by growTime
in descending order:
- Seed 0:
plantTime = 1
,growTime = 4
(highest growth time) - Seed 1:
plantTime = 2
,growTime = 3
- Seed 2:
plantTime = 3
,growTime = 1
(lowest growth time)
Step 2: Simulate planting in this order
Initialize: t = 0
(current day), ans = 0
(latest bloom day)
Plant Seed 0:
- Planting takes 1 day:
t = 0 + 1 = 1
- This seed blooms on day:
1 + 4 = 5
- Update answer:
ans = max(0, 5) = 5
- Status: Seed 0 planted on days [0], will bloom on day 5
Plant Seed 1:
- Planting takes 2 days:
t = 1 + 2 = 3
- This seed blooms on day:
3 + 3 = 6
- Update answer:
ans = max(5, 6) = 6
- Status: Seed 1 planted on days [1, 2], will bloom on day 6
Plant Seed 2:
- Planting takes 3 days:
t = 3 + 3 = 6
- This seed blooms on day:
6 + 1 = 7
- Update answer:
ans = max(6, 7) = 7
- Status: Seed 2 planted on days [3, 4, 5], will bloom on day 7
Result: All flowers bloom by day 7.
Visual Timeline:
Day: 0 1 2 3 4 5 6 7 S0: [P][---grow---][bloom] S1: [P P][--grow--][bloom] S2: [P P P][g][bloom]
Where P = planting, grow = growing period, bloom = flower has bloomed
By planting the seed with the longest growth time first, we minimize the total time. If we had planted them in reverse order (shortest growth time first), the seed with growTime = 4
would start growing much later, resulting in all flowers blooming on day 10 instead of day 7.
Solution Implementation
1class Solution:
2 def earliestFullBloom(self, plantTime: List[int], growTime: List[int]) -> int:
3 # Initialize variables to track the earliest bloom completion time
4 earliest_bloom_day = 0 # The earliest day when all flowers have bloomed
5 current_planting_day = 0 # Current day when we finish planting
6
7 # Create pairs of (plantTime, growTime) and sort by growTime in descending order
8 # Strategy: Plant flowers with longer grow times first to minimize total time
9 flower_pairs = zip(plantTime, growTime)
10 sorted_flowers = sorted(flower_pairs, key=lambda flower: -flower[1])
11
12 # Process each flower in the optimal order
13 for plant_duration, grow_duration in sorted_flowers:
14 # Update the day when we finish planting this flower
15 current_planting_day += plant_duration
16
17 # Calculate when this flower will bloom (planting end day + grow time)
18 bloom_day = current_planting_day + grow_duration
19
20 # Update the earliest day when all flowers have bloomed
21 earliest_bloom_day = max(earliest_bloom_day, bloom_day)
22
23 return earliest_bloom_day
24
1class Solution {
2 public int earliestFullBloom(int[] plantTime, int[] growTime) {
3 int n = plantTime.length;
4
5 // Create an array of indices to track original positions after sorting
6 Integer[] indices = new Integer[n];
7 for (int i = 0; i < n; i++) {
8 indices[i] = i;
9 }
10
11 // Sort indices based on grow time in descending order
12 // Seeds with longer grow times should be planted first
13 Arrays.sort(indices, (i, j) -> growTime[j] - growTime[i]);
14
15 // Track the earliest time when all flowers have bloomed
16 int earliestBloomTime = 0;
17 // Track the current planting end time
18 int currentPlantingTime = 0;
19
20 // Process each seed in the sorted order
21 for (int index : indices) {
22 // Add the planting time for current seed
23 currentPlantingTime += plantTime[index];
24
25 // Calculate when this flower will bloom (planting end time + grow time)
26 // Update the earliest bloom time if this flower blooms later
27 earliestBloomTime = Math.max(earliestBloomTime, currentPlantingTime + growTime[index]);
28 }
29
30 return earliestBloomTime;
31 }
32}
33
1class Solution {
2public:
3 int earliestFullBloom(vector<int>& plantTime, vector<int>& growTime) {
4 int n = plantTime.size();
5
6 // Create an index array to track original positions after sorting
7 vector<int> indices(n);
8 iota(indices.begin(), indices.end(), 0);
9
10 // Sort indices by grow time in descending order
11 // Seeds with longer grow times should be planted first
12 sort(indices.begin(), indices.end(), [&](int i, int j) {
13 return growTime[i] > growTime[j];
14 });
15
16 int maxBloomTime = 0; // Track the maximum bloom time across all seeds
17 int currentPlantTime = 0; // Cumulative time spent planting
18
19 // Process each seed in the sorted order
20 for (int index : indices) {
21 // Add the time to plant current seed
22 currentPlantTime += plantTime[index];
23
24 // Calculate when this seed will bloom (plant finish time + grow time)
25 // Update the maximum bloom time if this seed blooms later
26 maxBloomTime = max(maxBloomTime, currentPlantTime + growTime[index]);
27 }
28
29 return maxBloomTime;
30 }
31};
32
1/**
2 * Calculates the earliest day when all flowers have bloomed
3 * @param plantTime - Array where plantTime[i] is the number of days to plant seed i
4 * @param growTime - Array where growTime[i] is the number of days for seed i to grow after planting
5 * @returns The earliest possible day where all flowers have bloomed
6 */
7function earliestFullBloom(plantTime: number[], growTime: number[]): number {
8 const seedCount: number = plantTime.length;
9
10 // Create an array of indices [0, 1, 2, ..., n-1]
11 const seedIndices: number[] = Array.from({ length: seedCount }, (_, index) => index);
12
13 // Sort indices by grow time in descending order (longest grow time first)
14 // This greedy approach ensures seeds with longer grow times are planted earlier
15 seedIndices.sort((indexA: number, indexB: number) => growTime[indexB] - growTime[indexA]);
16
17 let maxBloomDay: number = 0; // Track the maximum bloom day across all seeds
18 let currentPlantingDay: number = 0; // Track the current day we're planting
19
20 // Process each seed in the sorted order
21 for (const seedIndex of seedIndices) {
22 // Add the planting time for current seed to get the day planting finishes
23 currentPlantingDay += plantTime[seedIndex];
24
25 // Calculate when this seed will bloom (planting finish day + grow time)
26 // Update the maximum bloom day if this seed blooms later
27 maxBloomDay = Math.max(maxBloomDay, currentPlantingDay + growTime[seedIndex]);
28 }
29
30 return maxBloomDay;
31}
32
Time and Space Complexity
Time Complexity: O(n log n)
The dominant operation in this algorithm is the sorting step. The sorted()
function is called on a zip object containing n
elements (where n
is the number of seeds), which requires O(n log n)
time. The subsequent iteration through the sorted list takes O(n)
time, but this is overshadowed by the sorting complexity. Therefore, the overall time complexity is O(n log n)
.
Space Complexity: O(n)
The space complexity arises from creating the zipped list of tuples from plantTime
and growTime
, which contains n
elements. The sorted()
function creates a new list of size n
to store the sorted result. Although the zip object itself is an iterator and doesn't consume extra space proportional to n
, the sorted list does require O(n)
additional space. Hence, the space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Incorrectly Sorting by Plant Time Instead of Grow Time
A common mistake is sorting by plantTime
rather than growTime
, or sorting in the wrong order (ascending instead of descending).
Incorrect approach:
# Wrong: Sorting by plantTime
sorted_flowers = sorted(zip(plantTime, growTime), key=lambda x: x[0])
# Wrong: Sorting growTime in ascending order
sorted_flowers = sorted(zip(plantTime, growTime), key=lambda x: x[1])
Why this fails: Consider two seeds:
- Seed A: plantTime = 1, growTime = 10
- Seed B: plantTime = 10, growTime = 1
If we plant A first: Day 1 (finish planting A) + 10 (grow) = Day 11 for A to bloom Then plant B: Day 11 (finish planting B) + 1 (grow) = Day 12 for B to bloom Total: 12 days
If we plant B first: Day 10 (finish planting B) + 1 (grow) = Day 11 for B to bloom Then plant A: Day 11 (finish planting A) + 10 (grow) = Day 21 for A to bloom Total: 21 days
The correct approach (sorting by growTime descending) gives us 12 days, while the wrong approach gives us 21 days.
Pitfall 2: Forgetting to Track the Maximum Bloom Time
Another mistake is only tracking the last flower's bloom time instead of the maximum bloom time across all flowers.
Incorrect approach:
for plant_duration, grow_duration in sorted_flowers: current_planting_day += plant_duration # Wrong: Only considering the last flower earliest_bloom_day = current_planting_day + grow_duration
Why this fails: Earlier planted flowers might bloom later than subsequently planted ones. For example:
- Seed 1: plantTime = 1, growTime = 100 โ blooms on day 101
- Seed 2: plantTime = 1, growTime = 1 โ blooms on day 3
The last flower (Seed 2) blooms on day 3, but we need to wait until day 101 for all flowers to bloom.
Correct approach:
Always use max()
to track the latest bloom time among all flowers:
earliest_bloom_day = max(earliest_bloom_day, current_planting_day + grow_duration)
Pitfall 3: Misunderstanding the Problem Constraints
Some might think you can plant multiple seeds simultaneously or that growth doesn't happen in parallel.
Key clarifications:
- You can only plant one seed at a time (sequential planting)
- Once a seed is planted, it grows independently (parallel growth)
- The planting of a seed requires
plantTime[i]
cumulative days of work, not necessarily consecutive
The solution correctly handles these constraints by accumulating planting time sequentially while allowing previously planted seeds to grow in parallel.
Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.
Which of the following method should we use to solve this problem?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donโt Miss This!