【题目描述】 有一块矩形土地被划分成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,m<=4。 对于40%的数据,n,m<=15。 对于60%的数据,n,m<=50。 对于100%的数据,n,m<=300,小块高度的绝对值<=10^9。 在每一部分数据中,均有一半数据保证小块高度非负
把最外圈土地(紧挨外围)全部push进一个小根堆,
每次取出一个并插入所有相邻合法土地。
#include <bits/stdc++.h> using namespace std; struct field{ int ht;//height of the field int wtr;//height of the water bool vis;//visited or not }; field f[310][310]; struct pos{ int x; int y; int ht;//the height used to calculate(as the height of water or land) }; priority_queue<pos> q; inline bool operator<(const pos &a, const pos &b){ return a.ht>b.ht; } int mx[4]={0, 0, -1, 1}, my[4]={-1, 1, 0, 0}; int n, m; void in_put(){ scanf("%d%d", &n, &m); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++){ scanf("%d", &f[i][j].ht); if(i==1 || i==n || j==1 || j==m){ f[i][j].vis = true; if(f[i][j].ht >= 0){ q.push({i, j, f[i][j].ht}); f[i][j].wtr = 0; } else{ q.push({i, j, 0}); f[i][j].wtr = 0 - f[i][j].ht; } } } } void out_put(){ for(int i=1; i<=n; i++){ for(int j=1; j<=m; j++){ printf("%d ", f[i][j].wtr); } printf(" "); } return; } int main(){ freopen("water.in", "r", stdin); freopen("water.out", "w", stdout); in_put(); while(!q.empty()){ pos temp = q.top(); q.pop(); for(int i=0; i<4; i++){ int nx = temp.x + mx[i], ny = temp.y + my[i]; if(nx>=1 && nx<=n && ny>=1 && ny<=m && !f[nx][ny].vis){ if(f[nx][ny].ht >= temp.ht){ f[nx][ny].wtr = 0; q.push({nx, ny, f[nx][ny].ht}); f[nx][ny].vis = true; } else{ f[nx][ny].wtr = temp.ht - f[nx][ny].ht; q.push({nx, ny, temp.ht}); f[nx][ny].vis = true; } } } } out_put(); return 0; }
另一种相似做法(题解):
一个块的高度就是从这个块走出矩形的所有路径上的最大值的最小值。
新建一个点代表矩形外部。相邻块之间连边,权值为两块的较大值,矩形边界的块向矩形外部连边,权值为max(高度,0),求出最小生成树,
时间复杂度O(nmlog(nm))。