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,59 @@
# 3633. Earliest Finish Time for Land and Water Rides I

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

Difficulty: Easy
Topics: Array, Two Pointers, Binary Search, Greedy, Sorting
Acceptance Rate: 66.9%

## Hints

### Hint 1

The tourist must take exactly one land ride and one water ride, in either order. There are only two "shapes" of plan: land-then-water, or water-then-land. Think about how to evaluate each shape independently, then combine.

### Hint 2

For a fixed ordering (say land first, then water), the finish time depends on a chosen land ride `i` and water ride `j`. Write down the closed-form expression for that finish time and ask yourself: which `i` and `j` minimize it? The `max` of two values (when one is held fixed) is monotonic — that observation lets you decouple the two choices.

### Hint 3

For the land-then-water plan, the finish time is `max(landStart[i] + landDuration[i], waterStart[j]) + waterDuration[j]`. For any fixed `j`, this expression is minimized by picking the `i` that yields the smallest land finish time. So you only need the **minimum land finish time** across all `i`, then sweep over `j`. The water-then-land plan is symmetric. Answer is the min of the two plans.

## Approach

Let `landFinish[i] = landStartTime[i] + landDuration[i]` and `waterFinish[j] = waterStartTime[j] + waterDuration[j]`.

For each pair `(i, j)` we have two candidate finish times:

- **Land then water:** `max(landFinish[i], waterStartTime[j]) + waterDuration[j]`
- **Water then land:** `max(waterFinish[j], landStartTime[i]) + landDuration[i]`

The answer is the minimum of all these candidates.

Naively this is `O(n * m)`, which is fine given the small constraints (`n, m <= 100`). But we can do better by separating the two choices:

- For the **land-then-water** plan, fix `j`. The expression depends on `i` only through `landFinish[i]`, and `max(., waterStartTime[j])` is non-decreasing in its first argument. So the optimal `i` is the one minimizing `landFinish[i]`. Compute `minLandFinish = min_i landFinish[i]` once, then evaluate `max(minLandFinish, waterStartTime[j]) + waterDuration[j]` for every `j` and take the min.
- For the **water-then-land** plan, symmetrically use `minWaterFinish = min_j waterFinish[j]`, then minimize `max(minWaterFinish, landStartTime[i]) + landDuration[i]` over `i`.

Return the minimum of the two best plans.

**Walking through example 1** (`landStartTime = [2,8], landDuration = [4,1], waterStartTime = [6], waterDuration = [3]`):
- `landFinish = [6, 9]`, so `minLandFinish = 6`.
- `waterFinish = [9]`, so `minWaterFinish = 9`.
- Land-then-water best: `max(6, 6) + 3 = 9`.
- Water-then-land best: `min(max(9, 2) + 4, max(9, 8) + 1) = min(13, 10) = 10`.
- Answer: `min(9, 10) = 9`. ✓

## Complexity Analysis

Time Complexity: O(n + m) — one pass to find the minimum finish time in each category, plus one pass over the other category.
Space Complexity: O(1) — only a handful of scalars are tracked.

## Edge Cases

- **Single ride in each category (`n = m = 1`):** Both plans reduce to one candidate each; we still return the min of the two. Example 2 is exactly this case.
- **One category opens much later than the other finishes:** The "wait until open" behavior is captured by `max(prevFinish, otherStartTime)`. Make sure not to start a ride before its `startTime`.
- **All rides in one category share the same start time:** The `min` over that category just picks the shortest duration; nothing special is required.
- **Large durations vs. small start times:** Make sure intermediate sums fit in the integer type. With constraints up to 1000 per value and at most 100 rides per side, a finish time stays well within `int` range.
- **Optimal plan can mix rides differently in each ordering:** The land ride that minimizes the land-then-water plan need not be the same as the one that minimizes the water-then-land plan — that's why we compute each plan independently.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
number: "3633"
frontend_id: "3633"
title: "Earliest Finish Time for Land and Water Rides I"
slug: "earliest-finish-time-for-land-and-water-rides-i"
difficulty: "Easy"
topics:
- "Array"
- "Two Pointers"
- "Binary Search"
- "Greedy"
- "Sorting"
acceptance_rate: 6691.8
is_premium: false
created_at: "2026-06-02T05:09:23.213598+00:00"
fetched_at: "2026-06-02T05:09:23.213598+00:00"
link: "https://leetcode.com/problems/earliest-finish-time-for-land-and-water-rides-i/"
date: "2026-06-02"
---

# 3633. Earliest Finish Time for Land and Water Rides I

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 <= 100`
* `landStartTime.length == landDuration.length == n`
* `waterStartTime.length == waterDuration.length == m`
* `1 <= landStartTime[i], landDuration[i], waterStartTime[j], waterDuration[j] <= 1000`
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

// earliestFinishTime returns the earliest moment a tourist can finish exactly one
// land ride and exactly one water ride, taken in either order.
//
// Approach: For the land-then-water plan, finish time for a pair (i, j) is
// max(landStart[i]+landDuration[i], waterStart[j]) + waterDuration[j]. For any
// fixed j, this is minimized by the i with the smallest land finish time. The
// water-then-land plan is symmetric. So we find min land finish and min water
// finish, then evaluate each plan in a single sweep. Final answer is the min of
// the two plans. Runs in O(n + m).
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
}
}

// Land-then-water: pick the best water ride given the best land finish.
bestLandThenWater := -1
for j := 0; j < len(waterStartTime); j++ {
start := minLandFinish
if waterStartTime[j] > start {
start = waterStartTime[j]
}
finish := start + waterDuration[j]
if bestLandThenWater == -1 || finish < bestLandThenWater {
bestLandThenWater = finish
}
}

// Water-then-land: pick the best land ride given the best water finish.
bestWaterThenLand := -1
for i := 0; i < len(landStartTime); i++ {
start := minWaterFinish
if landStartTime[i] > start {
start = landStartTime[i]
}
finish := start + landDuration[i]
if bestWaterThenLand == -1 || finish < bestWaterThenLand {
bestWaterThenLand = finish
}
}

if bestLandThenWater < bestWaterThenLand {
return bestLandThenWater
}
return bestWaterThenLand
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import "testing"

func TestEarliestFinishTime(t *testing.T) {
tests := []struct {
name string
landStartTime []int
landDuration []int
waterStartTime []int
waterDuration []int
expected int
}{
{
name: "example 1: multiple land rides, single water ride",
landStartTime: []int{2, 8},
landDuration: []int{4, 1},
waterStartTime: []int{6},
waterDuration: []int{3},
expected: 9,
},
{
name: "example 2: water opens early but is long",
landStartTime: []int{5},
landDuration: []int{3},
waterStartTime: []int{1},
waterDuration: []int{10},
expected: 14,
},
{
name: "edge case: both rides open at time 1 with duration 1",
landStartTime: []int{1},
landDuration: []int{1},
waterStartTime: []int{1},
waterDuration: []int{1},
expected: 3,
},
{
name: "edge case: land much later than water finishes, water-then-land wins",
landStartTime: []int{100},
landDuration: []int{1},
waterStartTime: []int{1},
waterDuration: []int{1},
expected: 101,
},
{
name: "edge case: multiple rides on both sides, mixed durations",
landStartTime: []int{1, 10, 20},
landDuration: []int{5, 1, 1},
waterStartTime: []int{2, 15},
waterDuration: []int{4, 1},
expected: 10,
},
{
name: "edge case: max constraints (start=1000, duration=1000)",
landStartTime: []int{1000},
landDuration: []int{1000},
waterStartTime: []int{1000},
waterDuration: []int{1000},
expected: 3000,
},
}

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