并查集好题

给你一个n*n的矩阵,每个格子的数代表给格子的海拔,求下一之后的积水是多少。(n<=1000)

思路:思路还是很巧妙的,维护并查集,排序以后从小到大枚举高度,然后看四周有没有之前已经访问过的或者已经flow的,就算是已经漏水的,漏水的话,这个格子也会露水,否则吧当前高度作为父亲,排序的作业是英文漏水只可能从低的地方漏水,不可能从高的地方漏水。

代码:

#include<algorithm>
#include<iostream>
#include<stdio.h>
#include<string.h>
//using namespace std;
const int maxn=1010;
int f[maxn*maxn];
int ix[5]= {0,0,1,-1};
int iy[5]= {-1,1,0,0};
int n;
inline int get(int v)
{
    return f[v]==v?v:f[v]=get(f[v]);
}
inline void Merge(int v,int u)
{
    int t1 = get(v);
    int t2 = get(u);
    f[t1] = t2;
    return;
}
struct node
{
    int x,y;
    int high,num;
    bool flow,vi;
} N[maxn*maxn];
int MAP[maxn*maxn];
inline int check(int x,int y)
{
    if(x==0||x==n-1||y==0||y==n-1)
        return 1;
    return 0;
}
using ll=long long ;

char buf[16 * 1024 * 1024 + 5]; // 10 MiB Buffer
int curpos = 0, iseof;
int nextInt()
{
    int ret = 0;
    if (iseof)
        return 0;
    while (!('0' <= buf[curpos] && buf[curpos] <= '9'))
    {
        if (buf[curpos] == 0)
        {
            if (fgets(buf, 16 * 1024 * 1024, stdin) == NULL)
                iseof = true;
            curpos = 0;
            continue;
        }
        curpos++;
    }
    int flg = 0;
    if (curpos && buf[curpos - 1] == '-')
        flg = -1;
    else
        flg = 1;
    while ('0' <= buf[curpos] && buf[curpos] <= '9')
    {
        ret = ret * 10 + (buf[curpos] - '0');
        curpos++;
    }
    return ret * flg;
}
int main()
{
//    std::ios::sync_with_stdio(false);
//    scanf("%d",&n);
    n=nextInt() ;
    int num=1;
    memset(f,0,sizeof(f));
    memset(MAP,0,sizeof(MAP));
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
        {
            int x;
            x=nextInt() ;
//            scanf("%d",&x);
            f[num]=num;
            N[num].x=i;
            N[num].y=j;
            N[num].num=num;
            N[num].high=x;
            N[num].flow=false;
            N[num].vi=false;
            num++;
        }
    std::sort(N+1,N+num,[=](node a,node b)
    {
        return a.high<b.high;
    });
//    for(int i=1; i<num; i++)
//        std::cout<<N[i].num<<" "<<N[i].high<<'\n';
    for(int i=1; i<num; i++)
        MAP[N[i].num]=i;
    for(int i=1; i<num; i++)
    {
        if(check(N[i].x,N[i].y))
            N[i].flow=true;
        N[i].vi=true;
        for(int j=0; j<4; j++)
        {
            int xx=N[i].x+ix[j];
            int yy=N[i].y+iy[j];
            if(xx<0||xx>=n||yy<0||yy>=n)
                continue;
            int ns=n*xx+yy+1;
            int root=get(ns);
            if(N[MAP[root]].flow)
                N[i].flow=1;
            else  if(N[MAP[ns]].vi)
                Merge(N[MAP[ns]].num,N[i].num);
        }
    }
    ll sum=0;
    for(int i=1; i<num; i++)
    {
//        std::cout<<get(N[i].num)<<" "<<N[i].num<<'\n';
        sum+=N[MAP[get(N[i].num)]].high-N[i].high;
    }
    printf("%lld\n",sum);

//    std::cout<<sum<<'\n';
}
/*
4
3 4 5 6
4 1 2 7
5 1 2 8
6 7 8 9
3
2 3 4
4 1 2
2 2 2
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值