1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Solution {
private static class Cell {
int row;
int col;
int height;

Cell(int _row, int _col, int _height) {
row = _row;
col = _col;
height = _height;
}
}

public int trapRainWater(int[][] heightMap) {
PriorityQueue<Cell> pq = new PriorityQueue<>((a, b) -> a.height - b.height);
boolean[][] visited = new boolean[heightMap.length][heightMap[0].length];
for (int i = 0; i < heightMap[0].length; i++) {
pq.offer(new Cell(0, i, heightMap[0][i]));
visited[0][i] = true;
pq.offer(new Cell(heightMap.length - 1, i, heightMap[heightMap.length - 1][i]));
visited[heightMap.length - 1][i] = true;
}
for (int i = 1; i < heightMap.length - 1; i++) {
pq.offer(new Cell(i, 0, heightMap[i][0]));
visited[i][0] = true;
pq.offer(new Cell(i, heightMap[0].length - 1, heightMap[i][heightMap[0].length - 1]));
visited[i][heightMap[0].length - 1] = true;
}
int ans = 0;
int[][] directions = new int[][] { { -1, 0 }, { 1, 0 }, { 0, 1 }, { 0, -1 } };
while (!pq.isEmpty()) {
Cell currCell = pq.poll();
for (int[] direction : directions) {
int newRow = currCell.row + direction[0];
int newCol = currCell.col + direction[1];
if (!isOutOfBound(heightMap, newRow, newCol) && !visited[newRow][newCol]) {
visited[newRow][newCol] = true;
ans += Math.max(0, currCell.height - heightMap[newRow][newCol]);
pq.offer(new Cell(newRow, newCol, Math.max(currCell.height, heightMap[newRow][newCol])));
}
}
}
return ans;
}

private boolean isOutOfBound(int[][] grid, int row, int col) {
return row < 0 || row >= grid.length || col < 0 || col >= grid[0].length;
}
}

这道题和pacific & atlantic那道题有些类似, 但是是那道题的升级版. 我们也是从border入手, 在border中我们挑最短的那个cell, 看它四周是否有比它小的没被visit过的cell, 首先border的cell肯定被visit过. 如果出现四周有个cell它的值比该border中最小的cell还要低, 那它一定可以存入水, 而且水面最大高度就是border中最小的cell的高度. 因为border上其他的cell都比它高, 因此能把水装起来. 但是水面可能比border中最小的height还高吗? 不可能, 因为会直接从border上最小height处流走. 然后我们把这个找到的cell放入queue中, 此时我们需要把它的height更新为它装完水后的高度, 因为下完雨后它的高度就是这么高, 因此可以成为其他cell的依靠. 我们可以认为此时我们把水首先给它放好到这个位置, 水被我们控制着不乱走.

如果遇到四周的cell的高度都很高, 那么我们也要把它放入queue中, 因为如果不放, 想象一下border中一圈还有个内层border, 它的高度比最外层border都要高. 此时如果我们只先把最外层border的cell都放入pq, 不把内部高的border的cell装入pq(只是因为它们更高), 此时内部即使内部border内可以存水, 我们也没考虑到. 因此是要放的.

时间复杂度: O(m * n) 每个cell最多进一次和出一次pq.
空间复杂度: O(m * n)