前言
之前学习数据结构时,对并查集接触不多,并不熟练。刷《算法笔记》的过程中,做到《算法笔记〉的题目(配套上机指南中仅一道练习题即本题),因此借此题,总结一下并查集的套路和正确思路
题目
When register on a social network, you are always asked to specify your hobbies in order to find some potential friends with the same hobbies. A "social cluster" is a set of people who have some of their hobbies in common. You are supposed to find all the clusters.
Input Specification:
Each input file contains one test case. For each test case, the first line contains a positive integer N (<=1000), the total number of people in a social network. Hence the people are numbered from 1 to N. Then N lines follow, each gives the hobby list of a person in the format:
Ki: hi[1] hi[2] ... hi[Ki]
where Ki (>0) is the number of hobbies, and hi[j] is the index of the j-th hobby, which is an integer in [1, 1000].
Output Specification:
For each case, print in one line the total number of clusters in the network. Then in the second line, print the numbers of people in the clusters in non-increasing order. The numbers must be separated by exactly one space, and there must be no extra space at the end of the line.
Sample Input:
8
3: 2 7 10
1: 4
2: 5 3
1: 4
1: 3
1: 4
4: 6 8 1 5
1: 4
Sample Output:
3
4 3 1
翻译过来大概就是,有一群人,每个人有若干爱好,构成若干社交圈,构成社交圈的规则是:若a与b有共同爱好,则a与b一个圈子,若此时a与c也有共同爱好,则b与c也是一个圈子。求有几个圈子。
错误思路
因为没有接触过并查集相关的题目,我一开始的思路是一口气构造出集合,即通过遍历验证是否有共同爱好,算出圈子,但是事实证明这条思路难以走通,难点在于:如何把没有共同爱好但却在同一个圈子的人联系起来。
正确思路
并查集,并非只有查找和合并这两个孤零零的功能。一个并查集的建立实际上也是通过这两个功能。即输入数据的过程中不断的合并,最终得到完整的集合。
因此,本题应该:(1)把拥有爱好i的人联系起来。 这可以通过数组记录第一个有爱好i的人,剩下的人与之合并来实现;(2)把间接在一个圈子里的人联系起来。 这可以通过对每个人j,把他,和他所拥有的所有爱好对应的第一个人,合并来实现;(3)最后,记录根结点的个数,并计算各节点对应的所有以之为根的结点的个数。
代码
我的代码基本参考《算法笔记》和柳神的博客,就不全部放上来了,仅记录几个关键点:
首先,查找与合并的代码:
int findFather(int a){
int temp = a;
while(father[a] != a)
a = father[a];
while(temp != father[temp]){
father[temp] = a;
temp = father[temp];
}
return a;
}
void Union(int a,int b){
int fa = findFather(a);
int fb = findFather(b);
if(fa != fb){
father[fa] = fb;
}
}
其次,建立集合的代码:
for(int i = 1;i <= n;i++){
scanf("%d:",&k);
for(int j = 1;j <= k;j++){
scanf("%d",&temphobby);
if(course[temphobby] == 0){
course[temphobby] = i;
}
Union(i,course[temphobby]);
}
}
建立集合的思路就是,第i个人通过加入同样拥有爱好j的某个人(令保存第一个人即可)所在的集合,来将多个集合合并。
总结
要活用并查集的合并、查找,通过某种属性(题目要求)建立他们的联系。