题目描述:
LeetCode 407. Trapping Rain Water II
Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the volume of water it is able to trap after raining.
Note:
Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000.
Example:
Given the following 3x6 height map: [ [1,4,3,1,3,2], [3,2,1,3,2,4], [2,3,3,2,3,1] ] Return 4.

The above image represents the elevation map [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] before the rain.

After the rain, water are trapped between the blocks. The total volume of water trapped is 4.
题目大意:
给定一个m x n正整数矩阵,表示一个二维高程图,计算降雨后其中可以蓄积的雨量。
注意:
m和n值均小于110.每一个单元的高度在(0, 20000)之间。
解题思路:
BFS(广度优先搜索)
记矩形的高度、宽度分别为m, n,令二维数组peakMap[i][j] = ∞,表示矩形区域最多可以达到的水面高度
将矩形的四条边中的各点坐标加入队列q,并将各点对应的高度赋值给peakMap相同坐标
每次从q中弹出对头元素x, y,探索其上、下、左、右四个方向nx, ny:
尝试用max(peakMap[x][y], heightMap[nx][ny]) 更新 peakMap[nx][ny] 的当前值(取两者中的较小值)
Python代码:
class Solution(object):
def trapRainWater(self, heightMap):
"""
:type heightMap: List[List[int]]
:rtype: int
"""
m = len(heightMap)
n = len(heightMap[0]) if m else 0
peakMap = [[0x7FFFFFFF] * n for _ in range(m)]
q = []
for x in range(m):
for y in range(n):
if x in (0, m - 1) or y in (0, n - 1):
peakMap[x][y] = heightMap[x][y]
q.append((x, y))
while q:
x, y = q.pop(0)
for dx, dy in zip((1, 0, -1, 0), (0, 1, 0, -1)):
nx, ny = x + dx, y + dy
if nx <= 0 or nx >= m - 1 or ny <= 0 or ny >= n - 1: continue
limit = max(peakMap[x][y], heightMap[nx][ny])
if peakMap[nx][ny] > limit:
peakMap[nx][ny] = limit
q.append((nx, ny))
return sum(peakMap[x][y] - heightMap[x][y] for x in range(m) for y in range(n))
另外一种解法:
蓄积雨水的单元格存在两种情况:
1. 单元格的高度严格小于其上、下、左、右方向的4个单元格高度
2. 单元格的高度小于或等于其上、下、左、右方向的4个单元格高度
对于情况1,可以利用“木桶原理”将其高度调整为四周单元格中的最小高度
对于情况2,可以通过DFS,寻找与其邻接的等高节点的四周高度的最小值
Java代码:
public class Solution {
private int m, n;
private int[][] heightMap;
private int dx[] = {1, 0, -1, 0};
private int dy[] = {0, 1, 0, -1};
private class Pair {
public int x, y;
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
public int hashCode() {
return x + y;
}
public boolean equals(Object o) {
if (o instanceof Pair) {
Pair p = (Pair)o;
return x == p.x && y == p.y;
}
return false;
}
}
public int trapRainWater(int[][] heightMap) {
this.heightMap = heightMap;
m = heightMap.length;
n = m == 0 ? 0 : heightMap[0].length;
int sum0 = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
sum0 += heightMap[i][j];
}
}
LinkedList<Pair> queue = new LinkedList<Pair>();
for (int i = 1; i < m - 1; i++) {
for (int j = 1; j < n - 1; j++) {
if (minNeighborHeight(i, j) >= heightMap[i][j]) {
queue.add(new Pair(i, j));
}
}
}
while (!queue.isEmpty()) {
Pair head = queue.removeFirst();
int i = head.x, j = head.y;
HashSet<Pair> vs = new HashSet<Pair>();
vs.add(head);
int minh = solve(i, j, vs);
if (minh > heightMap[i][j]) {
queue.add(head);
for (Pair e : vs) {
heightMap[e.x][e.y] = minh;
}
}
}
int sum1 = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
sum1 += heightMap[i][j];
}
}
return sum1 - sum0;
}
private int minNeighborHeight(int i, int j) {
int minh = Integer.MAX_VALUE;
for (int k = 0; k < dx.length; k++) {
int di = i + dx[k];
int dj = j + dy[k];
minh = Math.min(minh, heightMap[di][dj]);
}
return minh;
}
private int solve(int i, int j, HashSet<Pair> vs) {
int height = heightMap[i][j];
if (i == 0 || j == 0 || i == m - 1 || j == n - 1) {
return height;
}
int minh = minNeighborHeight(i, j);
if (minh != height) {
return minh;
}
minh = Integer.MAX_VALUE;
for (int k = 0; k < dx.length; k++) {
int di = i + dx[k];
int dj = j + dy[k];
Pair pair = new Pair(di, dj);
if (vs.contains(pair)) {
continue;
}
if (heightMap[di][dj] == height) {
vs.add(pair);
minh = Math.min(minh, solve(di, dj, vs));
} else {
minh = Math.min(minh, heightMap[di][dj]);
}
}
return minh;
}
}