【校队排位赛#2 B题】 并查集之压缩路径算法

在这里插入图片描述

题意:有m对集合,每对表示的人有相同的信仰,问在n个人里面最多有多少种不同的信仰

并查集思路:

这就像一组组朋友圈,将具有相同属性的划分为一个集合。然后找到有多少集合即可。

实现方法:

我们把每个圈子挑出一个人作为根节点,所有其成员都只认这个人(看成代表),我们叫他父结点,对于每对组合,如果这两个人有相同的父结点(代表者),那么就说明这两个人是相同圈子里面的人。

否则,他们就是不同圈子里面的人然后这两个不同的圈子,又通过这两个人联系到一起,组成新的一个大集合,这时候不需要两个代表(父结点),只需要将后者的父结点再指向前者的父结点即可,可以形象地表示成圈子B的代表又隶属于圈子A的代表了这样,只要在这个圈子里面随便挑两个人,问他们的父结点,就又可以追溯到同一个人了(这也是为什么一个代表能表示一个集合的原因了)

具体代码如下,感觉这种压缩路径的思想很赞

#include <iostream>
#include <vector>
#include <cstdio>
#include <map>
#include <climits>
#include <string>
#include <cmath>
#include <cstring>
#include <stack>
#include <queue>
#include <algorithm>
#define maxn  50000+5
using namespace std;
typedef  long long ll;
int father[maxn];
int n;
ll m;
void init()   //初始化
{
     for(int i=1;i<=n;i++)
     father[i] = i;
}

int get(int x)   //层层向上找父节点,直到到达根部
{
    if(father[x]==x)
    return x;
    return father[x]=get(father[x]);  //压缩路径算法
}
void merge(int x, int y)   //集合合并
{
    int a = get(x);
    int b =get(y);
    if(a!=b)
    father[b] = a;
}


int main()
{
     int num = 1;
    while(~scanf("%d%lld",&n,&m)&&(n||m))
    {
          init();
          while(m--)
          {
              int x, y;
              cin>>x>>y;
              merge(x,y);
          }
          int ans = 0;
        /* std::map<int,int>  tofind;   //不能这样写,树的深度不一定为2
         for(int i=1;i<=n;i++)
         {
             if(!tofind[father[i]])
             {
                  ans++;
                  tofind[father[i]] = 1;
             }
         }*/
         for(int i=1;i<=n;i++)
         {
             if(i==father[i])
             {
                  ans++;
             }
         }
         printf("Case %d: %d\n",num++,ans);
    }
    return 0;
}

发布了32 篇原创文章 · 获赞 23 · 访问量 1807

猜你喜欢

转载自blog.csdn.net/qq_45492531/article/details/104465854