Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 3635. Earliest Finish Time for Land and Water Rides II

[LeetCode Link](https://leetcode.com/problems/earliest-finish-time-for-land-and-water-rides-ii/)

Difficulty: Medium
Topics: Array, Two Pointers, Binary Search, Greedy, Sorting

Acceptance Rate: 52.9%

## Hints

### Hint 1

The brute force is to try every pair of (land ride, water ride) in both orders — that is `O(n * m)` combinations. With `n` and `m` up to `5 * 10^4` each, that's `2.5 * 10^9` pairs, far too slow. Ask yourself: do you really need to look at every *pair*, or can you decouple the two categories?

### Hint 2

Consider just one ordering, say **land first, then water**. If you commit to a specific water ride `j`, what is the *best* land ride to pair it with? Notice the finish time only depends on the land ride through a single number: when that land ride finishes. So you don't need to know which land ride you used — you only need the smallest possible value of that number.

### Hint 3

For the "land then water" ordering, the finish time when pairing some land ride with water ride `j` is `max(landFinish, waterStartTime[j]) + waterDuration[j]`. This is *monotonic* in `landFinish`, so the best choice is always the land ride with the **minimum** `landStartTime[i] + landDuration[i]`. Precompute that single minimum once, then sweep every water ride. Do the symmetric thing for "water then land". The two minimums collapse the problem from `O(n*m)` to `O(n+m)`.

## Approach

A valid plan picks exactly one land ride and one water ride and runs them back-to-back in one of two orders. We handle each order independently.

**Order 1 — land ride first, then water ride.**
If the land ride finishes at time `f`, then the water ride `j` can start no earlier than `max(f, waterStartTime[j])` and finishes at `max(f, waterStartTime[j]) + waterDuration[j]`. For a fixed `j`, this expression is non-decreasing in `f`, so among all land rides we always prefer the one with the smallest finish time. Define:

```
minLandFinish = min over i of (landStartTime[i] + landDuration[i])
```

Then the best "land first" result is:

```
min over j of ( max(minLandFinish, waterStartTime[j]) + waterDuration[j] )
```

**Order 2 — water ride first, then land ride.**
By identical reasoning, define:

```
minWaterFinish = min over j of (waterStartTime[j] + waterDuration[j])
```

and the best "water first" result is:

```
min over i of ( max(minWaterFinish, landStartTime[i]) + landDuration[i] )
```

The answer is the minimum of the two orderings.

**Why the decoupling is valid.** The only way the first ride influences the second is through the first ride's finish time, and the second-ride finish time is monotonically non-decreasing in that value. So fixing "use the earliest-finishing ride of the first category" is never worse than any other choice. This greedy collapse is what lets us avoid examining all pairs.

**Walkthrough of Example 1:** `landStartTime = [2,8]`, `landDuration = [4,1]`, `waterStartTime = [6]`, `waterDuration = [3]`.
- Land finishes: `2+4=6`, `8+1=9` → `minLandFinish = 6`.
- Water finishes: `6+3=9` → `minWaterFinish = 9`.
- Land first: for water ride 0, `max(6, 6) + 3 = 9`.
- Water first: for land ride 0, `max(9, 2) + 4 = 13`; for land ride 1, `max(9, 8) + 1 = 10`. Best `= 10`.
- Answer `= min(9, 10) = 9`. ✓

## Complexity Analysis

Time Complexity: O(n + m) — two linear passes to compute the minimums, two linear passes to sweep the other category.
Space Complexity: O(1) — only a handful of scalar accumulators are kept.

## Edge Cases

- **Single ride in each category (`n = m = 1`):** both orderings still get evaluated; the formula handles it without special casing.
- **Second ride opens long before the first finishes:** the `max(...)` clamps the start to the first ride's finish time, so waiting is never modeled as starting early. Example 2 exercises this (water opens at 1 but can't start until land/water predecessor frees up).
- **First ride finishes before the second ride even opens:** then `max(minFinish, startTime) == startTime`, i.e. the tourist waits idle for the second ride to open — the `max` captures this correctly.
- **Large values / overflow:** with values up to `10^5`, a finish time stays well within `int` range, but the reasoning generalizes; using Go's `int` (64-bit on typical platforms) is safe.
- **It is never optimal to pick a slower-finishing first ride** — the monotonic argument guarantees the single minimum suffices, so don't be tempted to keep more candidates than that.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
number: "3635"
frontend_id: "3635"
title: "Earliest Finish Time for Land and Water Rides II"
slug: "earliest-finish-time-for-land-and-water-rides-ii"
difficulty: "Medium"
topics:
- "Array"
- "Two Pointers"
- "Binary Search"
- "Greedy"
- "Sorting"
acceptance_rate: 5293.4
is_premium: false
created_at: "2026-06-03T05:24:13.050562+00:00"
fetched_at: "2026-06-03T05:24:13.050562+00:00"
link: "https://leetcode.com/problems/earliest-finish-time-for-land-and-water-rides-ii/"
date: "2026-06-03"
---

