1、题目要求:

某学校有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
 
结尾无空行
 
2、题目解析:
首先我们来理清一下题目意思,A和B是朋友,B和C是朋友,那么A和C也是朋友,如果此时C又和D是朋友,那么A和D也是朋友,则A此时与B,C,D都是朋友,那么这个以A为起点
的朋友圈就有4个人。又如测试用例,第一行表示第一个圈子,有1,2,3这3个人,第二个圈子有1,4这两个人,因为1和4是好朋友,所以4和2、3都是朋友,因此以1为中心的这个
朋友圈共有4个人。这是一个以朋友为传递关系建立成的朋友圈,是一种连通性的问题。简单来说就是求这几个连通分支里点最多的连通分支,朋友圈代表连通分支,朋友圈里
的人代表连通分支上的点。所以题目就是求以朋友关系连起来的最大的朋友圈(连通分支)里有几个人(点)。
 
3、代码:
#include <bits/stdc++.h>
using namespace std;
#define MAXn 30001
int fa[MAXn];
int num[30001]; int Find(int x){
int r=x;
while(r!=fa[r])
r=fa[r];
int i=x,temp;
while(r!=fa[i]){
temp=fa[i];
fa[i]=r;
i=temp;
}
return r;
} void Union(int x,int y){
int fx=Find(x),fy=Find(y);
if(fx!=fy){
fa[fx]=fy;
num[fy]+=num[fx];
}
} int main(){
int n,m,people,first,p;
cin>>n>>m;
for(int i=0;i<=n;i++)
fa[i]=i;
for(int i=0;i<=n;i++)
num[i]=1;
while(m--){
cin>>people;
cin>>first;
while(--people){
cin>>p;
Union(first,p);
}
}
int max=0;
for(int i=0;i<=n;i++){
if(num[i] > max)
max=num[i];
}
cout<<max<<endl;
return 0;
}

4、“并查集” 说明:

首先本题是属于数据结构中的 “并查集“ 类型的题目,所以要理解本题的代码,我们就要先弄懂什么是并查集。

所以现在,我们先来搞懂 ”并查集“是什么?

顾名思义,”并“就是合并,”查“就是查找,”集“就是集合(元素唯一)。”并查集"主要代码结构有两个函数,一个主函数“,一个函数是 Find(x) ,一个是 Union(x,y)。

将每个集合看成每棵树,用数组 fa[]  来存储各个节点的父节点,fa[i] 表示 i 的父节点。如下图1,fa[2] = 1,fa[3] = 1,fa[4] = 2......  (图论基础)

若每棵树只有一个节点,那么它的父节点就是它自己,即 fa[i] = i,谁和谁都没有关系,如图2。

 合并:Union(x,y)

那么我们要怎么把它们建立联系呢,就是把单独的树合并成一颗大的树?

可以令每棵树的根节点都一样,那么它们就合并了,比如要将 图1中的1 和2 合并成一颗树,可以让1成为2的根节点(当然也可以让2成为1的根节点),

即 fa[2] = 1,fa[1] = 1,(fa[1] = 2,fa[2] = 2)这样就变成了图3.

若要将3也合并到1那颗树上,如图.可以知道fa[3] = 2, fa[2] = 1,fa[1] = 1,则把3合并过来的表达式:fa[3] = fa[1],

假设x=2,y=3 ,将2和3合并,则 fa[x] = y ;

这就是将集合合并起来,从上面两个图中我们可以发现若1都是根节点,那么fa[1] = 1,若2是根节点,那么fa[2] = 2,即fa[i] = i 表示此时 i 是这颗树的根节点,

”查“就是查找根节点。

找到根节点有什么用?

查找:Find(x)

找到一颗树的根节点就相当于找到了这颗树的编号,换个说法说就是某个集合的编号,可以知道这个人在哪个集合里。

如下图

我们想找5属于哪颗树,就得一个一个父节点网上找,直到找到根节点 Find(5) = 1才知道5属于上面那颗树。如果我们想找8,那么我们必须找到8的根节点 Find(8) = 6

才知道8属于下面那棵树。

因此函数 Find(x) 就是查找 x的根节点,返回值是一个数(根节点)。

如果我想让5和6成为好朋友呢?只需要在5和6之间画一条线连起来,或者之间让6的父节点之接变成1,这样6就属于5的那颗树啦,如下图

或者这样也行,

