LeetCode 1488 - Avoid Flood in The City
The problem asks us to simulate rainfall over a series of lakes and prevent flooding. Each day is represented by an inte
Difficulty: 🟡 Medium
Topics: Array, Hash Table, Binary Search, Greedy, Heap (Priority Queue)
Solution
Problem Understanding
The problem asks us to simulate rainfall over a series of lakes and prevent flooding. Each day is represented by an integer in the rains array. If rains[i] > 0, it means lake rains[i] receives rain and becomes full. If it is already full, it floods, which we must avoid. If rains[i] == 0, that day is sunny and we have the option to dry exactly one lake of our choosing. The goal is to return an array ans representing the daily actions: -1 on rainy days and the lake to dry on sunny days. If it is impossible to avoid flooding, we return an empty array.
Key points are:
- Lakes are numbered up to 10^9, so we cannot use a fixed-size array indexed by lake number. Instead, we need a hash map to track full lakes.
- Sunny days must be strategically used to dry lakes that will receive rain in the future to avoid flooding.
- Multiple valid outputs may exist because the problem allows any flood-free drying sequence.
- Input size can be up to 10^5 days, so naive approaches will be too slow.
Important edge cases include consecutive rains on the same lake without an intervening dry day, multiple consecutive sunny days, and lakes that never fill again after being dried.
Approaches
The brute-force approach would simulate each day and, on sunny days, try all possible lakes to dry to prevent future flooding. On rainy days, we check all currently full lakes to see if the next rain will cause flooding. This is correct logically, but for each sunny day, we may need to search through all full lakes, making it extremely slow for large input sizes. The complexity is at least O(n^2), which is infeasible for n = 10^5.
The optimal approach uses two key observations. First, we only need to dry lakes that will be rained on again while they are full. Second, we can track the next rainy day for each lake using a hash map and schedule drying on the earliest sunny day available before the next rain. This can be efficiently implemented with a hash map for full lakes and a sorted data structure (like a SortedList in Python or a min-heap) for sunny days. This way, for each rain, we can quickly find the nearest available sunny day to dry the lake before it floods.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^2) | O(n) | Tries all possible lakes to dry on sunny days, too slow for large inputs |
| Optimal | O(n log n) | O(n) | Uses hash map and sorted set/min-heap to efficiently assign dry days before next rain |
Algorithm Walkthrough
- Initialize a hash map
full_lakesto track the last day a lake was filled. - Initialize a sorted list or min-heap
dry_daysto track indices of sunny days available for drying. - Initialize an array
ansof lengthnwith default values1on sunny days (can be overwritten) and-1on rainy days. - Iterate over the
rainsarray by index and value. - If
rains[i] > 0(rainy day):
- Check if the lake is already full in
full_lakes. If it is, searchdry_daysfor a sunny day after the last fill day to dry this lake. - If no such sunny day exists, return
[]because a flood is unavoidable. - Otherwise, assign that sunny day to dry this lake in
ansand remove the day fromdry_days. - Update
full_lakes[lake] = i.
- If
rains[i] == 0(sunny day):
- Add the current index
itodry_daysas an available day to dry some lake later.
- After iterating, all rainy days are
-1and sunny days are either assigned to dry a lake or can remain arbitrary (e.g.,1). - Return
ans.
Why it works: By always drying a lake on the earliest available sunny day before it receives rain again, we ensure no lake ever floods. The sorted set/min-heap ensures efficient retrieval of the correct day.
Python Solution
from typing import List
import bisect
class Solution:
def avoidFlood(self, rains: List[int]) -> List[int]:
full_lakes = {}
dry_days = []
ans = [-1 if rain > 0 else 1 for rain in rains]
for i, lake in enumerate(rains):
if lake > 0:
if lake in full_lakes:
idx = bisect.bisect_right(dry_days, full_lakes[lake])
if idx == len(dry_days):
return []
dry_day_index = dry_days.pop(idx)
ans[dry_day_index] = lake
full_lakes[lake] = i
else:
dry_days.append(i)
return ans
Explanation: We initialize full_lakes to remember the last day each lake was filled. For sunny days, we track them in dry_days. When rain falls on a full lake, we use binary search on dry_days to find the earliest available sunny day after the last fill day to dry the lake. If none exists, flooding is unavoidable. Otherwise, we assign that sunny day to dry the lake.
Go Solution
package main
import (
"sort"
)
func avoidFlood(rains []int) []int {
n := len(rains)
ans := make([]int, n)
for i, rain := range rains {
if rain == 0 {
ans[i] = 1
} else {
ans[i] = -1
}
}
fullLakes := make(map[int]int)
var dryDays []int
for i, lake := range rains {
if lake > 0 {
if lastDay, ok := fullLakes[lake]; ok {
idx := sort.Search(len(dryDays), func(j int) bool { return dryDays[j] > lastDay })
if idx == len(dryDays) {
return []int{}
}
dryDayIndex := dryDays[idx]
ans[dryDayIndex] = lake
dryDays = append(dryDays[:idx], dryDays[idx+1:]...)
}
fullLakes[lake] = i
} else {
dryDays = append(dryDays, i)
}
}
return ans
}
Go-specific notes: Since Go does not have a built-in sorted list with binary search insert, we maintain dryDays as a slice and use sort.Search to find the appropriate index. Removal of an element requires slice slicing.
Worked Examples
Example 2: rains = [1,2,0,0,2,1]
| Day | rains[i] | full_lakes | dry_days | ans |
|---|---|---|---|---|
| 0 | 1 | {1:0} | [] | -1 |
| 1 | 2 | {1:0, 2:1} | [] | -1 |
| 2 | 0 | {1:0, 2:1} | [2] | 1 (default 1) |
| 3 | 0 | {1:0, 2:1} | [2,3] | 1 (default 1) |
| 4 | 2 | {1:0, 2:4} | [3] | assign day 2 to dry lake 2 → ans[2]=2 |
| 5 | 1 | {1:5,2:4} | [3] | assign day 3 to dry lake 1 → ans[3]=1 |
Final output: [-1,-1,2,1,-1,-1]
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n) | Each rainy day may trigger a binary search over dry_days of length ≤ n |
| Space | O(n) | full_lakes hash map and dry_days list store up to n elements |
The algorithm is efficient because each lake is only tracked once per rain, and each sunny day is used at most once.
Test Cases
# Provided examples
assert Solution().avoidFlood([1,2,3,4]) == [-1,-1,-1,-1] # no floods, no dry needed
assert Solution().avoidFlood([1,2,0,0,2,1]) in ([-1,-1,2,1,-1,-1], [-1,-1,1,2,-1,-1]) # multiple valid answers
assert Solution().avoidFlood([1,2,0,1,2]) == [] # impossible to avoid flood
# Edge cases
assert Solution().avoidFlood([1,1]) == [] # consecutive rain on same lake, no dry days
assert Solution().avoidFlood([0,1,0,1]) == [1,-1,1,-1] # alternate dry and rain
assert Solution().avoidFlood([0,0,0,1,1]) == [1,1,1,-1,-1] # multiple sunny days before rain
assert Solution().avoidFlood([1]) == [-1]