Swim in Rising Water
Picture
In [[0,3],[1,2]], water level 2 opens a path 0→1→2. The square at height 3 is unnecessary. Instead of constructing routes, ask when the start and finish first become connected.
Predict
What happens to connectivity as the water level rises?
Walk through the small example
Cells activate and connected groups can only merge. No open cell disappears, which is exactly the update direction ordinary union-find supports.
Mental model and complete Python solution
Sort cells by elevation. Activate the next cell, joining its active neighbors. Once both corners are active and share a root, the current elevation is the answer. Any lower elevation was already tested without connecting them. Equal heights, if present in a generalized input, may activate one at a time; any connection formed during that height still has that same threshold. The original problem guarantees unique heights. An equivalent minimax Dijkstra uses maximum cell elevation along the path, including the starting elevation.
What the function promises: For a nonempty square elevation grid, return the minimum water level enabling a four-direction corner-to-corner route. Swimming adds no elapsed travel time. The helper also supports rectangular grids.
class DisjointSet:
def __init__(self, n):
self.parent = list(range(n))
self.size = [1] * n
def find(self, x):
while x != self.parent[x]:
self.parent[x] = self.parent[self.parent[x]]
x = self.parent[x]
return x
def union(self, a, b):
a, b = self.find(a), self.find(b)
if a == b:
return False
if self.size[a] < self.size[b]:
a, b = b, a
self.parent[b] = a
self.size[a] += self.size[b]
return True
def swim_level(grid):
rows, cols = len(grid), len(grid[0])
groups = DisjointSet(rows * cols)
active = [False] * (rows * cols)
cells = sorted((grid[r][c], r, c)
for r in range(rows) for c in range(cols))
for level, r, c in cells:
node = r * cols + c
active[node] = True
for nr, nc in ((r-1,c), (r+1,c), (r,c-1), (r,c+1)):
if 0 <= nr < rows and 0 <= nc < cols:
other = nr * cols + nc
if active[other]:
groups.union(node, other)
if active[0] and active[-1] and groups.find(0) == groups.find(rows*cols-1):
return level
assert swim_level([[0, 3], [1, 2]]) == 2
assert swim_level([[0, 2], [1, 3]]) == 3
assert swim_level([[7]]) == 7
Work and storage: O(RC log(RC+1)) time for sorting plus near-linear unions, and O(RC) space. DSU alone does not return the route; keep another representation if a route is requested.
When to recognize it: As the level rises, more cells activate and components only merge. Check when the corners first share a group.
Check before applying it: Being in one group tells us reachability, not route length. Water that rises and falls introduces deletions ordinary union-find cannot handle.
Change one rule
Water can rise and fall, and you must answer connectivity after each change. Does the same DSU update still work?
See what changes
Not unchanged. Falling water removes vertices or edges and can split groups. Consider offline processing or a dynamic-connectivity method, or recompute for smaller constraints.
Connect
Making A Large Island: Reuse cell groups, now maintained across increasing thresholds.
Path With Minimum Effort: The same answer is the smallest possible maximum height along a path.
Problem reference: Swim in Rising Water (Hard). The walkthrough and Python solution above are original course material.