你能穿过矩阵的最后一天
难度:
标签:
题目描述
给你一个下标从 1 开始的二进制矩阵,其中 0
表示陆地,1
表示水域。同时给你 row
和 col
分别表示矩阵中行和列的数目。
一开始在第 0
天,整个 矩阵都是 陆地 。但每一天都会有一块新陆地被 水 淹没变成水域。给你一个下标从 1 开始的二维数组 cells
,其中 cells[i] = [ri, ci]
表示在第 i
天,第 ri
行 ci
列(下标都是从 1 开始)的陆地会变成 水域 (也就是 0
变成 1
)。
你想知道从矩阵最 上面 一行走到最 下面 一行,且只经过陆地格子的 最后一天 是哪一天。你可以从最上面一行的 任意 格子出发,到达最下面一行的 任意 格子。你只能沿着 四个 基本方向移动(也就是上下左右)。
请返回只经过陆地格子能从最 上面 一行走到最 下面 一行的 最后一天 。
示例 1:

输入:row = 2, col = 2, cells = [[1,1],[2,1],[1,2],[2,2]] 输出:2 解释:上图描述了矩阵从第 0 天开始是如何变化的。 可以从最上面一行到最下面一行的最后一天是第 2 天。
示例 2:

输入:row = 2, col = 2, cells = [[1,1],[1,2],[2,1],[2,2]] 输出:1 解释:上图描述了矩阵从第 0 天开始是如何变化的。 可以从最上面一行到最下面一行的最后一天是第 1 天。
示例 3:

输入:row = 3, col = 3, cells = [[1,2],[2,1],[3,3],[2,2],[1,1],[1,3],[2,3],[3,2],[3,1]] 输出:3 解释:上图描述了矩阵从第 0 天开始是如何变化的。 可以从最上面一行到最下面一行的最后一天是第 3 天。
提示:
2 <= row, col <= 2 * 104
4 <= row * col <= 2 * 104
cells.length == row * col
1 <= ri <= row
1 <= ci <= col
cells
中的所有格子坐标都是 唯一 的。
代码结果
运行时间: 126 ms, 内存: 24.4 MB
/*
思路:
1. 初始化一个row x col的矩阵,初始全为0。
2. 依次将cells中的位置变为1,记录每一天的变化。
3. 使用二分查找找到能够从上到下通过陆地的最后一天。
4. 使用流处理来代替循环和条件判断。
*/
import java.util.Arrays;
import java.util.stream.IntStream;
public class Solution {
private int[] directions = {-1, 0, 1, 0, -1};
public int latestDayToCross(int row, int col, int[][] cells) {
return IntStream.rangeClosed(1, cells.length)
.reduce((left, right) -> {
int mid = right - (right - left) / 2;
return canWalk(row, col, cells, mid) ? mid : mid - 1;
}).getAsInt();
}
private boolean canWalk(int row, int col, int[][] cells, int day) {
int[][] grid = new int[row][col];
Arrays.stream(cells, 0, day).forEach(cell -> grid[cell[0] - 1][cell[1] - 1] = 1);
return IntStream.range(0, col).anyMatch(i -> grid[0][i] == 0 && dfs(grid, 0, i));
}
private boolean dfs(int[][] grid, int x, int y) {
if (x == grid.length - 1) return true;
grid[x][y] = 1;
return IntStream.range(0, 4)
.mapToObj(i -> new int[]{x + directions[i], y + directions[i + 1]})
.filter(nxNy -> nxNy[0] >= 0 && nxNy[1] >= 0 && nxNy[0] < grid.length && nxNy[1] < grid[0].length && grid[nxNy[0]][nxNy[1]] == 0)
.anyMatch(nxNy -> dfs(grid, nxNy[0], nxNy[1]));
}
}
解释
方法:
该题解采用深度优先搜索(DFS)来检测从最上面一行到最下面一行是否存在一条完全由陆地构成的路径。该方法逐天模拟每一块陆地被水淹没的过程。对于每一天淹没的陆地,算法检查该陆地的相邻格子是否已经访问过(即已经被淹没并验证过)。如果相邻的格子已被访问,算法会尝试从当前格子开始执行DFS,探索是否能够从顶部到达底部。使用两个集合管理状态:'visited' 存储已经被探索过的陆地格子,'wait' 存储等待探索的陆地格子。
时间复杂度:
O(n^2)
空间复杂度:
O(n)
代码细节讲解
🦆
在实现DFS时,为什么选择移除等待集合中的元素并添加到已访问集合,在算法上这样的处理有什么好处?
▷🦆
为什么在陆地变为水域时,只有当变为水域的格子在第一列或相邻格子已被访问过时才尝试进行DFS?这样的条件判断对算法效率有何影响?
▷🦆
DFS函数中,检查到达最右边列即认为成功从顶部到底部,但题目要求是从最上面一行到最下面一行,是否存在逻辑错误?
▷🦆
在题解中提到每次淹没一块陆地后,DFS可能会被触发,在最坏情况下,每次淹没可能触发对剩余所有陆地的完整探索,那么这种方法在处理大规模数据时的性能如何?
▷