【L2-024 部落】天梯赛L2系列详解

天梯赛L2-024 部落

题目详情:

在这里插入图片描述
在这里插入图片描述
输入样例:

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

输出样例:

10 2
Y
N

思路:

很明显的并查集的一道题目,不了解并查集的小伙伴,去下面看链接看一个大佬写得并查集,超级牛的。
1、这个题目是怎么回事呢?意思也很简单:就是说,现在有好多个人,分成了好几个小圈子,这些小圈子,又可以组成一个大圈子,即部落。现在给你,这些小圈子的信息,问你一共有多少个人,以及多少个部落。

2、不同的部落之间,人都是互相不认识的。
同一个部落内,a和b认识,b和c认识,但a和c不认识,这种情况也是出现的,即同一个部落的两个人可能不认识,但是他们一定认识相同的人。这不就恰好符合并查集的条件了。这个题目还要求一共又多少人,用set容器就可以轻松解决,有几个部落的话,就看有几个根节点就可以了。

3、但是这个题目还要进行压缩路径,因为如果不压缩路径的话就会超时。
压缩路径的意思也很简单:比如一个部落里a是老大,b是a的直系小弟,c是b的直系小弟,如果我们想通过a找到c的话,我们需要a去找b,b再去找c,那么如果我们压缩了路径,就是让c变成a的直系小弟,这个样子我们就不用通过b找到c,就节省了时间。

4、并查集的三大步骤:
1、前驱数组pre[]{一定要先初始化!!! }
2、查找相同结点的函数find(){ 可能要压缩路径 }
3、合并函数Union()

详细代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 10000 + 5;
int pre[maxn], temp[maxn];//并查集的前驱节点数组,临时存变量的数组
int find(int x)//并查集-查:找根节点(压缩路径版:即每个节点的父节点就是根节点)
{
     if( x == pre[x])
        return x;
     return pre[x] = find(pre[x]);//让x直接变成老大的直系小弟
     //直接递归不理解的话,看下面的扩展的代码
     /*int y = x;//先把x赋给y
    while(x != pre[x])//找到x的根节点
        x = pre[x];
    while(y != pre[y])//压缩路径
    {
        int z = y;//把y赋给z
        y = pre[y];//求y的前驱节点,进行下一步循环
        pre[z] = x;//令z的前驱节点为x(此时x为x的根节点)
    }//这个过程什么意思呢:就是现在的z就是开始的x,为了找x的根节点,需要先找x的父节点,再找x父节点的父节点...
    //直到找到根节点,那么现在就将x的前驱节点(即父节点)直接变为x的根节点,从而压缩了路径 
    return x;*/
    
}
void Union(int x,int y)//并查集-并:合并两个节点的根节点
{
    int fx = find(x);
    int fy = find(y);
    if(fx < fy)//本题可以将最大的数作为根节点
        pre[fx] = fy;
    if(fy < fx)
        pre[fy] = fx;
}
int n, k, q;
set<int>num;//用于存一共有多少个人
int main()
{
    for(int i=0; i<maxn; i++)//并查集-初始化前驱数组
    {
        pre[i] = i;
    }
    cin>>n;
    for(int i=0; i<n; i++)
    {
        cin>>k;
        for(int j=0; j<k; j++)
        {
            int x;
            cin>>x;
            temp[j] = x;
            num.insert(x);
            if(j>0)
                Union(temp[j],temp[j-1]);
        }
    }
    int num1 = 0;
    for(int i=1; i<=num.size(); i++)//求部落数
    {
        if( i == find(i))//就看有多少i就是根节点,那么就有几个部落
            num1++;
    }
    cout<<num.size()<<" "<<num1<<endl;
    cin>>q;
    for(int i=0; i<q; i++)
    {
        int x,y;
        cin>>x>>y;
        if(find(x) == find(y))//原来是这里写错了,写成pre[x] == pre[y],这个肯定是不对的,因为x和y的前驱节点和根节点是不一样的概念
            cout<<"Y\n";
        else
            cout<<"N\n";
    }
}

知识总结:

并查集的链接:https://www.cnblogs.com/xzxl/p/7226557.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值