树状数组--water

对于这道题,首先要明确,块的积水量取决于什么

1、如果水一直只往一个方向流,块的积水量,等于从它出发,沿该方向的最大高度,因为之前不管有过低,到了最大高度都会被卡住;

2、如果水往两个方向一直流,块的积水量,由1知,每条路径的最大积水量分别为max1,max2,如果两条路径一起考虑呢?应该为min(max1,max2)(取决于木桶原理);

3、由1,2推出,块的积水量等于从块出发,走到边界的x条路径的高度最大值得最小值;

知道了块的积水量是什么,我们再考虑如何求解

我们希望求出一条通往边界路径的最大值,且这个最大值是所有路径中最小的,我们贪心的想,如果加的每一条边都尽可能的小,当形成一条通往边界的路径时,当前路径的最大值一定是最小的,这个思想就很像一个算法,没错就是最小生成树;

*整体思路:

相邻矩形连边,边权为高度较大值(因为求一条路径的最大值,较小值没有贡献),最小生成树,使所有的节点最后都直接或间接的与边界相连(不一定是同一个边界),最后每次从边界开始dfs;(这可能是我写的最长最详细的一篇题解了吧,我不会画图,画图看的话应该会更直观)

代码:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 using namespace std;
 6 const int maxn=1e5;
 7 int head[2*maxn],tot,n,m,h[maxn*2],fa[maxn],ans[2*maxn],cnt;
 8 inline int getnum(int x,int y){
 9     if (x>n||y>m||!x||!y) return 0;
10     return (x-1)*m+y;
11 }
12 struct node{
13     int u,v,w;
14 }water[2*maxn];
15 struct e{
16     int to,next,w;
17 }ed[2*maxn];
18 bool cmp(node x,node y){
19     return x.w<y.w;
20 }
21 inline int find(int x){
22     if(fa[x]==x) return x;
23     else return find(fa[x]);
24 }
25 inline void update(int x,int y){
26     int fx=find(x),fy=find(y);
27     fa[fx]=fy;return;
28 }
29 inline void add(int u,int v,int w){
30     ed[++cnt].to=v;
31     ed[cnt].w=w;
32     ed[cnt].next=head[u];
33     head[u]=cnt; 
34 }
35 inline void mintree(){
36     for (int i = 0;i <= n*m;i++) fa[i]=i;
37     sort(water+1,water+1+tot,cmp);
38     for (int i = 1;i <= tot;i++){
39         int u=water[i].u,v=water[i].v,w=water[i].w;
40         if (find(u)!=find(v)) update(u,v),add(u,v,w),add(v,u,w);
41     }
42 }
43 inline void dfs(int nowid,int fa){
44     for (int i = head[nowid];i;i=ed[i].next){
45         int to=ed[i].to,w=ed[i].w;
46         if(to!=fa) ans[to]=max(ans[nowid],w),dfs(to,nowid);
47     }
48 }
49 int dirx[5]={0,-1,0,1,0};
50 int diry[5]={0,0,1,0,-1};
51 int main(){
52     scanf ("%d%d",&n,&m);
53     for (int i = 1;i <= n;i++)
54         for (int j = 1;j <= m;j++)
55             scanf ("%d",&h[getnum(i,j)]);
56     for (int i = 1;i <= n;i++){
57         for (int j = 1;j <= m;j++){
58             int u=getnum(i,j);
59             for (int k = 1;k <= 4;k++){
60                 int v=getnum(i+dirx[k],j+diry[k]);
61                 water[++tot]={u,v,max(h[u],h[v])};
62             }
63         }
64     }
65     mintree();
66     dfs(0,0);
67     for (int i = 1;i <= n*m;i++){
68         printf ("%d ",ans[i]-h[i]);
69         if (i%m==0) cout<<endl;
70     }
71     return 0;
72 }
原文地址:https://www.cnblogs.com/very-beginning/p/13598289.html