from bisect import bisect_right, insort
def avoidFlood(rains):
n = len(rains)
result = [-1 if x > 0 else 1 for x in rains]
lake_to_last_full = dict()
dry_days = []
for i, lake in enumerate(rains):
if lake == 0:
insort(dry_days, i)
else:
if lake in lake_to_last_full:
prev = lake_to_last_full[lake]
idx = bisect_right(dry_days, prev)
if idx == len(dry_days) or dry_days[idx] >= i:
return []
dry_day = dry_days.pop(idx)
result[dry_day] = lake
lake_to_last_full[lake] = i
return result
#include <vector>
#include <unordered_map>
#include <set>
using namespace std;
vector<int> avoidFlood(vector<int>& rains) {
int n = rains.size();
vector<int> result(n, 1);
unordered_map<int, int> lake_to_last_full;
set<int> dry_days;
for (int i = 0; i < n; ++i) {
if (rains[i] == 0) {
dry_days.insert(i);
} else {
result[i] = -1;
if (lake_to_last_full.count(rains[i])) {
int prev = lake_to_last_full[rains[i]];
auto it = dry_days.upper_bound(prev);
if (it == dry_days.end() || *it >= i) {
return {};
}
result[*it] = rains[i];
dry_days.erase(it);
}
lake_to_last_full[rains[i]] = i;
}
}
return result;
}
import java.util.*;
class Solution {
public int[] avoidFlood(int[] rains) {
int n = rains.length;
int[] result = new int[n];
Arrays.fill(result, 1);
Map<Integer, Integer> lakeToLastFull = new HashMap<>();
TreeSet<Integer> dryDays = new TreeSet<>();
for (int i = 0; i < n; ++i) {
if (rains[i] == 0) {
dryDays.add(i);
} else {
result[i] = -1;
if (lakeToLastFull.containsKey(rains[i])) {
int prev = lakeToLastFull.get(rains[i]);
Integer dryDay = dryDays.higher(prev);
if (dryDay == null || dryDay >= i) {
return new int[0];
}
result[dryDay] = rains[i];
dryDays.remove(dryDay);
}
lakeToLastFull.put(rains[i], i);
}
}
return result;
}
}
function avoidFlood(rains) {
const n = rains.length;
const result = rains.map(x => x > 0 ? -1 : 1);
const lakeToLastFull = new Map();
const dryDays = [];
function binarySearch(val) {
let left = 0, right = dryDays.length;
while (left < right) {
let mid = (left + right) >> 1;
if (dryDays[mid] <= val) left = mid + 1;
else right = mid;
}
return left;
}
for (let i = 0; i < n; ++i) {
const lake = rains[i];
if (lake === 0) {
// Insert in sorted order
let idx = binarySearch(i);
dryDays.splice(idx, 0, i);
} else {
if (lakeToLastFull.has(lake)) {
const prev = lakeToLastFull.get(lake);
const idx = binarySearch(prev);
if (idx === dryDays.length || dryDays[idx] >= i) {
return [];
}
const dryDay = dryDays[idx];
result[dryDay] = lake;
dryDays.splice(idx, 1);
}
lakeToLastFull.set(lake, i);
}
}
return result;
}
You are given an array rains
where rains[i]
represents the lake that will be filled with rain on day i
, or 0
if it is a dry day (no rain). Each positive number represents a specific lake. If a lake receives rain while it's already full (i.e., hasn't been dried since it was last filled), it will flood the city. On dry days (when rains[i] == 0
), you can choose exactly one lake to dry (remove all water from it), or do nothing.
The task is to return an array ans
of the same length as rains
:
rains[i] > 0
, then ans[i] == -1
(since you can't dry a lake on a rainy day).rains[i] == 0
, then ans[i]
should be the lake number you choose to dry on that day (if needed), or any arbitrary lake number if it doesn't matter.Constraints: Each dry day can be used to dry only one lake, and you cannot dry the same lake twice in a row without it being refilled. There will always be at most one valid solution.
At first glance, the problem seems to require checking every possible way to use dry days to prevent floods, which could be very inefficient. A brute-force approach would try all combinations of drying lakes on dry days, but with many dry days and lakes, this quickly becomes infeasible.
Instead, we need an optimized strategy. The key insight is that a lake only needs to be dried if it will rain into it again before it is dried. So, we track for each lake the last day it was filled. When it rains into a lake that is already full, we must have used a prior dry day (between the previous rain and now) to dry it.
The challenge is to efficiently find, for each such situation, a suitable dry day that hasn't already been used and is in the right interval. This leads us to use efficient data structures to track available dry days and quickly assign them to lakes as needed.
Let's break down the steps to solve this problem efficiently:
rains[i] == 0
).
rains[i] > 0
), check if the lake is already full (i.e., exists in the map).ans[i]
to the lake number. For unused dry days, any arbitrary value (e.g., 1) is acceptable.
This approach ensures that we always dry lakes just in time before they're filled again, using efficient lookups and assignments.
Let's walk through a sample input:
rains = [1,2,0,1,2]
ans[0] = -1
ans[1] = -1
ans[2] = 1
(for now, will decide whom to dry later).ans[2] = 1
. Remove 2 from dry days.[]
.In this case, it is impossible to avoid a flood.
Now, consider rains = [1,2,0,0,2,1]
:
ans[0] = -1
ans[1] = -1
ans[2] = 2
. Remove 2 from dry days.ans[3] = 1
. Remove 3 from dry days.Final ans = [-1, -1, 2, 1, -1, -1]
, successfully avoiding floods.
Brute-force approach: Would involve trying every possible assignment of lakes to dry days, which is exponential in the number of dry days, i.e., O(2^D) where D is the number of dry days. This is not feasible for large inputs.
Optimized approach:
The "Avoid Flood in The City" problem is efficiently solved by tracking the last fill day for each lake and using a sorted structure to manage dry days. By always assigning dry days to the lakes that need them just before they flood, and using efficient lookups, we ensure no floods occur (if possible) in O(N log N) time. The solution is elegant because it reduces a seemingly combinatorial problem to a sequence of greedy and data-structure-driven decisions.