PTAzzuli2023天梯赛体验赛3-2门派

题目

在某个江湖中,相互认识的人会加入同一个门派,而互不认识的人不会加入相同的门派。若甲认识乙,且乙认识丙,那么甲和丙就算是认识的。对于给定的认识关系,请计算共有多少个门派,人数最多的门派有多少人。

输入格式

首先输入一个整数T,表示测试数据的组数,然后是T组测试数据。每组测试首先输入两个整数n、m(1≤n≤1000,1≤m≤n(n-1)/2),n表示总人数,m表示认识关系数。然后输入m行,每行两个整数A、B(1≤A,B≤1000,且A!=B),表示编号为A、B的两人互相认识。

输出格式

对于每组测试,输出门派总数和人数最多的门派拥有的人数。

输入样例

2
5 3
1 2
2 3
4 5
5 1
2 5

输出样例

2 3
4 2

思路

本题是一道并查集算法的基础题,解决此类连通块问题,就可以考虑到并查集,具体实现请看代码分析。

代码实现

#include<bits/stdc++.h>
using namespace std;
class UF{//定义类UF(并查集)
public:
    vector<int>pre;//父组
    vector<int>size;//门派的人数
    UF(int n){
        pre.resize(n);
        size.resize(n,1);//初始人数为1
        for(int i=0;i<n;i++){
            pre[i]=i;
        }//并查集初始化
    }
    int root(int x){
        return pre[x]=(pre[x]==x?x:root(pre[x]));
    }//寻找根,这里采用了路径压缩减少runtime
    void merge(int x,int y){
        x=root(x);
        y=root(y);
        if(x!=y){
            pre[x]=y;
            size[y]+=size[x];
        }
    }//将两数合并,并且将子门派的人数加入到父门派
};
int main(){
    int t;
    cin>>t;
    while(t--){
        int a,b,n,i,m,count,ren=0;
        cin>>n>>m;
        UF f(n);//定义对象
        while(m--){
            count=0;
            cin>>a>>b;
            f.merge(a-1,b-1);
        }//数组从0开始
        for(i=0;i<n;i++){
            if(f.pre[i]==i){//如果pre[i]==i,说明这是门派根
                count++;//记录门派根的个数
                ren=max(f.size[i],ren);//更新最大人数
            }
        }
        cout<<count<<' '<<ren<<endl;
    }//输出门派和人数
}

 C语言版本

#include<stdio.h>
#include<string.h>
int pre[1005],size[1005];//定义全局变量,否则新定义函数找不到
int root(int x);               //调用
void merge(int x,int y);       //调用
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int a,b,n,i,m,count,ren=0;
        scanf("%d%d",&n,&m);       
        //初始化每个人对应一个门派
        for(i=0;i<n;i++){
			pre[i]=i;//初始化父组,使每个门派指向自己
			size[i]++;//让每个门派人数初始为1
		}
        while(m--){
            count=0;
            scanf("%d%d",&a,&b);
            merge(a-1,b-1);//将两个同门派的人合并成一个门派,人数加1
        }//数组从0开始,故都-1对应合并
        for(i=0;i<n;i++){
            if(pre[i]==i){//如果pre[i]==i,说明这是门派根
                count++;//记录门派根的个数
                ren=size[i]>ren?size[i]:ren;//更新最大人数
            }
        }
        printf("%d %d\n",count,ren);
        memset(size,0,sizeof size);//重新给size赋值为零,为下次使用做准备
    }//输出门派和人数
}
int root(int x){
	return pre[x]=(pre[x]==x?x:root(pre[x]));
}//寻找根,这里采用了路径压缩减少runtime
void merge(int x,int y){
    x=root(x);
    y=root(y);
    if(x!=y){
        pre[x]=y;
        size[y]+=size[x];
    }
}//将两数合并,并且将子门派的人数加入到父门派,并将子门派人数加到父门派

反思

本题采用了并查集,并使用路径压缩的解法来减少runtime是一种高效的查询方式,帮助我们顺利解决连通块问题,可以借此来学习并查集的应用和理解方式。

尾声

希望通过这道题目让大家学会使用并查集,大家多多加油哦~不要忘啦给笔者的点赞关注收藏喔~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sinking tenderness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值