BZOJ 1104: [POI2007]洪水pow 并查集

本文介绍了一种解决特定场景下城市排水系统设计的问题——时空隧道。通过算法分析和代码实现,详细阐述了如何确定最少数量的水泵安装位置,确保所有城市的水能够被有效排出。

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

时空隧道


分析:
首先我们可以得出水泵一定是安在城市上的…
以上纯属瞎扯淡…只是答案相等可以…至于为什么我们待会儿再说…
我们取出所有的城市放在s集合中,因为海拔高的可以被海拔低的覆盖,所以我们从小到大排序从所有的元素中取出海拔小于等于当前城市海拔的元素,合并相邻元素,然后查询当前城市所在的联通块是否已经被覆盖,如果没有就在当前城市安上水泵…
回到最初的那句话…有一个数据是长这个样子的:
这里写图片描述

然后8是非城市点,其他都是城市点,显然水泵应该安在8,按照我们的代码我们会把水泵安在9,但是这样安放其实本质上是把水泵安在了8,我们维护了城市所在的集合,这个集合中的元素都比城市低,所以无论安在哪一处都会使得城市被抽干,所以其实是安在了这个集合中的点而不是城市这个点…


代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
//by NeighThorn
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1000+5;
int n,m,h[maxn][maxn],fa[maxn*maxn],cnt,ans,mv[4][2]={1,0,-1,0,0,1,0,-1},have[maxn*maxn];
struct M{
    int x,y,hei;
    friend bool operator < (M a,M b){
        return a.hei<b.hei;
    }
}s[maxn*maxn],l[maxn*maxn];
inline int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
signed main(void){
    scanf("%d%d",&n,&m);
    cnt=0;memset(have,0,sizeof(have));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&h[i][j]);
            if(h[i][j]>0)
                s[++cnt].x=i,s[cnt].y=j,s[cnt].hei=h[i][j];
            else
                h[i][j]=-h[i][j];
            l[(i-1)*m+j].x=i,l[(i-1)*m+j].y=j,l[(i-1)*m+j].hei=h[i][j];
        }
    for(int i=1;i<=n*m;i++)
        fa[i]=i;
    for(int i=0;i<=n+1;i++)
        h[i][0]=h[i][m+1]=inf;
    for(int i=0;i<=m+1;i++)
        h[0][i]=h[n+1][i]=inf;
    sort(l+1,l+m*n+1);
    sort(s+1,s+cnt+1);ans=0;
    for(int i=1,k=1;i<=cnt;i++){
        for(;k<=m*n&&l[k].hei<=s[i].hei;k++){
            int x=l[k].x,y=l[k].y;
            for(int j=0;j<=3;j++){
                int xx=x+mv[j][0],yy=y+mv[j][1];
                if(h[xx][yy]<=h[x][y])
                    have[find((x-1)*m+y)]|=have[find((xx-1)*m+yy)],fa[find((xx-1)*m+yy)]=find((x-1)*m+y);
            }
        }
        if(!have[find((s[i].x-1)*m+s[i].y)])
            ans++,have[find((s[i].x-1)*m+s[i].y)]=1;
    }
    cout<<ans<<endl;
    return 0;
}

by >_< NeighThorn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值