Test 6.24 T3 水题
问题描述
秋之国首都下了一场暴雨,由于秋之国地势高低起伏,不少地方出现了积水。
秋之国的首都可以看成一个 n 行 m 列的矩阵,第 i 行第 j 列的位置高度为 ai,j,首都以外的地方的高度可以都看成 0。我们假设下的雨无限多,暴雨时,水位线超过了所有位置的高度。暴雨结束后,如果有雨水的高度超过的四相邻(上下左右)的位置的高度,水就会流过去,如果流到了首都外,水会消失。你需要求出最后每个位置的积水高度。
输入格式
第一行两个非负整数 n,m。
接下来 n 行,每行 m 个整数,表示 ai,j。
输出格式
输出 n 行,每行 m 个整数,表示每个位置的积水高度。
样例输入
3 3
4 4 0
2 1 3
3 3 -1
样例输出
0 0 0
0 1 0
0 0 1
解析
假如一个方格内的水能够留到外面去,这个方格中就不会有积水。水往低处流,将每个方格视为一个点,往四周比自己低的点连边,边权为两个高度的最大值。将城外单独作为一点,往所有在边界的点连边。由木桶原理,一个水池的高度一定由边缘高度最小的点决定。那么,一个点的高度会由到边界外的路径中经过的边权最大值最小的路径决定,记这个最大值为b。这样的路径一定是图的最小生成树上到城外点的路径。由此,我们得到如下算法:
连边构图完成后,将图的最小生成树求出来,在树上求城外点到每个点的路径上的最大值b,最后的答案即为\(b-a[i][j]\)。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 302
using namespace std;
struct node{
int u,v,w;
}e[N*N*4];
int p[4]={1,-1,0,0},q[4]={0,0,1,-1};
int head[N*N],ver[N*N*4],nxt[N*N*4],edge[N*N*4],l,tot;
int n,m,i,j,k,a[N][N],id[N][N],cnt,fa[N*N],dis[N*N];
bool in(int x,int y)
{
return x<=n&&x>=1&&y<=m&&y>=1;
}
void insert(int x,int y,int z)
{
l++;
ver[l]=y;
edge[l]=z;
nxt[l]=head[x];
head[x]=l;
}
int my_comp(const node &x,const node &y)
{
return x.w<y.w;
}
int find(int x)
{
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void Kruscal()
{
sort(e+1,e+tot+1,my_comp);
for(i=1;i<=n*m;i++) fa[i]=i;
int cnt=n*m+1;
for(int i=1;i<=tot;i++){
if(cnt==1) break;
int f1=find(e[i].u),f2=find(e[i].v);
if(f1!=f2){
insert(e[i].u,e[i].v,e[i].w);
insert(e[i].v,e[i].u,e[i].w);
fa[f1]=f2;
cnt--;
}
}
}
void dfs(int x,int pre)
{
for(int i=head[x];i!=-1;i=nxt[i]){
int y=ver[i];
if(y!=pre){
dis[y]=max(dis[x],edge[i]);
dfs(y,x);
}
}
}
int main()
{
freopen("water.in","r",stdin);
freopen("water.out","w",stdout);
cin>>n>>m;
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cin>>a[i][j];
id[i][j]=++cnt;
}
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
if(i==1||i==n||j==1||j==m) e[++tot]=(node){0,id[i][j],max(0,a[i][j])};
for(k=0;k<4;k++){
int x=i+p[k],y=j+q[k];
if(in(x,y)&&a[x][y]<=a[i][j]) e[++tot]=(node){id[x][y],id[i][j],max(a[x][y],a[i][j])};
}
}
}
memset(head,-1,sizeof(head));
Kruscal();
dfs(0,0);
cnt=0;
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cnt++;
cout<<dis[cnt]-a[i][j]<<' ';
}
cout<<endl;
}
fclose(stdin);
fclose(stdout);
return 0;
}