# 3635. Earliest Finish Time for Land and Water Rides II

You are given two categories of theme park attractions: **land rides** and **water rides**.

* **Land rides**
* `landStartTime[i]` - the earliest time the `ith` land ride can be boarded.
* `landDuration[i]` - how long the `ith` land ride lasts.
* **Water rides**
* `waterStartTime[j]` - the earliest time the `jth` water ride can be boarded.
* `waterDuration[j]` - how long the `jth` water ride lasts.



A tourist must experience **exactly one** ride from **each** category, in **either order**.

* A ride may be started at its opening time or **any later moment**.
* If a ride is started at time `t`, it finishes at time `t + duration`.
* Immediately after finishing one ride the tourist may board the other (if it is already open) or wait until it opens.



Return the **earliest possible time** at which the tourist can finish both rides.



**Example 1:**

**Input:** landStartTime = [2,8], landDuration = [4,1], waterStartTime = [6], waterDuration = [3]

**Output:** 9

**Explanation:** ​​​​​​​

* Plan A (land ride 0 -> water ride 0):
* Start land ride 0 at time `landStartTime[0] = 2`. Finish at `2 + landDuration[0] = 6`.
* Water ride 0 opens at time `waterStartTime[0] = 6`. Start immediately at `6`, finish at `6 + waterDuration[0] = 9`.
* Plan B (water ride 0 -> land ride 1):
* Start water ride 0 at time `waterStartTime[0] = 6`. Finish at `6 + waterDuration[0] = 9`.
* Land ride 1 opens at `landStartTime[1] = 8`. Start at time `9`, finish at `9 + landDuration[1] = 10`.
* Plan C (land ride 1 -> water ride 0):
* Start land ride 1 at time `landStartTime[1] = 8`. Finish at `8 + landDuration[1] = 9`.
* Water ride 0 opened at `waterStartTime[0] = 6`. Start at time `9`, finish at `9 + waterDuration[0] = 12`.
* Plan D (water ride 0 -> land ride 0):
* Start water ride 0 at time `waterStartTime[0] = 6`. Finish at `6 + waterDuration[0] = 9`.
* Land ride 0 opened at `landStartTime[0] = 2`. Start at time `9`, finish at `9 + landDuration[0] = 13`.



Plan A gives the earliest finish time of 9.

**Example 2:**

**Input:** landStartTime = [5], landDuration = [3], waterStartTime = [1], waterDuration = [10]

**Output:** 14

**Explanation:** ​​​​​​​

* Plan A (water ride 0 -> land ride 0):
* Start water ride 0 at time `waterStartTime[0] = 1`. Finish at `1 + waterDuration[0] = 11`.
* Land ride 0 opened at `landStartTime[0] = 5`. Start immediately at `11` and finish at `11 + landDuration[0] = 14`.
* Plan B (land ride 0 -> water ride 0):
* Start land ride 0 at time `landStartTime[0] = 5`. Finish at `5 + landDuration[0] = 8`.
* Water ride 0 opened at `waterStartTime[0] = 1`. Start immediately at `8` and finish at `8 + waterDuration[0] = 18`.



