[校内集训] 氵
题意
给一张网格图,每个格子有高度,假设边界之外的区域无限大且高度为0,向整个图灌氵,问到最后每个格子上氵柱的高度(氵往低处流)
思路
贪心:一个点可以存的氵量等于它到网格图外面的一条路径上最高的格子减去自己的高度
限制:上述路径最大值中应该选取最小的,因为如果选了一条最大值大的路径,氵就会从另一条最大值较小的路径流走
将相邻点之间连边,边权为两个格子高度的较大值,边界格子向根节点连边,边权为(max(h,0)),对于限制,跑出原图的最小生成树(即保证两点之间连通性的最短路径),对于贪心,从根出发到每个点即可得到上面说的最大值,(ans[i][j]=maxx-h[i][j])
Code
#include<bits/stdc++.h>
#define N 305
#define Min(x,y) ((x)<(y) ? (x):(y))
using namespace std;
const int INF = 1000000000;
int n,m,s,c,fa[N*N];
int a[N][N],ans[N][N];
int dox[4]={0,-1,0,1},doy[4]={1,0,-1,0};
struct E {int u,v,w;} e[N*N*4];
struct Edge
{
int next,to,dis;
}edge[N*N*2];int head[N*N],cnt;
void add_edge(int from,int to,int dis)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
edge[cnt].dis=dis;
head[from]=cnt;
}
template <class T>
void read(T &x)
{
char c;int sign=1;
while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
int find(int x) {return x==fa[x] ? fa[x] : fa[x] = find(fa[x]);}
int getid(int x,int y) {return (x-1)*m+y;}
bool cmp(E a,E b) {return a.w<b.w;}
void kruskal()
{
for(int i=1;i<=n*m+1;++i) fa[i]=i;
sort(e+1,e+c+1,cmp);
int p=0;
for(int i=1;i<=c;++i)
{
int fx=find(e[i].u),fy=find(e[i].v);
if(fx!=fy)
{
fa[fx]=fy;
add_edge(e[i].u,e[i].v,e[i].w);
add_edge(e[i].v,e[i].u,e[i].w);
if(++p==n*m) break;
}
}
}
void dfs(int rt,int fa,int maxx)
{
ans[(rt-1)/m+1][(rt-1)%m+1]=maxx-a[(rt-1)/m+1][(rt-1)%m+1];
for(int i=head[rt];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa) continue;
dfs(v,rt,max(maxx,edge[i].dis));
}
}
int main()
{
freopen("water.in","r",stdin);
freopen("water.out","w",stdout);
read(n);read(m);
s=n*m+1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
read(a[i][j]);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
for(int k=0;k<4;++k)
{
int dx=dox[k]+i,dy=doy[k]+j;
if(dx<1||dy<1||dx>n||dy>m) e[++c]=(E){s,getid(i,j),max(a[i][j],0)};
else e[++c]=(E){getid(i,j),getid(dx,dy),max(a[i][j],a[dx][dy])};
}
kruskal();
dfs(s,0,-INF);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j) printf("%d ",ans[i][j]);
printf("
");
}
return 0;
}