|
题目大意
有n个人,每个人喜欢k个活动,如果两个人有任意一个活动相同,就称为他们处于同一个社交网络。求这n个人一共形成了多少个社交网络。思路解析
可将爱好看做边,人看做顶点。所以本题问的就是极大联通子图的个数,与各联通子图内顶点的数量。由于本题并没有给出顶点的信息,而是各条边的信息。所以必然要用并查集统计连通图的个数。所谓并查集,简单地说就是,把一群节点分成若干个集合,每个集合内部都是联通的,但集合之间不连通,判断两个节点是否属于一个集合的充要条件是,两个节点分别至少存在一条路径,可以到达同一个节点。
放到本题的背景下,给出一个人的爱好,通过他的爱好,不断将这个人归属到不同圈子,在归属新圈子的同时,老圈子和他一起归属到新圈子之下,于是两个圈子变成了一个大圈子;如此循环下去,让你找出最终剩下的圈子。
示例代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> father;
int find_root(int v) {
int a = v;
while (v != father[v]) {
v = father[v];
}
while (a != father[a]) {//优化树形结构
int z = a;
father[z] = v;
a = father[a];
}
return v;
}
void Union(int x, int y) {
int x_root = find_root(x);
int y_root = find_root(y);
if (x_root != y_root) {
father[x_root] = y_root;
}
}
bool cmp(int a, int b) {
return a > b;
}
int main() {
int n;
scanf("%d", &n);
father.resize(n + 1);
vector<int> isfount(1010);
for (int i = 1; i <= n; i++) {
father[i] = i;
}
for (int i = 1; i <= n; i++) {
int num, temp;
scanf("%d: ", &num);
for (int j = 0; j < num; j++) {
scanf("%d", &temp);
if (isfount[temp] == 0) {
isfount[temp] = i;
}
Union(i, isfount[temp]);
}
}
vector<int> roots(n + 1);
for (int i = 1; i <= n; i++) {
roots[find_root(i)]++;
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (roots[i] != 0) {
cnt++;
}
}
sort(roots.begin(), roots.end(), cmp);
printf("%d\n%d", cnt, roots[0]);
for (int i = 1; i < cnt; i++) {
printf(" %d", roots[i]);
}
return 0;
}