7-12 朋友圈(并查集)

题目描述

某学校有N个学生,形成M个俱乐部。每个俱乐部里的学生有着一定相似的兴趣爱好,形成一个朋友圈。一个学生可以同时属于若干个不同的俱乐部。根据“我的朋友的朋友也是我的朋友”这个推论可以得出,如果A和B是朋友,且B和C是朋友,则A和C也是朋友。请编写程序计算最大朋友圈中有多少人。

输入格式

输入的第一行包含两个正整数N(≤30000)和M(≤1000),分别代表学校的学生总数和俱乐部的个数。后面的M行每行按以下格式给出1个俱乐部的信息,其中学生从1~N编号:

第i个俱乐部的人数Mi(空格)学生1(空格)学生2 … 学生Mi

 输出格式

输出给出一个整数,表示在最大朋友圈中有多少人。

输入样例:
7 4
3 1 2 3
2 1 4
3 5 6 7
1 6
输出样例:
4
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB

方法1:


这是第一种方法,此方法主题思路是以空间换时间的方法来进行运算;
即使不用O2,O3优化仍然能过,

#pragma GCC optimize(2)//O2,O3加速
#pragma GCC optimize(3)
#include<iostream>
using namespace std;
#define N 100000
int p[N],d[N];//每个始组的子孙数,即每个集合的人数

int chux[N],st[N];//chux[N]:每个输入的数是否出现,st[N]:用于最终操作,用来判断当前始祖是否出现过,
int q[N],tt=-1;//用来存储输入过的数;(模拟栈)
int q1[N],t1=-1;//用来存储进行完所有操作后,出现过的最终祖先(始祖)(即每个集合);(模拟栈)

int find(int x)
{
    return p[x]==x?x:p[x]=find(p[x]);
    //也可以用常规的迭代法求祖宗(始祖),因为此算法已经优化可以用常规算法求时间了(即使不开O2优化也能过);
    //while(p[x]!=x)x=p[x]; return x;
}

int Charu(int a,int b)
{
    int t1=find(a);
    int t2=find(b);
    p[t1]=t2;//使得b的祖先成为 a的祖先的祖先;
    p[a]=t2;//同时使得b的祖宗直接变成a的祖宗
    //这样下次搜寻a的祖先时,直接搜到b的祖先,节省时间
}

int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)p[i]=i;
    for(int i=1;i<=m;i++)
    {
        int k,fis;scanf("%d%d",&k,&fis);
        
        if(!chux[fis])chux[fis]++,q[++tt]=fis;//如果第一个数从来没出现过,则标记下这个数,同时存入栈q
        for(int j=1;j<k;j++)
        {
            int x;scanf("%d",&x);
           if(!chux[x])chux[x]++,q[++tt]=x;//如果这个数从来没出现过,则标记下这个数,同时存入栈q
            Charu(x,fis);// 使第一个数的祖宗(始祖)成为这一行所有数的祖宗(始祖)的祖宗(始祖)
        }
    }
   for(int i=0;i<=tt;i++)//tt+1是本次输入中所有出现过的数(同学);
    {
     int x=find( q[i] );//用x代替find(q[i])的原因,是因为防止多层数组嵌套发生越界现象
        if( !st[x] )st[x]++,q1[++t1]=x;//祖宗x未出现过,则标记x,并存入栈q1
        d[x]++;//该集合人数加1;
    }
    int maxx=0;
    for(int i=0;i<=t1;i++)
    {
        int x=q1[i];
        if(d[ x ]>maxx)maxx=d[ x ];//找到最大集合的人数
    }
    printf("%d",maxx);
    return 0;
}

方法2


第二种方法就是简单的合并集合找祖先的算法,速度有点慢;
第二种方法参考https://blog.csdn.net/weixin_41420443/article/details/87619759?ops_request_misc=&request_id=&biz_id=102&utm_term=%E6%9C%8B%E5%8F%8B%E5%9C%88PTA&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-87619759.142v83insert_down1,239v2insert_chatgpt&spm=1018.2226.3001.4187

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<iostream>
using namespace std;
#define N 100000
int p[N],d[N];//每个始组的子孙数,即每个集合的人数

int find(int x)
{
    return p[x]==x?x:p[x]=find(p[x]);
   //此算法有点慢,只能用这种找祖宗的方法才能过
    
}

int Charu(int a,int b)
{
    int t1=find(a);
    int t2=find(b);
    p[t1]=t2;//使得b的祖先成为 a的祖先的祖先;
}

int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)p[i]=i;
    for(int i=1;i<=m;i++)
    {
        int k,fis;scanf("%d%d",&k,&fis);
         
        for(int j=1;j<k;j++)
        {
            int x;scanf("%d",&x);
          
            Charu(x,fis);// 使第一个数的祖宗(始祖)成为这一行所有数的祖宗(始祖)的祖宗(始祖)
        }
    }
    int maxx=0;
    for(int i=1;i<=n;i++)
    {
           maxx=max(maxx,++d[find(i)]);//找到最大集合的人数
        //等效为:d[find(i)]++,maxx=max(maxx,d[ find(i) ]);
    }
    printf("%d",maxx);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

林未兮

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

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

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

打赏作者

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

抵扣说明:

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

余额充值