并查集【模板】

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

体育课上,体育老师牛牛弄不清学生们都是哪个班级的,他只能随便找两个同学,问他们是不是一个班的,牛牛记下了属于同一个班的学生的序号,但是他弄不清自己教的班级的情况,请你帮助他。

假设体育老师的学生有n人,学号分别是1,2,...n,他的小本本记录了m行,每行记下了属于一个班级的两个同学的学号u, v.

请问他的学生一共属于几个班级?人数最多的班级有几个人?

输入描述:

第一行输入两个整数n,m,代表学生总数和关系的组数。
接下来m行,每行两个整数u和v,表示u和v是一个班级的。
1≤n,m≤105,1≤u,v≤n1 \le n, m \le 10^5, 1 \le u, v \le n1≤n,m≤105,1≤u,v≤n

输出描述:

输出一行2个整数,由空格分隔,第一个数表示有几个班级,第二个数表示人数最多的班级有几个人。

输入:

9 7
1 2
6 1
5 8
3 4
4 7
3 7
2 9

输出:

3 4

答案:

#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int fa[N];
int find(int x){
    if(fa[x] != x){
        int t = find(fa[x]); 
        fa[x] = t;
    }
    return fa[x];
}
void merge(int x,int y){
    int f1 = find(x);
    int f2 = find(y);
    if(f1 != f2){
        fa[f1] = f2; 
    }
}
int main(){
    int n,q;
    cin >>n >> q;
    for(int i = 0;i <=n;i++){
        fa[i] = i;
    }
    for(int i=1;i<=q;i++)
    {
        int a,b;cin>>a>>b;
        merge(a,b);
    }   
    map<int,int>mp;
    for(int i=1;i<=n;i++)
        merge(i,fa[i]);
    for(int i=1;i<=n;i++)
        mp[fa[i]]++;
    int mi=0;
    for(auto [x,y]:mp)
        mi=max(mi,y);
    cout<<mp.size()<<" "<< mi<<endl;
    return 0;
}

解释:

并查集原理是将数组设置为父亲指向本身通过一次次的查找来归并判断元素所在集合

{ai注释}

 map<int,int>mp;
    for(int i=1;i<=n;i++)
        merge(i,fa[i]);
    for(int i=1;i<=n;i++)
        mp[fa[i]]++;
    int mi=0;
    for(auto [x,y]:mp)
        mi=max(mi,y);
    cout<<mp.size()<<" "<< mi<<endl;
    return 0;

 

代码逻辑解释

  1. 初始化:定义了一个map<int, int>类型的mp,用于存储每个分组(以fa[i]作为分组的标识符)及其对应的元素数量。

  2. 合并操作:通过一个循环,对每个元素i(从1到n),执行merge(i, fa[i])操作。这个操作的具体实现没有给出,但假设其目的是将元素ifa[i]所在的分组合并(或者设置i的分组标识为fa[i])。然而,这个操作本身在代码片段中并不改变mp的内容,只是改变了元素的分组标识(如果merge函数是这样设计的)。

  3. 统计分组元素数量:再次通过一个循环,对于每个元素i,将fa[i]作为键在mp中计数。这意味着每个元素的分组标识(fa[i])都会被计数,从而统计出每个分组中的元素数量。

  4. 寻找最大分组和分组总数:通过遍历mp,找到最大分组的大小(即分组中元素的数量),并统计分组的总数(mp.size())。

  5. 输出结果:输出分组的总数和最大分组的大小。

潜在的问题和改进点

  1. merge函数的实现:代码中没有给出merge函数的实现,但基于上下文,这个函数应该负责更新元素的分组标识。如果merge函数只是简单地设置fa[i]的值,那么它可能不足以反映所有元素的分组状态,除非在其他地方有额外的逻辑来确保fa数组的一致性。

  2. fa数组的初始化:代码中没有显示fa数组的初始化。如果fa数组未正确初始化(例如,如果fa[i]的初始值不是有效的分组标识),那么合并操作可能无法正确执行。

  3. 效率考虑:如果merge函数涉及复杂的逻辑或大量数据移动,那么每次循环都调用它可能会非常低效。此外,如果fa数组在合并过程中频繁更改,那么可能需要在统计mp之前重新遍历所有元素以更新分组标识。

  4. 代码健壮性:如果n的值非常大,或者merge函数存在逻辑错误,那么代码可能会遇到性能问题或错误的结果。

        在C++中,使用auto关键字与结构化绑定(structured bindings)一起,可以很方便地从std::pairstd::tuplestd::arraystd::map(的迭代器)等容器中直接解包出多个值。但是,当你尝试直接使用auto [x,y]:mp这样的语法时,它是不正确的,因为这不是一个有效的语法结构。

如果你的目的是遍历std::map<int, int>(假设你的mp是这种类型)中的所有键值对,并分别将键和值赋给变量xy,你应该使用范围for循环结合结构化绑定。但是,由于mp本身是一个容器,而不是一个迭代器范围,你需要遍历mp的迭代器。

例子:

#include <iostream>  
#include <map>  
  
int main() {  
    std::map<int, int> mp = {{1, 2}, {3, 4}, {5, 6}};  
  
    // 使用范围for循环和结构化绑定遍历map  
    for (const auto& [x, y] : mp) {  
        std::cout << "Key: " << x << ", Value: " << y << std::endl;  
    }  
  
    return 0;  
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值