UVALive 4627 -- Islands (并查集)

本文介绍了一种基于优先队列和并查集的洪水模拟算法,该算法可以高效地计算出随着海水逐年升高,岛屿上高出水面的连通区域数量。通过排序和比较岛屿高度与海水高度,实现了动态更新连通区域的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:一个岛屿是一个n*m的格子,每个单位格的高度不一样,岛周围的水每年的高度不一样,给出 t 年每年水的高度,水的高度是递增的,求出在每一年中高出当年水的高度有几块连通的岛屿。

思路分析:

①可以把每个格子的高度都存起来,从到小排序,在这里我使用优先队列,其实还是直接排序好些,从最后一年的海的高度进行筛选,然后用岛的高度来筛选海的高度:

①如果岛屿的高度高于海的高度,那么对它四周的岛的高度进行判断,如果也找到高于海的高度的岛,同时它们不在一个集合中(说明它们没有连接过)把它们连接在一起,同时联通的个数减一;(注意减一这里,我再解释一下,其实就是每个高于海的岛屿都要加一次,相当于每个岛屿都是独立的情况,“联通块”也是最大的,那么我们在通过并查集联通时,相当于把两个链接在一起,“联通块”数自然要减一,而通过对当前高于海高度岛的周围进行判断合并减一时,过程中可能会出现负的联通块的值,没关系,等队列进行这一个点判断又会加回来。。。)

② 如果岛屿的高度小于海的高度,那么当前这一年的连通的块数也就求出来了,而年数就应该减一,知道找到海的高度低于岛的高度的那一年。

③用这种方法时,要注意岛屿的高度都高于第二年往上的高度,也就是在第三年第四年。。。的时候队列里的点都高于这一年海的高度,进而队列就为空,而实际上第一年等都没有进行判断,其实连通块就是一啦。。。。用海的高度来筛选岛的高度就不会出现这种情况。

代码实现:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1000005;
int n,m,t,sum,mp[1005][1005],fa[maxn],high[100005],num[100005],dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
struct Node{
    int x,y,h;
    Node(int _x=0,int _y=0,int _h=0):x(_x),y(_y),h(_h){}
    bool operator<(const Node& rhs)const {
        return h<rhs.h;
    }
};
priority_queue<Node> q;
int Find(int a){
    return a==fa[a]?fa[a]:fa[a]=Find(fa[a]);
}
void Merge(int x,int y){
    int fx=Find(x),fy=Find(y);
    if(fx!=fy){
        sum--;
        fa[fy]=fx;
    }
}
void Solve(){
    int j=t,i;
    Node head,now;
    while(!q.empty()){
        head=q.top();
        q.pop();
        while(j>0&&head.h<=high[j]) num[j]=sum,j--;
        if(j==0) break;
        sum++;
        for(i=0;i<4;i++){
            now.x=head.x+dx[i];
            now.y=head.y+dy[i];
            if(now.x<1||now.x>n||now.y<1||now.y>m||mp[now.x][now.y]<=high[j]) continue;
            Merge((head.x-1)*m+head.y,(now.x-1)*m+now.y);
        }
    }
    if(j!=0) for(int i=j;i>0;i--) num[i]=sum;
    for(i=1;i<=t;i++) printf("%d ",num[i]);
    printf("\n");
}
int main(){
    int c;
    scanf("%d",&c);
    while(c--){
        scanf("%d%d",&n,&m);
        while(!q.empty()) q.pop();
        for(int i=1;i<=m*n;i++) fa[i]=i;
        memset(num,0,sizeof(num));
        sum=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&mp[i][j]);
                q.push(Node(i,j,mp[i][j]));
            }
        }
        scanf("%d",&t);
        for(int i=1;i<=t;i++) scanf("%d",&high[i]);
        Solve();
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值