GPLT L3-003. 社交集群

在社交网络平台注册时,用户通常会输入自己的兴趣爱好,以便找到和自己兴趣相投的朋友。有部分兴趣相同的人们就形成了“社交集群”。现请你编写程序,找出所有的集群。

输入格式:

输入的第一行给出正整数N(<=1000),即社交网络中的用户总数(则用户从1到N编号)。随后N行,每行按下列格式列出每个人的兴趣爱好: Ki: hi[1] hi[2] … hi[Ki] 其中Ki(>0)是第i个人的兴趣的数量,hi[j]是第i个人的第j项兴趣的编号,编号范围为[1, 1000]内的整数。

输出格式:

首先在第一行输出整个网络中集群的数量,然后在第二行按非递增的顺序输出每个集群中用户的数量。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

8
3: 2 7 10
1: 4
2: 5 3
1: 4
1: 3
1: 4
4: 6 8 1 5
1: 4

输出样例:

3
4 3 1

把存在相同爱好的人归为一个群,如上面的例子,2、4、6、8是一个集群,3、5、7是一个集群,1是一个集群,这里的数字是每个人的编号,由此可以得到存在三个集群,且数量分别是4 3 1。 从题中可以看出,这个题目可以用并查集求解。 init_father、find_father和union_father函数分别是并查集的三个基本操作,不理解并查集的可以看维基百科-并查集,这里我用了较多的容器,是为了方便求解,set是为了快速判断两个人是否存在相同的兴趣点,map是为了统计有几个社交集群,priority_queue是为了按非递增顺序输出社交集群的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <cstdio>
#include <vector>
#include <set>
#include <iterator>
#include <functional>
#include <map>
#include <queue>

using namespace std;

int *father;

void init_father(int n) {
father = new int[n + 1];
for (int i = 0; i <= n; i++) father[i] = i;
}

int find_father(int x) {
if (father[x] == x) return x;
return find_father(father[x]);
}

void union_father(int arg1, int arg2) {
int fa = find_father(arg1);
int fb = find_father(arg2);
father[fa] = fb;
}

int main() {
int n = 0, ki = 0, hobby = 0;
scanf("%d", &n);
init_father(n);
vector<set<int> > hobbies(n + 1);
for (int i = 1; i <= n; i++) {
scanf("%d:", &ki);
for (int j = 0; j < ki; j++) {
scanf("%d", &hobby);
hobbies[i].insert(hobby);
for (int k = 1; k < i; k++) {
if (hobbies[k].find(hobby) != hobbies[k].end()) {
union_father(i, k);
}
}
}
}

map<int, int> m;
for (int i = 1; i <= n; i++) {
m[find_father(i)]++;
}
printf("%ld\n", m.size());

priority_queue<int, vector<int>, less<int>> q;
for (auto it = m.begin(); it != m.end(); it++) {
q.push(it->second);
}
while (q.size() > 1) {
printf("%d ", q.top());
q.pop();
}
printf("%d", q.top());
return 0;
}