• 树状数组--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 }
  • 相关阅读:
    题解-CF617E XOR and Favorite Number
    题解-P3174 [HAOI2009]毛毛虫
    数论相关
    力扣 403 青蛙过河 搜索 || 动态规划
    【转载】剑指Offer 26 树的子结构
    三元组 蓝桥杯
    剑指 Offer 14- I. 剪绳子
    【转载】剑指 Offer 43. 1~n整数中1出现的次数
    n个节点的二叉树有几种情况
    1044. 最长重复子串 二分 + Rabin-Karp | | 后缀数组
  • 原文地址:https://www.cnblogs.com/very-beginning/p/13598289.html
Copyright © 2020-2023  润新知