题目
在一个 N x N 的坐标方格 grid 中,每一个方格的值 grid[i][j] 表示在位置 (i,j) 的平台高度。
现在开始下雨了。当时间为 t 时,此时雨水导致水池中任意位置的水位为 t 。你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。当然,在你游泳的时候你必须待在坐标方格里面。
你从坐标方格的左上平台 (0,0) 出发。最少耗时多久你才能到达坐标方格的右下平台 (N-1, N-1)?
示例1
输入: [[0,2],[1,3]]
输出: 3
解释:
时间为0时,你位于坐标方格的位置为 (0, 0)。
此时你不能游向任意方向,因为四个相邻方向平台的高度都大于当前时间为 0 时的水位。
等时间到达 3 时,你才可以游向平台 (1, 1). 因为此时的水位是 3,坐标方格中的平台没有比水位 3 更高的,所以你可以游向坐标方格中的任意位置
示例2
输入: [[0,1,2,3,4],[24,23,22,21,5],[12,13,14,15,16],[11,17,18,19,20],[10,9,8,7,6]]
输出: 16
解释:
0 1 2 3 4
24 23 22 21 5
12 13 14 15 16
11 17 18 19 20
10 9 8 7 6
最终的路线用加粗进行了标记。
我们必须等到时间为 16,此时才能保证平台 (0, 0) 和 (4, 4) 是连通的
提示:
2 <= N <= 50.
grid[i][j] 是 [0, ..., N*N - 1] 的排列。
题解
此题和1631.最小体力消耗路径类似
1. BFS
- 从起点开始进行遍历,每到一个点,再记录所有周围能走的点
- 在所有能走的点中我们只走水位最低的点
- 在到达终点之前重复2
参考代码
class Solution {
public int swimInWater(int[][] grid) {
int row = grid.length, col = grid[0].length;
int[] dx = {1, -1, 0, 0};
int[] dy = {0, 0, 1, -1};
PriorityQueue<int[]> priQueue = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] o1, int[] o2) {
return o1[2] - o2[2];
}
});
priQueue.offer(new int[]{0, 0, grid[i][j]});
int[][] res = new int[row][col];
for(int[] cur : res) {
Arrays.fill(cur, Integer.MIN_VALUE);
}
res[0][0] = grid[0][0];
boolean[][] vissited = new boolean[row][col];
while(!priQueue.isEmpty()) {
int[] cur = priQueue.poll();
int x = cur[0];
int y = cur[1];
int t = cur[2];
if(visited[x][y]) continue;
visited[x][y] = true;
for(int i = 0; i < 4; i++) {
int newX = x + dx[i];
int newY = y + dy[i];
if(newX >= 0 && newX < row && newY >= 0 && newY < col && !visited[newX][newY]) {
int newT = Math.max(res[x][y], grid[newX][newY]);
res[newX][newY] = Math.max(res[newX][newY], newT);
priQueue.offer(new int[]{newX, newY, res[newX][newY]});
}
}
}
return res[row-1][col-1];
}
}
2. 并查集
- 将网格的每个格子看作一个节点
- 相邻节点之间的权值为对应两个相邻节点对应的值中的较大者
- 对所有的
[点,点,权值]
按从小到大进行排序 - 从小到大合并两个节点,直到起点与终点处于相同的连通分量
参考代码
class Solution {
public int swimInWater(int[][] grid) {
int row = grid.length, col = grid[0].length;
List<int[]> edges = new ArrayList<>();
for(int i = 0; i < row; i++) {
for(int j = 0; j < col; j++) {
int id = i * col + j;
if(i > 0) {
edges.offer(new int[]{id - col, id, Math.max(grid[i][j], grid[i-1][j])});
}
if(j > 0) {
edges.offer(new int[]{id - 1, id, Math.max(grid[i][j], grid[i][j-1])});
}
}
}
Collections.sort(edges, new Comparator<int[]>() {
public int compare(int[] o1, int[] o2) {
return o1[2] - o2[2];
}
});
UnionFind myUnionFind = new UnionFind(row * col);
int res = 0;
for(int[] edge : edges) {
int i = edge[0];
int j = edge[1];
int t = edge[2];
myUnionFind.union(i, j);
if(myUnionFind.isConnected(0, row * col - 1)) {
res = t;
break;
}
}
return res;
}
class UnionFind {
int[] parent;
int[] rank;
int count;
public UnionFind(int n) {
parent = new int[n];
rank = new int[n];
this.count = n;
Arrays.fill(rank, 1);
for(int i = 0; i < n; i++) {
parent[i] = i;
}
}
public boolean isConnected(int a, int b) {
int rootA = find(a);
int rootB = find(b);
return rootA == rootB;
}
public void union(int a, int b) {
int rootA = find(a);
int rootB = find(b);
if(rootA == rootB) return;
if(rank[rootA] < rank[rootB]) {
parent[rootA] = rootB;
} else {
if(rank[rootA] == rank[rootB]) {
rank[rootA]++;
}
parent[rootB] = rootA;
}
count--;
}
public int find(int index) {
if(index != parent[index]) {
return find(parent[index]);
}
return index;
}
}
}