第十章 并查集

1013.战争中的城市

在这里插入图片描述

  • KM<3500000,根据时间复杂度,对于K次询问,每次很可能是去遍历每一条边
  • 除去一个节点后,初始状态应当有n-1个集合,我们对于每一次询问,遍历每一条边,并且将没有在一个集合内的点相互合并,每次合并之后,集合数目减一,最后需要连通的边数就是集合数目减一
#include<iostream>
#include<algorithm>

using namespace std ;

const int N=1010,M=500010;

int n,m,k;

int p[N];

struct Edge{
    
    int a,b;
    
}edge[M];

int find(int x){
    
    if(p[x]!=x)p[x]=find(p[x]);
    
    return p[x];
}

int main(){
    
    scanf("%d %d %d",&n,&m,&k);       
    
    for(int i=0;i<m;i++){
        
        scanf("%d %d",&edge[i].a,&edge[i].b);   //一共有50w条边,输入数据超过100w用cin会超时
    }
    
    while(k--){
        
        int x;
        
        cin>>x;
        
        for(int i=1;i<=n;i++)p[i]=i;      
        
        int cnt=n-1;                          //除去一个点,初状态有n-1个集合
        
        for(int i=0;i<m;i++){
            
            int a=edge[i].a,b=edge[i].b;
            
            if(a!=x && b!=x){
                
                int pa =find(a),pb=find(b);   //如果不在一个集合内,合并,联通集合数目减减
                
                if(pa!=pb){
                    
                    p[pa]=pb;
                    
                    cnt--;
                }
            }
        }
        
        cout<<cnt-1<<endl;
    }
}

1114.家产 **

在这里插入图片描述

  • 考虑到最多有1000组输入而每个人最多有7个亲人,所以最多的人数在7000人
  • 首先建立好每条边(即根据输入在每一个有关联的人之间建立一条边,最多7000条)
  • 其次遍历每一条边,用并查集合并集合,每次合并都将根节点序号更大的集合合并到根节点更小的 集合
  • 维护根节点的数据即当前家庭的数据(包括人数,房产数,房产面积)
  • 按照要求排序输出
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<vector>

using namespace std;

const int N=1e4+10;

int p[N],c[N],hv[N],ha[N];         //家庭序号,家庭人数,家庭房产数,家庭房产面积

int n;

bool st[N];

int find(int x){
    
    if(p[x]!=x)p[x]=find(p[x]);
    
    return p[x];
}

struct Edges{
    
    int a,b;
    
}edge[N];

struct Family{
    
    int id,c,hv,ha;                             //家庭代表id,人数,房产数,房产面积
    
    bool operator < (const Family & t)const {
        
        // ha/c > t.ha/t.c --->ha * t.c > t.ha * c;
        
        if(ha * t.c != t.ha * c) return ha*t.c > t.ha*c; //保证精度,用交叉乘法代替
        
        else return id< t.id;
    }
};

int main(){
    
    cin>>n;
    
    int cnt=0;
    
    while(n--){
        
        int id,father,mother,k;                         //建好所有关联边
        
        cin>>id>>father>>mother>>k;
        
        st[id]=true;
        
        if(father!=-1)edge[cnt++]={id,father};
        
        if(mother!=-1)edge[cnt++]={id,mother};
        
        for(int i=0;i<k;i++){
            
            int son ;
            
            cin>>son;
            
            edge[cnt++]={id,son};
        }
        
        cin>>hv[id]>>ha[id];
    }
    
    for(int i=0;i<N;i++)p[i]=i,c[i]=1;
    
    for(int i=0;i<cnt;i++){                        //将每个家庭集合合并
        
        int a=edge[i].a,b=edge[i].b;
        
        int pa=find(a),pb=find(b);
        
        st[a]=st[b]=true;
        
        if(pa!=pb){
            
            if(pb>pa)swap(pa,pb);
            
            c[pb]+=c[pa];
            
            hv[pb]+=hv[pa];
            
            ha[pb]+=ha[pa];
            
            p[pa]=pb;
        }
    }
    
    vector<Family> family;
    
    for(int i=0;i<N;i++){             //找到每个家庭代表并且排序输出
        
        if(st[i] && p[i]==i)family.push_back({i,c[i],hv[i],ha[i]});
    }
    
    sort(family.begin(),family.end());
    
    cout<<family.size()<<endl;
    
    for(auto it :family){
        
        printf("%04d %d %.3lf %.3lf\n",it.id,it.c,(double)it.hv/it.c,(double)it.ha/it.c);
    }
}