Plan A provides the earliest finish time of 14.**​​​​​​​**



**Constraints:**

* `1 <= n, m <= 5 * 104`
* `landStartTime.length == landDuration.length == n`
* `waterStartTime.length == waterDuration.length == m`
* `1 <= landStartTime[i], landDuration[i], waterStartTime[j], waterDuration[j] <= 105`
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

// Earliest Finish Time for Land and Water Rides II
//
// A plan runs one land ride and one water ride back-to-back in one of two
// orders. The second ride's finish time depends on the first ride only through
// the first ride's finish time, and is monotonically non-decreasing in it.
// So for each ordering we only need the earliest-finishing ride of the first
// category, then sweep the second category once. This collapses the naive
// O(n*m) pairing into an O(n+m) greedy.
func earliestFinishTime(landStartTime []int, landDuration []int, waterStartTime []int, waterDuration []int) int {
minLandFinish := landStartTime[0] + landDuration[0]
for i := 1; i < len(landStartTime); i++ {
if f := landStartTime[i] + landDuration[i]; f < minLandFinish {
minLandFinish = f
}
}

minWaterFinish := waterStartTime[0] + waterDuration[0]
for j := 1; j < len(waterStartTime); j++ {
if f := waterStartTime[j] + waterDuration[j]; f < minWaterFinish {
minWaterFinish = f
}
}

best := -1

// Order 1: land ride first, then water ride j.
for j := range waterStartTime {
finish := max(minLandFinish, waterStartTime[j]) + waterDuration[j]
if best == -1 || finish < best {
best = finish
}
}

// Order 2: water ride first, then land ride i.
for i := range landStartTime {
finish := max(minWaterFinish, landStartTime[i]) + landDuration[i]
if finish < best {
best = finish
}
}

return best
}

func max(a, b int) int {
if a > b {
return a
}
return b
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import "testing"

func TestSolution(t *testing.T) {
tests := []struct {
name string
landStartTime []int
landDuration []int
waterStartTime []int
waterDuration []int
expected int
}{
{
name: "example 1: two land rides, one water ride",
landStartTime: []int{2, 8},
landDuration: []int{4, 1},
waterStartTime: []int{6},
waterDuration: []int{3},
expected: 9,
},
{
name: "example 2: water first beats land first",
landStartTime: []int{5},
landDuration: []int{3},
waterStartTime: []int{1},
waterDuration: []int{10},
expected: 14,
},
{
name: "edge case: single ride each, no waiting",
landStartTime: []int{1},
landDuration: []int{1},
waterStartTime: []int{1},
waterDuration: []int{1},
expected: 3,
},
{
name: "edge case: second ride opens far in the future forces a wait",
landStartTime: []int{1},
landDuration: []int{2},
waterStartTime: []int{100},
waterDuration: []int{5},
expected: 105,
},
{
name: "edge case: best land ride for ordering is not the lowest index",
landStartTime: []int{10, 1},
landDuration: []int{10, 1},
waterStartTime: []int{1},
waterDuration: []int{1},
expected: 3,
},
{
name: "edge case: multiple rides on both sides",
landStartTime: []int{3, 7},
landDuration: []int{2, 1},
waterStartTime: []int{4, 9},
waterDuration: []int{2, 1},
expected: 7,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := earliestFinishTime(tt.landStartTime, tt.landDuration, tt.waterStartTime, tt.waterDuration)
if result != tt.expected {
t.Errorf("earliestFinishTime(%v, %v, %v, %v) = %d, want %d",
tt.landStartTime, tt.landDuration, tt.waterStartTime, tt.waterDuration, result, tt.expected)
}
})
}
}