LeetCode 1970 - Last Day Where You Can Still Cross
This problem asks us to find the last day we can cross a grid from the top row to the bottom row, walking only on land. The grid is initially all land (0), and each day, specific cells are flooded with water (1) according to the cells array.
Difficulty: 🔴 Hard
Topics: Array, Binary Search, Depth-First Search, Breadth-First Search, Union-Find, Matrix
Solution
Problem Understanding
This problem asks us to find the last day we can cross a grid from the top row to the bottom row, walking only on land. The grid is initially all land (0), and each day, specific cells are flooded with water (1) according to the cells array. Each entry cells[i] = [ri, ci] tells us that on day i + 1 (1-based), the cell at row ri and column ci becomes water.
The input consists of row and col defining the grid size, and a cells list that will flood every cell over time. The output is the last day (1-based) where it is still possible to traverse from any top row cell to any bottom row cell using only land cells and moving in four directions (up, down, left, right).
Constraints reveal that the matrix can be up to 2 * 10^4 cells in total, and all cells values are unique. This implies that any naive approach checking each day linearly with full traversals would be inefficient. The problem guarantees that each cell is flooded exactly once, so we do not need to handle repeated flooding events.
Important edge cases include a very narrow grid (like 2xN), a grid where flooding occurs in a single column or row first, or situations where the last top-to-bottom path is blocked late in the sequence.
Approaches
Brute Force
A brute-force approach would simulate the flooding day by day. On each day, we would mark the corresponding cell as water and then perform a search from the top row to see if any path to the bottom row exists. This search could be BFS or DFS. We would continue until no path exists and return the last day when a path was possible.
While correct, this approach is too slow because performing BFS/DFS on a grid of size row * col for potentially every day (up to row * col days) would result in O((row * col)^2) time complexity, which is infeasible for large grids.
Optimal Approach
The key observation is that the problem is monotonic: once a path is blocked on a certain day, all subsequent days will also be blocked. This allows us to use binary search over the day number, combined with BFS or DFS to check connectivity for a given day.
We can define a helper function canCross(day) that builds the grid after flooding all cells up to that day, then checks if a top-to-bottom path exists. Binary search over the day range [1, len(cells)] allows us to find the last day efficiently.
Union-Find is another alternative to BFS for connectivity checks, but BFS/DFS is simpler to implement and understand for this problem.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O((row * col)^2) | O(row * col) | Flood day by day, BFS/DFS to check path |
| Binary Search + BFS | O(row * col * log(row * col)) | O(row * col) | Binary search over days with BFS to check paths |
Algorithm Walkthrough
- Binary Search Setup: Initialize
left = 1andright = row * col. We will search for the last day where crossing is possible. - Flood Grid Function: For a given day
d, create a grid where the firstdcells fromcellsare water and the rest are land. - Path Check Function: Use BFS or DFS starting from all land cells in the top row. Traverse only land cells. If any cell in the bottom row is reached, return True.
- Binary Search Iteration: While
left <= right, computemid = (left + right) // 2. UsecanCross(mid)to check if crossing is possible on that day. - Update Search Range: If
canCross(mid)is True, movelefttomid + 1(we can try later days). If False, moverighttomid - 1(crossing not possible, try earlier day). - Return Result: After the binary search completes,
rightwill be the last day crossing is possible.
Why it works: The invariant is that crossing is possible for all days <= last possible day and impossible for days > last day. Binary search efficiently finds the transition point.
Python Solution
from collections import deque
from typing import List
class Solution:
def latestDayToCross(self, row: int, col: int, cells: List[List[int]]) -> int:
def canCross(day: int) -> bool:
grid = [[0] * col for _ in range(row)]
for i in range(day):
r, c = cells[i]
grid[r-1][c-1] = 1
queue = deque()
for c in range(col):
if grid[0][c] == 0:
queue.append((0, c))
grid[0][c] = 1 # mark visited
directions = [(0,1),(1,0),(0,-1),(-1,0)]
while queue:
r, c = queue.popleft()
if r == row - 1:
return True
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 <= nr < row and 0 <= nc < col and grid[nr][nc] == 0:
grid[nr][nc] = 1
queue.append((nr, nc))
return False
left, right = 1, row * col
last_day = 0
while left <= right:
mid = (left + right) // 2
if canCross(mid):
last_day = mid
left = mid + 1
else:
right = mid - 1
return last_day
Explanation: We first define canCross(day) to simulate the grid after day floods and perform BFS from the top row. During BFS, visited land cells are marked as water to avoid revisiting. Binary search then efficiently finds the last day where a path exists.
Go Solution
package main
func latestDayToCross(row int, col int, cells [][]int) int {
canCross := func(day int) bool {
grid := make([][]int, row)
for i := range grid {
grid[i] = make([]int, col)
}
for i := 0; i < day; i++ {
r, c := cells[i][0]-1, cells[i][1]-1
grid[r][c] = 1
}
type point struct{ r, c int }
queue := []point{}
for c := 0; c < col; c++ {
if grid[0][c] == 0 {
queue = append(queue, point{0, c})
grid[0][c] = 1
}
}
directions := []point{{0,1},{1,0},{0,-1},{-1,0}}
for len(queue) > 0 {
p := queue[0]
queue = queue[1:]
if p.r == row-1 {
return true
}
for _, d := range directions {
nr, nc := p.r + d.r, p.c + d.c
if nr >= 0 && nr < row && nc >= 0 && nc < col && grid[nr][nc] == 0 {
grid[nr][nc] = 1
queue = append(queue, point{nr, nc})
}
}
}
return false
}
left, right := 1, row * col
lastDay := 0
for left <= right {
mid := (left + right) / 2
if canCross(mid) {
lastDay = mid
left = mid + 1
} else {
right = mid - 1
}
}
return lastDay
}
Go-specific notes: We use slices to represent the grid and a slice-based queue for BFS. The point struct helps manage coordinates. The logic mirrors the Python implementation, handling 0-based indexing carefully for the grid.
Worked Examples
Example 1: row = 2, col = 2, cells = [[1,1],[2,1],[1,2],[2,2]]
| Day | Flooded Cells | BFS Path Exists? |
|---|---|---|
| 1 | [1,1] | Yes (1,2 → 2,2) |
| 2 | [1,1],[2,1] | Yes (1,2 → 2,2) |
| 3 | [1,1],[2,1],[1,2] | No |
| 4 | All | No |
Last day = 2.
Example 2: row = 2, col = 2, cells = [[1,1],[1,2],[2,1],[2,2]]
| Day | Flooded Cells | BFS Path Exists? |
|---|---|---|
| 1 | [1,1] | Yes (1,2 → 2,2) |
| 2 | [1,1],[1,2] |