接雨水的题目描述如下。
(1) 2D接雨水: 字节员工是不是个个都会接雨水 ;
(2) 3D接雨水: 407. 接雨水 II ;
(3) 3D接雨水: 字节人都会的 3D接雨水 。
问题描述
难度:困难
给你一个 m x n 的矩阵,其中的值均为非负整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。
示例1:
输入:heightMap = [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]];
输出:4
解释:下雨后,雨水将会上图蓝色的方块中。总的接雨水量为1+1+1=4。
我找到的最好的思路是:把这个看成木桶接水,就像是短板理论。最外层一圈一定接不到水,也就是可以看成木桶的边缘,先把木桶的边缘都放进优先队列中,这时取出最小的柱子,向这个柱子的 4 个方向扩散,扩散出的柱子有两种情况,如果比当前柱子大,那么不能接水,如果比当前柱子小,那么接到的水就是当前柱子高度减去扩散柱子的高度(为什么?因为当前柱子一定是边缘最小的了,所以它一定能接到水),后面在把扩散的柱子加入到优先队列中,已经比较完的柱子就移出队列,这样就又形成了新的桶的边缘,并且水桶面积也在不断缩小(当然要记录走过的柱子,防止重复走),最终就会计算完成。
下面是用C语言实现的3D接雨水:
#include <stdio.h>
#include <stdlib.h>
#define Cols (6)
#define Rows (3)
#define Status_ToDo (0) // 待处理的。
#define Status_Done (1) // 已处理的。
#define Status_Border (2) // 作为木桶板。
int addRainWaterOfTBLR(int height[][Cols], int status[][Cols], int minBorderHeight, int row, int col);
void initStatus(int arr[][Cols]) {
// // 注: 使用下面语句得到的rowCount为0, 因为arr作为形参的时候就会被当作一个指针。
// int rowCount = sizeof(arr) / sizeof(arr[0]);
for (int row = 0; row < Rows; row++) {
for (int col = 0; col < Cols; col++) {
if (0 == row || Rows-1 == row || 0 == col || Cols-1 == col) {
arr[row][col] = Status_Border;
} else {
arr[row][col] = Status_ToDo;
}
}
}
// 去掉四个角。
arr[0][0] = arr[0][Cols-1] = arr[Rows-1][0] = arr[Rows-1][Cols-1] = Status_Done;
}
void showStatus(int height[][Cols], int status[][Cols]) {
printf("打印状态:\n");
for (int row = 0; row < Rows; row++) {
for (int col = 0; col < Cols; col++) {
printf("%d(%d), ", height[row][col], status[row][col]);
}
printf("\n");
}
}
void findPositionOfMinBorder(int height[][Cols], int status[][Cols], int result[2]) {
// printf("findPositionOfMinBorder\n");
result[0] = 0;
result[1] = 0;
for (int row = 0; row < Rows; row++) {
for (int col = 0; col < Cols; col++) {
if (Status_Border == status[row][col]
&& ((0 == result[0] && 0 == result[1]) || height[row][col] < height[result[0]][result[1]])
) {
result[0] = row;
result[1] = col;
}
}
}
printf("最小板的位置是(%d,%d)\n", result[0], result[1]);
}
void repairBorder(int status[][Cols]) {
for (int row = 0; row < Rows; row++) {
for (int col = 0; col < Cols; col++) {
if (Status_Border == status[row][col]) {
//上下左右都不是待处理的。
if ((0 == row || (row > 0 && status[row-1][col] != Status_ToDo))
&& (Rows-1 == row || (Rows-1 > row && status[row+1][col] != Status_ToDo))
&& (0 == col || (col > 0 && status[row][col-1] != Status_ToDo))
&& (Cols-1 == col || (Cols-1 > col && status[row][col+1] != Status_ToDo))
) {
status[row][col] = Status_Done;
}
}
}
}
}
int addRainWaterAtPosition(int height[][Cols], int status[][Cols], int minBorderHeight, int row, int col) {
int result = 0;
// printf("addRainWaterAtPosition(%d,%d)\n", row, col);
if (height[row][col] <= minBorderHeight) {
result += (minBorderHeight - height[row][col]);
result += addRainWaterOfTBLR(height, status, minBorderHeight, row, col);
} else {
status[row][col] = Status_Border;
}
return result;
}
int addRainWaterOfTBLR(int height[][Cols], int status[][Cols], int minBorderHeight, int row, int col) {
int result = 0;
// printf("addRainWaterOfTBLR(%d,%d)\n", row, col);
status[row][col] = Status_Done;
if (row > 0 && Status_ToDo == status[row-1][col]) { // 上。
result += addRainWaterAtPosition(height, status, minBorderHeight, row-1, col);
}
if (row < Rows-1 && Status_ToDo == status[row+1][col]) { // 下。
result += addRainWaterAtPosition(height, status, minBorderHeight, row+1, col);
}
if (col > 0 && Status_ToDo == status[row][col-1]) { // 左。
result += addRainWaterAtPosition(height, status, minBorderHeight, row, col-1);
}
if (col < Cols-1 && Status_ToDo == status[row][col+1]) { // 右。
result += addRainWaterAtPosition(height, status, minBorderHeight, row, col+1);
}
return result;
}
// 判断完成。
int checkFinish(int status[][Cols]) {
for (int row = 0; row < Rows; row++) {
for (int col = 0; col < Cols; col++) {
if (Status_ToDo == status[row][col]) {
return 0;
}
}
}
return 1;
}
// 简介: 3D接雨水。
int main(void)
{
int rainWater = 0;
int heightMap[][Cols] = {{1,4,3,1,3,2},{3,2,1,3,2,4},{2,3,3,2,3,1}};
// printf("heightMap_rowCount = %d。\n", sizeof(heightMap) / sizeof(heightMap[0]));
int (*status)[Cols] = malloc(sizeof(int) * (sizeof(heightMap) / sizeof(heightMap[0])) * Cols);
initStatus(status);
showStatus(heightMap, status);
while(!checkFinish(status)) {
int positionOfMinBorder[2] = {0, 0};
findPositionOfMinBorder(heightMap, status, positionOfMinBorder);
int minBorderHeight = heightMap[positionOfMinBorder[0]][positionOfMinBorder[1]];
// printf("minBorderHeight=%d\n", minBorderHeight);
rainWater += addRainWaterOfTBLR(heightMap, status, minBorderHeight, positionOfMinBorder[0], positionOfMinBorder[1]);
repairBorder(status);
showStatus(heightMap, status);
}
printf("总的接雨水量为%d\n", rainWater);
free(status);
printf("程序正常退出。\n");
return 0;
}