7-2 朋友圈 分数 25 作者 DS课程组 单位 浙江大学
某学校有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
整体思路:经典的并查集。题目说有M个俱乐部,那么我们将不同俱乐部中的人都指向第一个人。第一个人记录指向自己的人有多少个。如果别的俱乐部中有某个俱乐部中的人(只要有一个,那么根据朋友的朋友是朋友),就把这两个俱乐部并在一起。
小小的的复习一下并查集的相关知识:
主要的两个小小算法:
1、按秩归并(在Union中)
那么在按秩归并中又有两种不同的方法:
(1)比数高:即让S[root] = -树高(当然一开始所有元素默认初始化为-1,即初始高度为1)
void Union(int S[], int x, int y) {
int r1 = Find(S, x);
int r2 = Find(S, y);
if(S[r2] < S[r1]){//r2的树高比r1高 因为S[r]是树高的相反数
S[r1] = r2;//r1和r2并在一起,树高就是高的那个r2就变成r1的根结点
}else{
if(S[r2] == S[r1]) S[r1]--;//r1和r2的树高相等,那并起来高就多1
S[r2] = r1;
}
}
(2)比规模:即让S[root] = -树元素个数(一般采取这种方法,因为可以直观的知道这个集合里的元素有多少个,即为以根结点为下标数组值的相反数)
void Union(int S[], int x, int y) {
int r1 = Find(S, x);
int r2 = Find(S, y);
if (S[r1] < S[r2]) {//当r1中元素更多时
S[r1] += S[r2];
S[r2] = r1;
}
else{//两种情况:
// 1.当S[r2] < S[r1],即r2元素更多时,将r1的父结点指向r2。
// 2.当S[r2] == S[r1],即两个结点都有同一个父结点,也将r1的父结点指向r2。
S[r2] += S[r1];
S[r1] = r2;
}
}
2、路径压缩(在Find中)
int Find(int S[], int x){
if(S[x] < 0) return x; //找到集合的根
else return S[x] = Find(S, S[x]);
}
这里最后一个语句:
else return S[x] = Find(S, S[x]);
做了三件事:1. 找到集合的根 2. 把根变成x的父结点 3. 再返回根。
好了不多bb上代码:
#include<bits/stdc++.h>
using namespace std;
#define N 30005
int S[N];
int Find(int S[], int x) {
if (S[x] < 0) return x;
else return S[x] = Find(S, S[x]);
}
void Union(int S[], int x, int y) {
int r1 = Find(S, x);
int r2 = Find(S, y);
if (S[r1] < S[r2]) {
S[r1] += S[r2];
S[r2] = r1;
}
else if(S[r2] < S[r1] || S[r2] == -1 && S[r1] == -1) {
S[r2] += S[r1];
S[r1] = r2;
}
}
int main() {
int n, m, t, mx = 0, x, y;
cin >> n >> m;
for (int i = 1; i <= n; ++i) S[i] = -1;
for (int i = 1; i <= m; ++i) {
cin >> t >> x;
for (int j = 1; j < t; ++j) {
cin >> y;
Union(S, x, y);
}
}
for (int i = 1; i <= n; ++i) {
if (S[i] < 0) {
mx = max(mx, -S[i]);
}
}
cout << mx;
return 0;
}
最后面求最大集合就是用了按秩归并的比规模,以集合的根为数组下标的值就是这个集合元素值的相反数。
如有错误的地方或者不好的地方,请各位大佬指出,本人小萌新,请多多指教!