zoukankan      html  css  js  c++  java
  • BZOJ2936 Codevs3634 POI1999 积水 【并查集】*

    BZOJ2936 Codevs3634 POI1999 积水


    题目描述

    有这样一块土地,它可以被划分成N×M个正方形小块,每块面积是一平方英寸,第i行第j列的小块可以表示成P(i,j)。这块土地高低不平,每一小块地P(i,j)都有自己的高度H(i,j)(单位是英寸)。
    一场倾盆大雨后,这块地由于地势高低不同,许多低洼地方都积存了不少降水。假如你已经知道这块土地的详细信息,你能求出它最多能积存多少立方英寸的降水么?

    输入格式

    输入文件第一行有两个数,N,M(1<=N, M <=100),表示土地的规模是N×M平方英寸。
    以下有N行,每行有M个整数,表示每块地的高低(每个整数在[1,10000]内,以英寸为单位)。

    输出格式

    输出文件只有一行,一个数,表示土地中最多能积存多少立方英寸的水。

    样例输入

    3 6
    3 3 4 4 4 2
    3 1 3 2 1 4
    7 3 1 6 4 1

    样例输出

    5


    先%一下yyf大神的题解

    我们考虑一层一层地向图中加水
    这样的话当我们考虑到v这个高度,所有小于v的高度我们都已经考虑过了
    所以我们只考虑当前有多少个位置可以加入一层水
    对于边界上的节点,我们需要排除掉,所以我们用0表示已经删除掉的集合,所以把需要删除的节点和0连接起来,否则的话我们需要维护高度相同的联通块的连通性,就把当前块和周围已经访问过的块连接起来就行了

    所以用并查集维护siz


    #include <bits/stdc++.h>
    using namespace std;
    #define N 110
    #define V 10010
    int mx[4]={0,0,1,-1};
    int my[4]={1,-1,0,0};
    namespace Union_Find{
        int fa[V],siz[V];
        void init(int n){
            fa[0]=siz[0]=0;
            for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
        }
        int find(int x){
            if(fa[x]==x)return x;
            return fa[x]=find(fa[x]);
        }
        void merge(int x,int y){
            int fx=find(x),fy=find(y);
            if(fx==fy)return;
            siz[fy]+=siz[fx];
            fa[fx]=fy;
        }
    };
    int n,m;
    bool vis[N][N];
    vector<pair<int,int> > g[V];
    bool checkin(int x,int y){
        if(x<1||y<1||x>n||y>m)return 0;
        return 1;
    }
    int id(int x,int y){
        if(!checkin(x,y))return 0;
        return (x-1)*m+y;
    }
    int cnt=0,ans=0;
    int main(){
        using namespace Union_Find;
        freopen("2936.in","r",stdin);
        scanf("%d%d",&n,&m);
        init(n*m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                int vl;scanf("%d",&vl);
                g[vl].push_back(pair<int,int>(i,j));
            }
        int up=n*m;
        for(int v=1;v<=V;v++){//考虑一层一层地加水
            if(siz[find(0)]==up)break;
            for(int j=0;j<g[v].size();j++){
                int x=g[v][j].first,y=g[v][j].second;
                vis[x][y]=1;
                cnt++;
                for(int k=0;k<4;k++){
                    int nx=x+mx[k],ny=y+my[k];
                    //如果当前点在边界上或者旁边有比它低的点就合并起来
                    //在边界上siz[0]++ 说明这个点不可取
                    //不在边界上维护可以到达的联通块
                    if(!checkin(nx,ny)||vis[nx][ny])merge(id(nx,ny),id(x,y));
                }
            }
            ans+=cnt-siz[find(0)];
        }
        printf("%d",ans);
        return 0;
    }
    
  • 相关阅读:
    浙江大华2012笔试题+答案解析
    腾讯2011年10月15日校招笔试+答案解析
    百度2011招聘笔试题+答案解析
    阿里巴巴2011笔试题+答案解析
    微软2012实习生笔试题+答案解析
    腾讯2012实习生笔试题+答案解析
    迅雷2012春招+答案解析
    华为2012春上机笔试题+参考程序
    中兴2011笔试题+答案解析
    mysql修改数据库名
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9676300.html
Copyright © 2011-2022 走看看