On an N x Ngrid
, each squaregrid[i][j]
represents the elevation at that point(i,j)
.
Now rain starts to fall. At timet
, the depth of the water everywhere ist
. You can swim from a square to another 4-directionally adjacent square if and only if the elevation of both squares individually are at most t
. You can swim infinite distance in zero time. Of course, you must stay within the boundaries of the grid during your swim.
You start at the top left square(0, 0)
. What is the least time until you can reach the bottom right square(N-1, N-1)
?
Example 1:
Input:
[[0,2],[1,3]]
Output:
3
Explanation:
At time
0
, you are in grid location
(0, 0)
.
You cannot go anywhere else because 4-directionally adjacent neighbors have a higher elevation than t = 0.
You cannot reach point
(1, 1)
until time
3
.
When the depth of water is
3
, we can swim anywhere inside the grid.
Example 2:
Input:
[[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]]
Output:
16
Explanation:
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
The final route is marked in bold.
We need to wait until time 16 so that (0, 0) and (4, 4) are connected.
Note:
2
<
= N
<
= 50
.tag: priority queue
class Solution {
public int swimInWater(int[][] grid) {
int N = grid.length;
Set<Integer> seen = new HashSet();
PriorityQueue<Integer> pq = new PriorityQueue<Integer>((k1, k2) ->
grid[k1 / N][k1 % N] - grid[k2 / N][k2 % N]);
pq.offer(0);
int ans = 0;
int[] dr = new int[]{1, -1, 0, 0};
int[] dc = new int[]{0, 0, 1, -1};
while (!pq.isEmpty()) {
int k = pq.poll();
int r = k / N, c = k % N;
ans = Math.max(ans, grid[r][c]);
if (r == N-1 && c == N-1) return ans;
for (int i = 0; i < 4; ++i) {
int cr = r + dr[i], cc = c + dc[i];
int ck = cr * N + cc;
if (0 <= cr && cr < N && 0 <= cc && cc < N && !seen.contains(ck)) {
pq.offer(ck);
seen.add(ck);
}
}
}
throw null;
}
}
union-find
class Solution {
class UnionFind {
int[] parent;
int[] rank;
UnionFind(int[][] grid) {
int N = grid.length;
parent = new int[N * N];
rank = new int[N * N];
for (int i = 0; i < N * N; i++) {
parent[i] = i;
}
}
public int find(int i) {
if (parent[i] != i) {
// path compression
parent[i] = find(parent[i]);
}
return parent[i];
}
public void union(int i, int j) {
int rooti = find(i);
int rootj = find(j);
if (rooti == rootj) return;
// union by rank
if (rank[rooti] < rank[rootj]) {
parent[rooti] = rootj;
} else if (rank[rootj] < rank[rooti]) {
parent[rootj] = rooti;
} else {
parent[rooti] = rootj;
rank[rootj]++;
}
}
public boolean isConnected() {
return find(0) == find(parent.length - 1);
}
}
private int[][] dirs = {{-1,0}, {1,0}, {0,-1}, {0,1}};
public int swimInWater(int[][] grid) {
UnionFind uf = new UnionFind(grid);
int N = grid.length;
int[][] indexes = new int[N * N][2];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
indexes[grid[i][j]][0] = i;
indexes[grid[i][j]][1] = j;
}
}
for (int i = 0; i < N * N; i++) {
int[] p = indexes[i];
for (int[] d : dirs) {
int x = p[0] + d[0], y = p[1] + d[1];
if (x < 0 || x >= N || y < 0 || y >=N || grid[x][y] > i) continue;
// union with smaller neighbors
uf.union(p[0] * N + p[1], x * N + y);
}
if (uf.isConnected()) return i;
}
return N * N; // dummy. will not be executed
}
}