这道题是并查集的题,其实系统的看过并查集的一些函数过后,就不是很难,但是又很多细节我在编程的时候也忽略了。
- father[faA] = faB; 在合并的时候是让father进行操作
- father数组里面存储的并不一定是根,可能只是父亲,所以要用到根的时候,还是最好使用函数findfather()
- 在做题的时候,我其实考虑到用一个兴趣数组vector<> [] 去做,但是其实后来发现是自己还没有万完全的理解并查集,在并查集中,其实顺着一个点,就可以找到根,因此一个点,就足以代表一个集合中的所有点,我这样做,其实是很重复,无效的。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1100;
int father[maxn];
int isRoot[maxn] = {0};
int course[maxn] = {0};
vector<int> hobbies[maxn];
//找父亲结点+路径压缩
int findFather(int x)
{
int a = x;
while(x!=father[x])
{
x = father[x];
}
while(a!=father[a])
{
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
//合并两个结点
void Union(int a,int b)
{
int faA = findFather(a);
int faB = findFather(b);
if(faA != faB)
{
father[faA] = faB;
}
}
//每个结点初始化都是一个单独集合
void init(int n)
{
for(int i=1;i<=n;i++)
{
father[i] = i;
isRoot[i] = 0;
}
}
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int n;
//n代表有几个人
scanf("%d",&n);
init(n);
//把每一个人都加到相应的活动中
for(int i=1;i<=n;i++)
{
int num;
scanf("%d: ",&num);
for(int j=0;j<num;j++)
{
int hobby;
scanf("%d",&hobby);
if(course[hobby] == 0)
course[hobby] = i;
Union(i,findFather(course[hobby]));
// for(int k=0;k<hobbies[hobby].size();k++)
// {
// Union(i,hobbies[hobby][k]);
// }
// hobbies[hobby].push_back(i);
}
}
//遍历一遍计算根的子孩子个数
for(int i=1;i<=n;i++)
{
//cout << i << "father:" << father[i] << "\n";
isRoot[findFather(i)]++;
//isRoot[father[i]]++;
}
//计算根的个数
int RootNum=0;
sort(isRoot+1,isRoot+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(isRoot[i]==0)
break;
RootNum++;
}
printf("%d\n",RootNum);
for(int i=1;i<=RootNum;i++)
{
printf("%d",isRoot[i]);
if(i!=RootNum)
printf(" ");
}
}