water——小根堆+BFS

B. water

题目描述

  • 有一块矩形土地被划分成 n×m 个正方形小块。这些小块高低不平,每一小块都有自己的高度。水流可以由任意一块地流向周围四个方向的四块地中,但是不能直接流入对角相连的小块中。
  • 一场大雨后,由于地势高低不同,许多地方都积存了不少降水。给定每个小块的高度,求每个小块的积水高度。
  • 注意:假设矩形地外围无限大且高度为 0。

输入格式

  • 第一行包含两个非负整数 n,m 。
  • 接下来 n 行每行 m 个整数表示第 i 行第 j 列的小块的高度。

输出格式

  • 输出 n 行,每行 m 个由空格隔开的非负整数,表示每个小块的积水高度。

样例输入

3 3
4 4 0
2 1 3
3 3 -1

样例输出

0 0 0
0 1 0
0 0 1

数据范围与提示

  • 对于20%的数据 (n,mle 4)
  • 对于40%的数据 (n,mle 15)
  • 对于60%的数据 (n,mle 50)
  • 对于100%的数据 (n,mle 300),|小块高度|(le 10^9)
  • 在每一部分数据中,均有一半数据保证小块高度非负

Solve

  • 题目大意
    • 二维的积水问题。
  • 木桶原理:桶能装的水的多少取决于最短的木板。
  • 同理,一块土地积存的水取决于最低的那个边界,我们知道矩阵最边上的位置可不可以存水(<0就可以,反之就流出去了,存不了水),就从边上向内搜索,找到更低的地方就可以存水。
  • w是每块方格最高的水位(不能存水的格子水位就等于高度)。
  • 具体实现过程:
    1. 将边界上的点(横坐标等于1或n,纵坐标等于1或m)放入小根堆,如果高度小于0,压入的高度是0。
    2. 每次取出堆顶(即高度最小的点),进行BFS,这里进行BFS是因为DFS在这种可以随意走,一直递归下去(指没有进行过回溯)就可能跑完的图有爆栈的可能,其实这到题还是没什么关系,我的电脑实测可以递归到26万层左右,这道题只有1万个点。
    3. 进行BFS的时候,搜索到低的点就改变其最高水位,有高的点就在判断没有进入过堆后压入堆中
  • 需要注意的是,题目中提到矩形地外围无限大且高度为 0,说明水位最低也是0,所以在压如边界的时候,如果高度为负值,直接压入0。
  • 详见代码注释

Code

#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 305;
struct Node {
    int x, y, h;
    Node() {};
    Node(int a, int b, int c) {
        x = a, y = b, h = c;
    }
    bool operator < (const Node &b) const {
        return h > b.h;
    }//重载运算符,这是小根堆
};
int n, m, h[N][N], w[N][N];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};//Bfs时的4个方向
priority_queue<Node> que;//堆
queue<Node> q;//Bfs用的队列
bool vis[N][N];//标记是否入过堆
void Bfs(Node a) {
    q.push(a);
    while (!q.empty()) {
        Node u = q.front(); q.pop();
        if (w[u.x][u.y] != -1) continue;
        w[u.x][u.y] = a.h;
        for (int k = 0; k < 4; ++k) {
            int tx = u.x + dx[k];
            int ty = u.y + dy[k];
            if (tx < 1 || tx > n || ty < 1 || ty > m) continue;//超出了边界
            if (w[tx][ty] != -1) continue;//已经访问过且赋值
            if (h[tx][ty] <= a.h) q.push(Node(tx, ty, 0));//高度低的入队,继续Bfs
            else if (!vis[tx][ty]) //高度高的压入堆
                que.push(Node(tx, ty, h[tx][ty])), vis[tx][ty] = 1;
        }
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j) {
            scanf("%d", &h[i][j]);
            w[i][j] = -1;//初始化为-1,为未访问标记
            if (i == 1 || i == n || j == 1 || j == m)//将边界入堆并标记
                que.push(Node(i, j, h[i][j] < 0 ? 0 : h[i][j])), vis[i][j] = 1;
        }
    while (!que.empty()) {//每次取出最低的进行操作
        Node u = que.top(); que.pop();
        if (w[u.x][u.y] != -1) continue;//如果已经访问那就不需要了
        Bfs(u);
    }
    for (int i = 1; i <= n; ++i, puts(""))
        for (int j = 1; j <= m; ++j)
            printf("%d ", w[i][j] - h[i][j]);//w-h即水的深度
    return 0;
}
原文地址:https://www.cnblogs.com/shawk/p/13416310.html