2136. Earliest Possible Day of Full Bloom
Problem Description
In this problem, you have n
flower seeds that you need to plant and grow. Each flower seed has two associated times:
-
plantTime[i]
: This is the number of full days it takes to plant thei-th
seed. You can only work on planting one seed per day, but you don't have to do it on consecutive days. However, you cannot consider a seed as planted until you've spent the total number ofplantTime[i]
days on it. -
growTime[i]
: This is the number of full days it takes for thei-th
seed to grow and bloom after it has been completely planted. Once a flower blooms, at the end of its growth time, it stays bloomed forever.
The task is to arrange your planting strategy to ensure that all the seeds are blooming by the earliest possible day. You can plant the seeds in any order starting from day 0.
Intuition
To solve the problem, the main insight is that seeds with a longer growth time ("growTime") should be prioritised for planting. This is because the growth process can occur simultaneously with the planting of other seeds. In other words, if a seed takes a long time to grow, you want to start that growth period as soon as possible, so it doesn't delay the point in time when all flowers are blooming.
Here is the step-by-step intuition for the solution:
-
Pair each seed's planting time with its growth time and consider these as a combined attribute of the seed.
-
Sort the seeds in descending order of their growth time. By doing this, we prioritize planting seeds with the longest growth time.
-
Initialize two variables,
t
andans
.t
will keep track of the total time spent planting seeds up to the current seed, andans
will represent the earliest day all seeds are blooming. Start with both at zero. -
Iterate through the sorted list of pairs:
-
Increment
t
by the current seed's planting time. This represents the day by which planting of this seed is finished. -
Calculate the potential blooming day for the current seed by adding its growth time to
t
. -
Update
ans
to be the maximum of its current value and the potential blooming day for the current seed. This ensures thatans
always represents the day by which all seeds seen so far will have bloomed.
-
-
After the iteration,
ans
will contain the earliest possible day where all seeds are blooming by following the optimal planting order.
The logic behind this approach is that by focusing on getting the long-growth seeds growing as early as possible, you minimize the waiting time at the end of the planting schedule for the flowers to bloom.
Solution Approach
To implement the solution, we utilize sorting and straightforward iteration to find the optimal sequence for planting the seeds.
-
Sorting: We begin by pairing each
plantTime
with the correspondinggrowTime
for each seed. Then, we sort the pairs based ongrowTime
in descending order. This sorting places the seeds with the longest growth period first, which are the seeds we want to plant as soon as possible.# Pseudocode for [Sorting](/problems/sorting_summary) paired_times = zip(plantTime, growTime) sorted_times = sorted(paired_times, key=lambda x: -x[1])
-
Iteration: Next, we iterate through our sorted pairs and simulate the planting and growing process.
# Pseudocode for Iteration t = 0 # Total days spent planting ans = 0 # The earliest day all seeds are blooming for pt, gt in sorted_times: t += pt # Increment total planting days by current seed's planting time ans = max(ans, t + gt) # Update ans to the max of current ans and the potential bloom day
-
During each step, we adjust our
t
value by adding the current seed's planting time. Thist
represents the time passed solely due to planting activities. -
We then consider the total time until the current seed blooms, which is
t + gt
(gt
- growth time for the current seed). Since growth occurs after planting, it makes sense to sum these up. -
The
ans
value is updated to ensure it represents the day when each seed planted so far will have bloomed. By using themax
function, we ensure that it reflects the latest bloom amongst all seeds considered.
-
-
Variables: Two key variables are used:
t
: Tracks the cumulative days spent on planting.ans
: Tracks the earliest possible day where all seeds are blooming.
-
Return Value: After the loop completes,
ans
represents the earliest day by which all the seeds would have bloomed. We arrived at this number by iteratively keeping track of the maximal end date for the blooming of each seed, hence considering the fact that while one seed is growing, we could be planting another.
The code demonstrates proper utilization of Python's list operations, such as zipping, sorting, and iteration. Moreover, the use of a simple max function elegantly resolves the problem of comparing bloom times and determining the overall earliest bloom day.
# Reference Solution Code
class Solution:
def earliestFullBloom(self, plantTime: List[int], growTime: List[int]) -> int:
ans = t = 0
for pt, gt in sorted(zip(plantTime, growTime), key=lambda x: -x[1]):
t += pt # day when planting of this seed is finished
ans = max(ans, t + gt) # latest bloom day amongst all seeds
return ans # the earliest possible day where all seeds are blooming
In the provided algorithm, each step is purposeful and designed to optimize the bloom time based on seed growth characteristics. By focusing on the growth period and organizing the planting schedule accordingly, we reach an optimal and efficient solution.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example to illustrate the solution approach:
Suppose we are given the following plantTime
and growTime
for n = 3
seeds:
- Seed 1:
plantTime[0] = 1
,growTime[0] = 4
- Seed 2:
plantTime[1] = 2
,growTime[1] = 3
- Seed 3:
plantTime[2] = 3
,growTime[2] = 2
Following the solution approach:
- Pair each seed's planting time with its growth time:
Seed 1 -> (1, 4) Seed 2 -> (2, 3) Seed 3 -> (3, 2)
- Sort the seeds in descending order of grow time:
Sorted Pairs: Seed 1 -> (1, 4) Seed 2 -> (2, 3) Seed 3 -> (3, 2)
(Note: Since our example is already in descending order, no changes are made).
- Initialize
t
andans
to zero:
t = 0 (Total days spent planting) ans = 0 (Earliest day all seeds are blooming)
- Iterate through the sorted list of pairs:
- For Seed 1:
pt = 1
,gt = 4
t += 1 (t = 1) ans = max(ans, t + gt) = max(0, 1 + 4) = 5
- For Seed 2:
pt = 2
,gt = 3
t += 2 (t = 3) ans = max(ans, t + gt) = max(5, 3 + 3) = 6
- For Seed 3:
pt = 3
,gt = 2
t += 3 (t = 6) ans = max(ans, t + gt) = max(6, 6 + 2) = 8
- At the end of the iteration,
ans
is8
, which means all seeds will be blooming by day 8.
In this example, prioritizing the planting of seeds with the largest growTime
allowed the seeds to start growing earlier. Even though seeds with shorter plantTime
could have been planted first, since we prioritize by growTime
, we minimize the overall waiting for flowers to bloom. Thus, following the optimal planting order, all seeds will be blooming by day 8.
Solution Implementation
1from typing import List
2
3class Solution:
4 def earliestFullBloom(self, plant_time: List[int], grow_time: List[int]) -> int:
5 # Initialize time counters for current time `curr_time` and max time `max_time`.
6 max_time = curr_time = 0
7
8 # Pair plant and grow times, then sort by grow time in descending order.
9 # This ensures that the plants with the longest grow time get planted first.
10 for planting_time, growth_time in sorted(zip(plant_time, grow_time), key=lambda x: -x[1]):
11 # Accumulate the current time by adding the planting time.
12 curr_time += planting_time
13 # Calculate the final full bloom time for the current plant and update `max_time`.
14 # This is done by taking the sum of the current time and the grow time.
15 max_time = max(max_time, curr_time + growth_time)
16
17 # Return the maximum time it will take for all plants to fully bloom.
18 return max_time
19
1class Solution {
2 public int earliestFullBloom(int[] plantTime, int[] growTime) {
3 int numPlants = plantTime.length; // Get the number of plants.
4 Integer[] indices = new Integer[numPlants]; // Create an array to store the indices of plants.
5 for (int i = 0; i < numPlants; i++) {
6 indices[i] = i; // Initialize indices with the index of each plant.
7 }
8
9 // Sort the array of indices based on the grow time in descending order.
10 // The comparison function takes two indices and sorts them according to the grow time of their corresponding plants.
11 Arrays.sort(indices, (i, j) -> growTime[j] - growTime[i]);
12
13 int maxDays = 0; // This will store the maximum number of days needed for all plants to reach full bloom.
14 int totalPlantTime = 0; // This will accumulate the total days spent planting thus far.
15
16 // Iterate over the sorted array of indices.
17 for (int i : indices) {
18 // Add the time it takes to plant the current plant.
19 totalPlantTime += plantTime[i];
20 // Calculate the total days needed for this plant to fully bloom,
21 // which is the sum of the days spent planting up to now and this plant's grow time.
22 // Update maxDays to be the maximum of itself and the total bloom days of the current plant.
23 maxDays = Math.max(maxDays, totalPlantTime + growTime[i]);
24 }
25
26 // Return the maximum number of days needed for all plants to reach full bloom.
27 return maxDays;
28 }
29}
30
1class Solution {
2public:
3 int earliestFullBloom(vector<int>& plantTimes, vector<int>& growTimes) {
4 // Get number of plants
5 int numPlants = plantTimes.size();
6
7 // Create an index vector to sort the plants based on growTime
8 vector<int> indices(numPlants);
9 iota(indices.begin(), indices.end(), 0);
10
11 // Sort the indices based on grow times (descending order)
12 // So that plants with longer grow times are planted first
13 sort(indices.begin(), indices.end(), [&](int i, int j) {
14 return growTimes[i] > growTimes[j];
15 });
16
17 // Initialize variables to track the current day and the earliest
18 // day that all plants will be in full bloom
19 int earliestBloomDay = 0;
20 int currentDay = 0;
21
22 // Iterate over the plants in the order determined by indices
23 for (int idx : indices) {
24 // Plant the current plant; this increments current planting day
25 currentDay += plantTimes[idx];
26
27 // Determine the earliest day all plants will be in full bloom
28 // This is the maximum of current day + grow time of current plant
29 // and the previous earliest bloom day
30 earliestBloomDay = max(earliestBloomDay, currentDay + growTimes[idx]);
31 }
32
33 // Return the earliest day when all plants will be in full bloom
34 return earliestBloomDay;
35 }
36};
37
1// Function to determine the earliest day when all flowers can bloom
2// by planning the optimal order in which to plant the flowers.
3function earliestFullBloom(plantTimes: number[], growTimes: number[]): number {
4 // Number of flowers
5 const flowerCount = plantTimes.length;
6
7 // Create an array of indices representing the flowers
8 const flowerIndices: number[] = Array.from({ length: flowerCount }, (_, index) => index);
9
10 // Sort the indices based on the growing time in descending order
11 // so that we plant the flowers with the longest growing time first.
12 flowerIndices.sort((indexA, indexB) => growTimes[indexB] - growTimes[indexA]);
13
14 // Initialize variables:
15 // 'totalTime' to track the total time elapsed,
16 // 'maxBloomTime' to keep the maximum bloom time so far.
17 let totalTime = 0;
18 let maxBloomTime = 0;
19
20 // Iterate over the sorted indices array
21 for (const flowerIndex of flowerIndices) {
22 // Increment the total time by the current flower's planting time
23 totalTime += plantTimes[flowerIndex];
24 // Update the maximum bloom time
25 maxBloomTime = Math.max(maxBloomTime, totalTime + growTimes[flowerIndex]);
26 }
27
28 // Return the maximum bloom time
29 // which is the earliest day when all flowers will bloom
30 return maxBloomTime;
31}
32
Time and Space Complexity
The given Python function earliestFullBloom
calculates the earliest day on which all plants will be in full bloom. It does so by first sorting the combined planting and growing times in descending order of growing time, then iterating through them to calculate the total time required.
Time Complexity
The time complexity of the function is determined by the sorted
function and the subsequent iteration through the sorted list.
- Sorting the zipped list
sorted(zip(plantTime, growTime), key=lambda x: -x[1])
has a time complexity ofO(n log n)
, wheren
represents the number of elements inplantTime
orgrowTime
lists. - The following for loop iterates through each element once, which has a time complexity of
O(n)
.
Combining these two steps, the overall time complexity is dominated by the sorting step, making it O(n log n)
.
Space Complexity
The space complexity of the function is determined by the additional space used by the sorted list and the variables inside the function.
- The sorted list creates a new list of pairs from
plantTime
andgrowTime
, having space complexityO(n)
. - Variables
ans
,t
,pt
, andgt
use constant spaceO(1)
.
Hence, the total space complexity of the function is O(n)
for storing the sorted list.
Learn more about how to find time and space complexity quickly using problem constraints.
Which technique can we use to find the middle of a linked list?
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
LeetCode 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 we
Want a Structured Path to Master System Design Too? Don’t Miss This!