class Solution {
class Element implements Comparable<Element> {
int x;
int y;
int pujiHeight;
public Element(int x, int y, int pujiHeight) {
this.x = x;
this.y = y;
this.pujiHeight = pujiHeight;
}
@Override
public int compareTo(Element other) {
return Integer.compare(this.pujiHeight, other.pujiHeight);
}
}
public int trapRainWater(int[][] heightMap) {
// sanity check
if (heightMap == null || heightMap.length == 0 || heightMap[0].length == 0) {
return -1;
}
// initiation (result, graph, visited, minHeap, etc)
int[] result = new int[]{0};
boolean[][] visited = new boolean[heightMap.length][heightMap[0].length];
PriorityQueue<Element> minHeap = new PriorityQueue<>();
// bfs
bfs(minHeap, heightMap, visited, result);
// return result
return result[0];
}
private static final int[][] dirs = new int[][]{{-1, 0}, {1,0}, {0, 1}, {0, -1}};
private void bfs(PriorityQueue<Element> minHeap, int[][] heightMap, boolean[][] visited, int[] result) {
// initial state
initialState(minHeap, heightMap, visited);
while (!minHeap.isEmpty()) {
Element cur = minHeap.poll();
for (int[] dir : dirs) {
int neiX = cur.x + dir[0];
int neiY = cur.y + dir[1];
if (isValid(heightMap, neiX, neiY) && !visited[neiX][neiY]) {
int cost = Math.max(cur.pujiHeight - heightMap[neiX][neiY], 0);
result[0] += cost;
int neiPujiHeight = cur.pujiHeight + Math.max(heightMap[neiX][neiY] - cur.pujiHeight, 0);
Element neiElement = new Element(neiX, neiY, neiPujiHeight);
minHeap.offer(neiElement);
visited[neiX][neiY] = true;
}
}
}
}
private void initialState(PriorityQueue<Element> minHeap, int[][] heightMap, boolean[][] visited) {
// add the top and bottom row
for (int i = 0; i < heightMap[0].length; i++) {
minHeap.offer(new Element(0, i, heightMap[0][i]));
minHeap.offer(new Element(heightMap.length - 1, i, heightMap[heightMap.length - 1][i]));
visited[0][i] = true;
visited[heightMap.length - 1][i] = true;
}
// add the left and right column
for (int j = 0; j < heightMap.length; j++) {
minHeap.offer(new Element(j, 0, heightMap[j][0]));
minHeap.offer(new Element(j, heightMap[0].length - 1, heightMap[j][heightMap[0].length - 1]));
visited[j][0] = true;
visited[j][heightMap[0].length - 1] = true;
}
}
private boolean isValid(int[][] heightMap, int x, int y) {
return x >= 0 && x < heightMap.length && y >= 0 && y < heightMap[0].length;
}
}