即让5的根节点等于6的根节点,因此合并的原理就是:查找x和y 的根节点,fx = Find(x) , fy = Find(y) ;令y的根节点等于x的根节点,Find(fx) = fy。

说到这,想必都清楚”并“和”查“的意思了吧?不知道我说得清楚了没?文案和图有点乱。。。。

还有一点要注意的是,还有个"路径压缩",这个 “路径压缩” 是啥捏?

举个例子:如下图

假设1是根节点,(不分是否二叉树), 如果我们要找3的根节点是谁,那么上面图的做法是 fa[3] = 1,查找一次就找到了。而下面图的做法是 fa[3] = 2,不是根节点(怎么判断是不是根节点上文有说),

再查找 fa[2] = 1 找到了,是不是上面图的做法查询的速度会更快一点?如果有n个节点,那么下面图要查找n-1次了,此时效率比较低。所以“路径压缩” 就是要把树(集合)上的

节点都搞成上面图的样子,这样就缩短了查询根节点的路径了。

5、代码解析:

了解了“并查集 " ,那么我们现在就来看看本题的代码怎么实现吧!

#include <bits/stdc++.h>
using namespace std;
#define MAX 30001
int fa[MAX];//存储父节点
int num[30001];//存储节点数 /*查找根节点*/
int Find(int x){
//找x的根节点
int r=x;//x赋给r,以免中间发生变化后x改变
while(r!=fa[r])//循环直到找到根节点,上文第4点有说例子
r=fa[r];//让r的父节点等于r,直到找到x的根节点,此时就可返回r了,下面是为了压缩路径写的代码
//路径压缩
int i=x,temp;//temp为中间变量
while(r!=fa[i]){//假设链式树,“1---2---3---4” 根节点r=1,x=3,即要把3直接套在1后面,这里上文“路径压缩”有说到。
temp=fa[i];//把3的父节点2赋给temp
fa[i]=r;//让3的父节点等于1
i=temp;//把3的父节点2赋给i,继续循环把2的根节点变成1
}
return r;//最后返回根节点r
} /*合并两个集合*/
void Union(int x,int y){
int fx=Find(x),fy=Find(y);//分别找到x和y的根节点
if(fx!=fy){//如果x和y的根节点不一样,说明两个人不在一棵树上,此时就要合并两个人
fa[fx]=fy;//让y的根节点fy成为x的根节点,两棵树(集合)就合并了
num[fy]+=num[fx];//fy这颗树多加num[fx]个人,num[fx]代表以fx为根节点所在的树上有几个节点
}
} int main(){
int n,m,people,first,p;
cin>>n>>m;
//给每个人编号,并让每个人的父节点都是自己
for(int i=0;i<=n;i++)
fa[i]=i;
//定义一个num[]数组来存储第i棵树(朋友圈)上有几个节点(人),刚开始都是一个人,所以初始化为1
for(int i=0;i<=n;i++)
num[i]=1;
//输入数据
while(m--){
cin>>people;//每一行中第一个数是这个圈子有几个人
cin>>first;//第二个数在这里输入,是为了将第一个数作为每一行(每一个圈子)的根,用来标识朋友圈加以区别
while(--people){
cin>>p;//依次输入每个人的编号
Union(first,p);//每输入一个人就合并到第一个人的树(圈子)上,这样每一行的人都代表一个圈子里的人
}
}
//求出最大的树(圈子)有几个节点(人)
int max=0;
for(int i=0;i<=n;i++){//人的编号从1~N
if(num[i] > max)
max=num[i];
}
cout<<max<<endl;
return 0;
}

不知道我讲的明白了没,如果大家还没理解,可以看看这个博客,畅通工程并查集详解

