题意:给出n个人(编号为1~n)以及每个人的若干个爱好,把有一个或多个共同爱好的人归为一个集合,问共有多少个集合,每个集合里有多少个人?
思路:典型的并查集题目。并查集的模板init()函数,union()函数,findSet()函数就不多讲了。这里根据爱好来归类,因此,在读入数据时把爱好进行合并。设置数组hobby[],hobby[id]表示编号为id的这个人的一个爱好,如果某个人有多个爱好,只要记录一个就好了;设置数组num[],num[fa]表示以fa为根结点的爱好集合的人数,初始化为0。然后遍历n个人(人的编号的1~n),对于每个人i,因为我们已经记录了这个人的一个爱好(即hobby[i]),故可以找到该爱好所属的集合的根结点(即int fa=FindSet(hobby[i])),然后令num[fa]++即可。统计好每个集合的人数之后,再统计共有多少个集合,只需要遍历所有爱好(爱好的编号为1~1000),记录num[i]不为0的个数即可。
代码:
#include <cstdio> #include <algorithm> using namespace std; ; int hobby[maxn];//hobby[id]记录id的任意一个爱好 };//num[fa]记录以fa为根的集合的结点个数 int father[maxn]; bool cmp(int a,int b){return a>b;} void Init(){ ;i<maxn;i++) father[i]=i; } int FindSet(int a){ int root=a; while(root!=father[root]) root=father[root]; while(a!=father[a]){ int tmp=a; a=father[a]; father[tmp]=root; } return root; } void Union(int a,int b){ int setA=FindSet(a); int setB=FindSet(b); if(setA!=setB) father[setB]=setA; } int main() { Init(); int n,k; scanf("%d",&n); ;id<=n;id++){ int pre,curr; scanf("%d:%d",&k,&pre); hobby[id]=pre;//记录第i个人的一个爱好 ;j<k;j++){ scanf("%d",&curr); Union(pre,curr); pre=curr; } } //遍历n个人,统计每个集合的人数 ;id<=n;id++) num[FindSet(hobby[id])]++; //遍历所有爱好,统计集合的个数 ; ;i<maxn;i++) ) cnt++; sort(num,num+maxn,cmp); printf("%d\n",cnt); ;i<cnt;i++){ printf("%d",num[i]); ) printf(" "); } ; }