3633. Earliest Finish Time for Land and Water Rides I
Problem Description
You are visiting a theme park that has two categories of attractions: land rides and water rides.
For each category, you have information about when rides open and how long they last:
Land rides:
landStartTime[i]
- the earliest time thei-th
land ride can be boardedlandDuration[i]
- how long thei-th
land ride lasts
Water rides:
waterStartTime[j]
- the earliest time thej-th
water ride can be boardedwaterDuration[j]
- how long thej-th
water ride lasts
As a tourist, you must experience exactly one ride from each category. You can choose to do them in either order (land first then water, or water first then land).
The rules for riding are:
- You can start a ride at its opening time or any time after it opens
- If you start a ride at time
t
, you will finish at timet + duration
- After finishing one ride, you can immediately board the next ride (if it's already open) or wait until it opens
Your goal is to find the earliest possible time at which you can finish both rides.
For example, if you choose a land ride that opens at time 2 with duration 3, you'll finish at time 5. Then if you want to take a water ride that opens at time 4 with duration 2, you can board it immediately at time 5 (since it's already open) and finish at time 7. The total completion time would be 7.
Intuition
Since we need to take exactly one ride from each category, we have two possible orders: land ride first then water ride, or water ride first then land ride. We need to check both orders and find which one gives us the earliest completion time.
For any given order, the key insight is that we want to minimize the total time. When we finish the first ride, we want to start the second ride as soon as possible. But there's a constraint - we can't start the second ride before it opens.
Let's think about the optimal strategy for choosing rides in a fixed order (say land first, then water):
-
For the first category (land rides), we want to finish as early as possible. The earliest we can finish any land ride is
min(landStartTime[i] + landDuration[i])
for alli
. This gives us the minimum end time for the first ride. -
For the second category (water rides), we need to consider two constraints:
- We can't start before the water ride opens:
waterStartTime[j]
- We can't start before we finish the land ride:
minEnd
from step 1
So we start the water ride at
max(waterStartTime[j], minEnd)
and finish atmax(waterStartTime[j], minEnd) + waterDuration[j]
. - We can't start before the water ride opens:
-
To get the overall minimum completion time, we try all possible water rides and pick the one that finishes earliest.
The same logic applies when we reverse the order (water first, then land).
The solution elegantly captures this with the calc
function that takes the start times and durations of rides in order. It first finds the minimum end time for the first category, then finds the minimum total completion time considering all options for the second category. By calling this function twice with the parameters swapped, we check both possible orders and return the minimum.
Learn more about Greedy, Two Pointers, Binary Search and Sorting patterns.
Solution Approach
The solution implements an enumeration and greedy approach by considering both possible orders of rides and selecting the minimum completion time.
The core of the solution is the helper function calc(a1, t1, a2, t2)
which calculates the minimum completion time when taking rides in a specific order:
a1
,t1
represent the start times and durations of the first categorya2
,t2
represent the start times and durations of the second category
Step 1: Find the earliest end time for the first category
min_end = min(a + t for a, t in zip(a1, t1))
This calculates the minimum possible end time among all rides in the first category. For each ride i
, the end time is startTime[i] + duration[i]
. We take the minimum of all these values.
Step 2: Calculate the minimum total completion time
return min(max(a, min_end) + t for a, t in zip(a2, t2))
For each ride in the second category:
- We can start at time
max(a, min_end)
where:a
is the ride's opening timemin_end
is when we finish the first ride- We take the maximum because we must wait for both conditions to be satisfied
- The completion time is
max(a, min_end) + t
wheret
is the ride's duration - We return the minimum completion time among all second category rides
Step 3: Check both orders
x = calc(landStartTime, landDuration, waterStartTime, waterDuration)
y = calc(waterStartTime, waterDuration, landStartTime, landDuration)
return min(x, y)
x
represents doing land rides first, then water ridesy
represents doing water rides first, then land rides- We return the minimum of the two options
The time complexity is O(n + m)
where n
is the number of land rides and m
is the number of water rides, as we iterate through each list a constant number of times. The space complexity is O(1)
as we only use a few variables to store intermediate results.
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 to understand how the solution works.
Given:
- Land rides:
landStartTime = [1, 6]
,landDuration = [3, 2]
- Water rides:
waterStartTime = [2, 4]
,waterDuration = [5, 1]
Step 1: Calculate for Land → Water order
First, find the earliest we can finish any land ride:
- Land ride 0: starts at 1, duration 3 → finishes at 1 + 3 = 4
- Land ride 1: starts at 6, duration 2 → finishes at 6 + 2 = 8
- Minimum end time for land rides:
min_end = 4
Now calculate total completion time for each water ride option:
- Water ride 0: opens at 2, but we finish land at 4, so start at
max(2, 4) = 4
, finish at 4 + 5 = 9 - Water ride 1: opens at 4, we finish land at 4, so start at
max(4, 4) = 4
, finish at 4 + 1 = 5
Minimum completion time for Land → Water: 5 (choosing land ride 0 then water ride 1)
Step 2: Calculate for Water → Land order
First, find the earliest we can finish any water ride:
- Water ride 0: starts at 2, duration 5 → finishes at 2 + 5 = 7
- Water ride 1: starts at 4, duration 1 → finishes at 4 + 1 = 5
- Minimum end time for water rides:
min_end = 5
Now calculate total completion time for each land ride option:
- Land ride 0: opens at 1, but we finish water at 5, so start at
max(1, 5) = 5
, finish at 5 + 3 = 8 - Land ride 1: opens at 6, we finish water at 5, so start at
max(6, 5) = 6
, finish at 6 + 2 = 8
Minimum completion time for Water → Land: 8 (choosing water ride 1 then either land ride)
Step 3: Compare both orders
- Land → Water: 5
- Water → Land: 8
- Final answer: min(5, 8) = 5
The optimal strategy is to take land ride 0 (1→4), then immediately take water ride 1 (4→5), finishing at time 5.
Solution Implementation
1class Solution:
2 def earliestFinishTime(
3 self,
4 landStartTime: List[int],
5 landDuration: List[int],
6 waterStartTime: List[int],
7 waterDuration: List[int],
8 ) -> int:
9 def calculate_finish_time(
10 first_stage_start: List[int],
11 first_stage_duration: List[int],
12 second_stage_start: List[int],
13 second_stage_duration: List[int]
14 ) -> int:
15 """
16 Calculate the minimum finish time when completing first stage then second stage.
17
18 Args:
19 first_stage_start: Start times for first stage activities
20 first_stage_duration: Durations for first stage activities
21 second_stage_start: Start times for second stage activities
22 second_stage_duration: Durations for second stage activities
23
24 Returns:
25 Minimum finish time for completing both stages
26 """
27 # Find the earliest possible completion time of the first stage
28 # by choosing the activity with minimum (start_time + duration)
29 min_first_stage_end = min(
30 start + duration
31 for start, duration in zip(first_stage_start, first_stage_duration)
32 )
33
34 # For the second stage, we can start each activity at the maximum of:
35 # - its own start time
36 # - the completion time of the first stage
37 # Then find the minimum total completion time
38 min_total_time = min(
39 max(start, min_first_stage_end) + duration
40 for start, duration in zip(second_stage_start, second_stage_duration)
41 )
42
43 return min_total_time
44
45 # Try both orderings: land first then water, or water first then land
46 land_then_water = calculate_finish_time(
47 landStartTime, landDuration,
48 waterStartTime, waterDuration
49 )
50 water_then_land = calculate_finish_time(
51 waterStartTime, waterDuration,
52 landStartTime, landDuration
53 )
54
55 # Return the minimum of both orderings
56 return min(land_then_water, water_then_land)
57
1class Solution {
2 /**
3 * Calculates the earliest possible finish time for completing two types of tasks.
4 * Tasks can be completed in either order: land tasks first then water tasks, or vice versa.
5 *
6 * @param landStartTime array of start times for land tasks
7 * @param landDuration array of durations for land tasks
8 * @param waterStartTime array of start times for water tasks
9 * @param waterDuration array of durations for water tasks
10 * @return the minimum finish time across both possible orderings
11 */
12 public int earliestFinishTime(
13 int[] landStartTime, int[] landDuration, int[] waterStartTime, int[] waterDuration) {
14
15 // Calculate finish time if land tasks are done first, then water tasks
16 int landThenWaterTime = calculateSequentialFinishTime(
17 landStartTime, landDuration, waterStartTime, waterDuration);
18
19 // Calculate finish time if water tasks are done first, then land tasks
20 int waterThenLandTime = calculateSequentialFinishTime(
21 waterStartTime, waterDuration, landStartTime, landDuration);
22
23 // Return the minimum of the two possible orderings
24 return Math.min(landThenWaterTime, waterThenLandTime);
25 }
26
27 /**
28 * Calculates the finish time when completing first set of tasks before second set.
29 *
30 * @param firstStartTimes array of start times for first task set
31 * @param firstDurations array of durations for first task set
32 * @param secondStartTimes array of start times for second task set
33 * @param secondDurations array of durations for second task set
34 * @return the total finish time for this task ordering
35 */
36 private int calculateSequentialFinishTime(
37 int[] firstStartTimes, int[] firstDurations,
38 int[] secondStartTimes, int[] secondDurations) {
39
40 // Find the earliest completion time among all first tasks
41 int earliestFirstTaskCompletion = Integer.MAX_VALUE;
42 for (int i = 0; i < firstStartTimes.length; i++) {
43 int taskEndTime = firstStartTimes[i] + firstDurations[i];
44 earliestFirstTaskCompletion = Math.min(earliestFirstTaskCompletion, taskEndTime);
45 }
46
47 // Find the earliest total completion time when starting second tasks
48 // after completing at least one first task
49 int earliestTotalCompletion = Integer.MAX_VALUE;
50 for (int i = 0; i < secondStartTimes.length; i++) {
51 // Second task can only start after both:
52 // 1. Its own start time constraint
53 // 2. At least one first task is complete
54 int actualSecondTaskStart = Math.max(earliestFirstTaskCompletion, secondStartTimes[i]);
55 int totalCompletionTime = actualSecondTaskStart + secondDurations[i];
56 earliestTotalCompletion = Math.min(earliestTotalCompletion, totalCompletionTime);
57 }
58
59 return earliestTotalCompletion;
60 }
61}
62
1class Solution {
2public:
3 int earliestFinishTime(vector<int>& landStartTime, vector<int>& landDuration,
4 vector<int>& waterStartTime, vector<int>& waterDuration) {
5 // Calculate earliest finish time for land-first then water route
6 int landFirstFinishTime = calculateEarliestFinish(landStartTime, landDuration,
7 waterStartTime, waterDuration);
8
9 // Calculate earliest finish time for water-first then land route
10 int waterFirstFinishTime = calculateEarliestFinish(waterStartTime, waterDuration,
11 landStartTime, landDuration);
12
13 // Return the minimum of both possible routes
14 return min(landFirstFinishTime, waterFirstFinishTime);
15 }
16
17private:
18 int calculateEarliestFinish(vector<int>& firstSegmentStart, vector<int>& firstSegmentDuration,
19 vector<int>& secondSegmentStart, vector<int>& secondSegmentDuration) {
20 // Find the earliest possible completion time for the first segment
21 int earliestFirstSegmentEnd = INT_MAX;
22 for (int i = 0; i < firstSegmentStart.size(); ++i) {
23 int currentEndTime = firstSegmentStart[i] + firstSegmentDuration[i];
24 earliestFirstSegmentEnd = min(earliestFirstSegmentEnd, currentEndTime);
25 }
26
27 // Find the overall earliest finish time considering all second segment options
28 int overallEarliestFinish = INT_MAX;
29 for (int i = 0; i < secondSegmentStart.size(); ++i) {
30 // Second segment starts at max of (first segment end, second segment start time)
31 int actualSecondStart = max(earliestFirstSegmentEnd, secondSegmentStart[i]);
32 int totalFinishTime = actualSecondStart + secondSegmentDuration[i];
33 overallEarliestFinish = min(overallEarliestFinish, totalFinishTime);
34 }
35
36 return overallEarliestFinish;
37 }
38};
39
1/**
2 * Calculates the earliest possible finish time by considering both paths:
3 * land-first-then-water and water-first-then-land
4 * @param landStartTime - Array of possible start times for land activities
5 * @param landDuration - Array of durations for corresponding land activities
6 * @param waterStartTime - Array of possible start times for water activities
7 * @param waterDuration - Array of durations for corresponding water activities
8 * @returns The minimum finish time across all possible combinations
9 */
10function earliestFinishTime(
11 landStartTime: number[],
12 landDuration: number[],
13 waterStartTime: number[],
14 waterDuration: number[],
15): number {
16 // Calculate finish time for land-first-then-water path
17 const landFirstFinishTime = calc(landStartTime, landDuration, waterStartTime, waterDuration);
18
19 // Calculate finish time for water-first-then-land path
20 const waterFirstFinishTime = calc(waterStartTime, waterDuration, landStartTime, landDuration);
21
22 // Return the minimum of both paths
23 return Math.min(landFirstFinishTime, waterFirstFinishTime);
24}
25
26/**
27 * Calculates the minimum finish time when completing first activity then second activity
28 * @param firstStartTimes - Array of possible start times for the first activity
29 * @param firstDurations - Array of durations for the first activity
30 * @param secondStartTimes - Array of possible start times for the second activity
31 * @param secondDurations - Array of durations for the second activity
32 * @returns The minimum total finish time for this sequence
33 */
34function calc(
35 firstStartTimes: number[],
36 firstDurations: number[],
37 secondStartTimes: number[],
38 secondDurations: number[]
39): number {
40 // Find the earliest possible end time for the first activity
41 let earliestFirstActivityEnd = Number.MAX_SAFE_INTEGER;
42 for (let i = 0; i < firstStartTimes.length; i++) {
43 earliestFirstActivityEnd = Math.min(
44 earliestFirstActivityEnd,
45 firstStartTimes[i] + firstDurations[i]
46 );
47 }
48
49 // Find the minimum total finish time considering all second activity options
50 let minimumTotalFinishTime = Number.MAX_SAFE_INTEGER;
51 for (let i = 0; i < secondStartTimes.length; i++) {
52 // Second activity can only start after first is complete
53 const actualSecondStartTime = Math.max(earliestFirstActivityEnd, secondStartTimes[i]);
54 const totalFinishTime = actualSecondStartTime + secondDurations[i];
55 minimumTotalFinishTime = Math.min(minimumTotalFinishTime, totalFinishTime);
56 }
57
58 return minimumTotalFinishTime;
59}
60
Time and Space Complexity
The time complexity is O(n + m)
, where n
is the length of the land rides arrays (landStartTime
and landDuration
) and m
is the length of the water rides arrays (waterStartTime
and waterDuration
).
Breaking down the time complexity:
- The
calc
function is called twice - Inside each
calc
function call:min(a + t for a, t in zip(a1, t1))
iterates through all elements in the first pair of arrays, takingO(len(a1))
timemin(max(a, min_end) + t for a, t in zip(a2, t2))
iterates through all elements in the second pair of arrays, takingO(len(a2))
time
- First call to
calc
:O(n) + O(m) = O(n + m)
- Second call to
calc
:O(m) + O(n) = O(m + n)
- Finding the minimum of
x
andy
:O(1)
- Total:
O(n + m) + O(m + n) = O(n + m)
The space complexity is O(1)
as the algorithm only uses a constant amount of extra space:
- Variables
min_end
,x
, andy
each store single integer values - The generator expressions in
min()
functions don't create intermediate lists but compute values on-the-fly - No additional data structures are created that scale with input size
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Incorrectly Calculating the Earliest Completion Time
A common mistake is to separately find the minimum start time and minimum duration for each category, then combine them. This approach fails because the ride with the earliest start time might not be the same ride with the shortest duration.
Incorrect approach:
# WRONG: Finding minimum start and duration separately
min_land_start = min(landStartTime)
min_land_duration = min(landDuration)
min_land_end = min_land_start + min_land_duration # This is incorrect!
Why it fails: Consider land rides with:
- Ride A: starts at time 1, duration 10 (ends at time 11)
- Ride B: starts at time 5, duration 2 (ends at time 7)
The incorrect approach would calculate: min_start(1) + min_duration(2) = 3, but you cannot combine attributes from different rides. The actual minimum end time is 7 (from Ride B).
Correct approach:
# CORRECT: Calculate end time for each ride, then find minimum
min_land_end = min(start + duration
for start, duration in zip(landStartTime, landDuration))
Pitfall 2: Forgetting to Consider Both Ride Orders
Another mistake is assuming one specific order (like always doing land rides first) without checking if the opposite order might yield a better result.
Incorrect approach:
# WRONG: Only considering one order
def earliestFinishTime(self, landStartTime, landDuration, waterStartTime, waterDuration):
# Only calculates land → water order
min_land_end = min(s + d for s, d in zip(landStartTime, landDuration))
return min(max(s, min_land_end) + d for s, d in zip(waterStartTime, waterDuration))
Why it fails: Consider:
- Land ride: starts at 10, duration 1 (ends at 11)
- Water ride: starts at 0, duration 2 (ends at 2)
If we do land first: finish at time 11, then water at time 11, total = 13 If we do water first: finish at time 2, then land at time 10, total = 11
The water-first order is clearly better.
Correct approach:
# CORRECT: Check both orders and return minimum
land_first = calculate_finish_time(landStartTime, landDuration,
waterStartTime, waterDuration)
water_first = calculate_finish_time(waterStartTime, waterDuration,
landStartTime, landDuration)
return min(land_first, water_first)
Pitfall 3: Misunderstanding the Waiting Logic
Some might incorrectly assume that if the second ride hasn't opened yet when the first ride finishes, the total time should include a "waiting penalty" or that waiting makes the solution invalid.
Incorrect thinking:
# WRONG: Adding unnecessary complexity for waiting if second_start > first_end: waiting_time = second_start - first_end total_time = first_end + waiting_time + second_duration # Overcomplicating
Correct understanding:
The formula max(second_start, first_end) + second_duration
automatically handles waiting. If we finish the first ride before the second opens, we simply wait until it opens. The max() function elegantly captures this logic without explicit waiting time calculations.
Which algorithm is best for finding the shortest distance between two points in an unweighted graph?
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
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Want a Structured Path to Master System Design Too? Don’t Miss This!