PTA 朋友圈 (25 分) 代码详解 (并查集)的更多相关文章

  1. 图论-欧拉图-欧拉回路-Euler-Fluery-Hierholzer-逐步插入回路法-DFS详解-并查集

    欧拉图性质: 1.无向连通图G是欧拉图,当且仅当G不含奇数度结点(G的所有结点度数为偶数): 2.无向连通图G含有欧拉通路,当且仅当G有零个或两个奇数度的结点: 3.有向连通图D是欧拉图,当且仅当该图 ...

  2. 开胃小菜——impress.js代码详解

    README 友情提醒,下面有大量代码,由于网页上代码显示都是同一个颜色,所以推荐大家复制到自己的代码编辑器中看. 今天闲来无事,研究了一番impress.js的源码.由于之前研究过jQuery,看i ...

  3. JAVA类与类之间的全部关系简述+代码详解

    本文转自: https://blog.csdn.net/wq6ylg08/article/details/81092056类和类之间关系包括了 is a,has a, use a三种关系(1)is a ...

  4. Github-karpathy/char-rnn代码详解

    Github-karpathy/char-rnn代码详解 zoerywzhou@gmail.com http://www.cnblogs.com/swje/ 作者:Zhouwan  2016-1-10 ...

  5. 代码详解:TensorFlow Core带你探索深度神经网络“黑匣子”

    来源商业新知网,原标题:代码详解:TensorFlow Core带你探索深度神经网络“黑匣子” 想学TensorFlow?先从低阶API开始吧~某种程度而言,它能够帮助我们更好地理解Tensorflo ...

  6. Java中String的intern方法,javap&cfr.jar反编译,javap反编译后二进制指令代码详解,Java8常量池的位置

    一个例子 public class TestString{ public static void main(String[] args){ String a = "a"; Stri ...

  7. Kaggle网站流量预测任务第一名解决方案:从模型到代码详解时序预测

    Kaggle网站流量预测任务第一名解决方案:从模型到代码详解时序预测 2017年12月13日 17:39:11 机器之心V 阅读数:5931   近日,Artur Suilin 等人发布了 Kaggl ...

  8. 委托与事件代码详解与(Object sender,EventArgs e)详解

    委托与事件代码详解 using System;using System.Collections.Generic;using System.Text; namespace @Delegate //自定义 ...

  9. Github-jcjohnson/torch-rnn代码详解

    Github-jcjohnson/torch-rnn代码详解 zoerywzhou@gmail.com http://www.cnblogs.com/swje/ 作者:Zhouwan  2016-3- ...

随机推荐

  1. 什么是 Shell 脚本?

    Shell 既是一种命令语言,又是一种程序设计语言.Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务.Windows Explorer 是一个典型的图形 ...

  2. CentOS6.5 mini安装到VirtualBox虚拟机中

    下载Oracle VM VirtualBox 下载下来安装 下载镜像 http://archive.kernel.org/centos-vault/6.5/isos/i386/CentOS-6.5-i ...

  3. SpringMvc实现批量删除,使用post传值一直报404错误

    Ajax结合SpringMVC实现批量删除信息,在前台使用post向后台传递要删除的id的集合额时候,一直报404错误, 前台post传值的源码如下: 了解一下: (1)第二行的rows为前面得到的一 ...

  4. Rust 与 Golang - 何时使用它们?

    [转自 Fizer Khan的<Rust Vs Golang - When to use them?>(翻译)] 在过去的十年中,Rust 和 Go 两种新的编程语言主要为企业开发而开发和 ...

  5. Python实现 利用朴素贝叶斯模型(NBC)进行问句意图分类

    目录 朴素贝叶斯分类(NBC) 程序简介 分类流程 字典(dict)构造:用于jieba分词和槽值替换 数据集构建 代码分析 另外:点击右下角魔法阵上的[显示目录],可以导航~~ 朴素贝叶斯分类(NB ...

  6. linux挂载光驱

    挂载光驱到linux中.linux的镜像盘中有安装oracle的所有的软件包,可以会用yum一键安装. 1.此时的linux的界面显示光驱图标 2.挂载 因为光盘里面的文件是只读模式的,yum安装时不 ...

  7. Spring BeanFactory和现实工厂的对比

    本文不分析Spring的源码流程,只是介绍一些基础的概念,在阅读源码之前,我们应该首先明确研究的对象是什么,才能有的放矢. Spring作为BeanFactory, 和现实工厂有着许多类似之处. 需要 ...

  8. Caffeine缓存的简单介绍

    1.简介 在本文中,我们将了解Caffeine,一个用于Java的高性能缓存库. 缓存和Map之间的一个根本区别是缓存会清理存储的项目. 一个清理策略会决定在某个给定时间哪些对象应该被删除,这个策略直 ...

  9. 不用SCRAPY也可以应用selector

    在PY文件中: from scrapy.selector import Selectorfrom scrapy.http import HtmlResponse url="https://m ...

  10. java02动手动脑

    1 编写一个方法,生成一千个随机数,用ppt提供的纯随机数发生器. 做这个题目时,看到老师已经给出Xn+1=(aXn+c) mod Integer.MAX_VALUE;给出了公式自然就算法明了. 我想 ...