diff --git a/problems/3633-earliest-finish-time-for-land-and-water-rides-i/analysis.md b/problems/3633-earliest-finish-time-for-land-and-water-rides-i/analysis.md new file mode 100644 index 0000000..f5c7a8e --- /dev/null +++ b/problems/3633-earliest-finish-time-for-land-and-water-rides-i/analysis.md @@ -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. diff --git a/problems/3633-earliest-finish-time-for-land-and-water-rides-i/problem.md b/problems/3633-earliest-finish-time-for-land-and-water-rides-i/problem.md new file mode 100644 index 0000000..bd99b4f --- /dev/null +++ b/problems/3633-earliest-finish-time-for-land-and-water-rides-i/problem.md @@ -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` diff --git a/problems/3633-earliest-finish-time-for-land-and-water-rides-i/solution.go b/problems/3633-earliest-finish-time-for-land-and-water-rides-i/solution.go new file mode 100644 index 0000000..dbb820b --- /dev/null +++ b/problems/3633-earliest-finish-time-for-land-and-water-rides-i/solution.go @@ -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 +} diff --git a/problems/3633-earliest-finish-time-for-land-and-water-rides-i/solution_test.go b/problems/3633-earliest-finish-time-for-land-and-water-rides-i/solution_test.go new file mode 100644 index 0000000..5164be2 --- /dev/null +++ b/problems/3633-earliest-finish-time-for-land-and-water-rides-i/solution_test.go @@ -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) + } + }) + } +}