Skip to content

Commit 3928d3f

Browse files
committed
feat(algorithms, graphs): last day you can still cross
1 parent 43f0d1f commit 3928d3f

16 files changed

+492
-0
lines changed

algorithms/graphs/last_day_where_you_can_still_cross/README.md

Lines changed: 282 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
from typing import List, Tuple
2+
from datastructures.sets.union_find import UnionFind
3+
4+
5+
def latest_day_to_cross_binary_search(
6+
row: int, col: int, cells: List[List[int]]
7+
) -> int:
8+
# Binary search for first day where crossing becomes impossible
9+
left, right = 1, len(cells)
10+
# tracks the first day where crossing becomes impossible
11+
first_true_index = -1
12+
13+
cardinal_directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
14+
15+
# Checks to see if a given row and column is within the grid bounds
16+
is_within_bounds = lambda r, c: 0 <= r < row and 0 <= c < col
17+
18+
def can_cross(day: int) -> bool:
19+
"""Check if it is possible to cross from top to bottom on the given day"""
20+
# Build the grid state: Create a 2D grid, mark the first k cells as water (1).
21+
grid = [[0] * col for _ in range(row)]
22+
23+
for i in range(day):
24+
r, c = cells[i]
25+
grid[r - 1][c - 1] = 1
26+
27+
# BFS from top row: Initialize queue with all land cells from row 0.
28+
queue: List[Tuple[int, int]] = []
29+
for j in range(col):
30+
if grid[0][j] == 0:
31+
queue.append((0, j))
32+
grid[0][j] = 1
33+
34+
# BFS exploration: For each cell, check if we've reached the bottom row. Explore all 4 directions, adding
35+
# unvisited land cells to the queue.
36+
idx = 0
37+
while idx < len(queue):
38+
x, y = queue[idx]
39+
idx += 1
40+
41+
# Return True if we reach the bottom row, False otherwise.
42+
if x == row - 1:
43+
return True
44+
45+
for dx, dy in cardinal_directions:
46+
next_x, next_y = x + dx, y + dy
47+
if is_within_bounds(next_x, next_y) and grid[next_x][next_y] == 0:
48+
queue.append((next_x, next_y))
49+
grid[next_x][next_y] = 1
50+
return False
51+
52+
def feasible(day: int) -> bool:
53+
"""Returns true when crossing is Not Possible."""
54+
return not can_cross(day)
55+
56+
# Binary search loop
57+
while left <= right:
58+
mid = (left + right) // 2
59+
60+
if feasible(mid):
61+
first_true_index = mid
62+
right = mid - 1
63+
else:
64+
left = mid + 1
65+
66+
return first_true_index - 1
67+
68+
69+
def last_day_to_cross_union_find(rows: int, cols: int, water_cells):
70+
# create a variable to keep track of the number of days
71+
day = 0
72+
# create the matrix that needs to be crossed
73+
matrix = [[0 for _ in range(cols)] for _ in range(rows)]
74+
# create the two virtual nodes, one before the first column and the other after the last column of the matrix
75+
left_node, right_node = 0, rows * cols + 1
76+
77+
# specify the directions where water can move
78+
water_directions = [
79+
(1, 0),
80+
(0, 1),
81+
(-1, 0),
82+
(0, -1),
83+
(1, 1),
84+
(1, -1),
85+
(-1, 1),
86+
(-1, -1),
87+
]
88+
89+
# convert the water_cells from 1-based to 0-based array for the convenience
90+
water_cells = [(r - 1, c - 1) for r, c in water_cells]
91+
92+
# initialize the UnionFind object, this will create the disjoint set union datastructure, an array - parents
93+
uf = UnionFind(rows * cols + 2)
94+
95+
def find_index(current_row, current_col):
96+
"""maps the index of the element in 2-D matrix to an index of the 1-D array (parents)"""
97+
return current_row * cols + (current_col + 1)
98+
99+
def within_bounds(r, c):
100+
"""checks whether the water cells to be connected are within the bounds of the matrix as per given dimensions"""
101+
if not (0 <= c < cols):
102+
return False
103+
if not (0 <= r < rows):
104+
return False
105+
return True
106+
107+
# On each day, one cell of the matrix will get flooded
108+
for row, col in water_cells:
109+
# change the matrix's cell from land (0) to water (1)
110+
matrix[row][col] = 1
111+
112+
# check if the recently flooded cell connects with any of the existing water cells
113+
for dr, dc in water_directions:
114+
if within_bounds(row + dr, col + dc) and matrix[row + dr][col + dc] == 1:
115+
uf.union(find_index(row, col), find_index((row + dr), (col + dc)))
116+
if col == 0:
117+
uf.union(find_index(row, col), left_node)
118+
if col == cols - 1:
119+
uf.union(find_index(row, col), right_node)
120+
121+
# check if got a series of connected water cells from the left to the right side of the matrix
122+
if uf.find(left_node) == uf.find(right_node):
123+
break
124+
day += 1
125+
126+
return day
62.4 KB
Loading
51.3 KB
Loading
53.8 KB
Loading
63.5 KB
Loading
67.2 KB
Loading
101 KB
Loading
44.7 KB
Loading
156 KB
Loading

0 commit comments

Comments
 (0)