1118.森林里的鸟

在这里插入图片描述

#include<iostream>
#include<cstring>

using namespace std;

const int N=1e4+10;

int n;

int p[N],birds[10];

bool st[N];

int find(int x){
    
    if(p[x]!=x)p[x]=find(p[x]);
    
    return p[x];
}

int main(){

    scanf("%d",&n);
    
    for(int i=1;i<N;i++)p[i]=i;
    
    while(n--){
        
        int k;
        
        scanf("%d",&k);
        
        for(int i=0;i<k;i++){
            
            scanf("%d",&birds[i]);
            
            st[birds[i]]=true;
        }
        
        for(int i=1;i<k;i++){
            
            int a=birds[i-1],b=birds[i];
            
            int pa=find(a),pb=find(b);
            
            if(pa!=pb)p[pa]=pb;
        }
    }
    
    
    int res=0,cnt=0;
    
    for(int i=1;i<N;i++){
        
        if(st[i]){
            
            cnt++;
            
            if(p[i]==i)res++;
        }
    }
    
    cout<<res<<" "<<cnt<<endl; 
    
    int q;
    
    scanf("%d",&q);
    
    while(q--){
        
        int a,b;
        
        scanf("%d %d",&a,&b);
        
        int pa=find(a),pb=find(b);
        
        if(pa!=pb)cout<<"No"<<endl;
        
        else cout<<"Yes"<<endl;
    }
}

1107.社会集群 **

在这里插入图片描述

  • 首先将数据读入到邻接表中,该邻接表表头为所有爱好编号,成员为拥有该爱好的人的序号
  • 然后根据所有具有相同爱好的人进行合并集合
  • 最后遍历并查集,找出总集合数目
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>

using namespace std;

const int N=1010;

vector<int> hobby[N];    //邻接表

int p[N],cnt[N];         

bool st[N];

int find(int x){
    
    if(p[x]!=x)p[x]=find(p[x]);
    
    return p[x];
}

int main(){
    
    int n;
    
    cin>>n;
    
    for(int i=1;i<=n;i++)p[i]=i,cnt[i]=1;
    
    for(int i=1;i<=n;i++){              //向邻接表中读入所有具有相同爱好的人
        
        int k;
        
        scanf("%d:",&k);
        
        while(k--){
            
            int x;
            
            scanf("%d", &x);
            
            hobby[x].push_back(i);
        }
    }
    
    int res=0;
    
    vector<int> nums;
    
    for(int i=1;i<=1000;i++)                     //对于每一个爱好,合并其成员
       for(int j=1;j<hobby[i].size();j++){
           
           int a=hobby[i][0],b=hobby[i][j];
           
           int pa=find(a),pb=find(b);
           
           if(pa!=pb){
               
               cnt[pa]+=cnt[pb];
               
               p[pb]=pa;
           }
       }
       
    for(int i=1;i<=n;i++){               //统计总集合数和其成员个数
        
        if(p[i]==i){
            
            res++;
            
            nums.push_back(cnt[i]);
            
        }
    }
    
    sort(nums.begin(),nums.end(),greater<int> ());
    
    cout<<res<<endl;
    
    cout<<nums[0];
    
    for(int i=1;i<nums.size();i++)cout<<" "<<nums[i];
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值