原题目: 1107 Social Clusters (30 分).
题意
社交圈中的人拥有一些相同的兴趣爱好(注意:并不是完全相同才在一个群中,而是只要有至少一个相同的爱好就在一个圈中)。
A social cluster is a set of people who have some of their hobbies in common.
给出社交网络中的总人数N(1-N-1),接着给出每人的爱好数K以及爱好ID(1~1000)。
① 输出社交圈数量(或许用set),
② 以及各圈人数(非递增)。
分析
- 题意分析:每个社交圈的结点是人,而不是爱好。爱好是用来判断是否处在一个社交群的;即若一个人与圈内人有至少一个相同的爱好,就会被并入这个圈中——并查集。
- 确定了用并查集,则先写好初始化、findFather、Union。
- 生成社交圈:
①hobby[t]表示任意一个爱好为t的人的编号——不妨用录入的第一个喜欢t的人的编号。
②合并: 对于编号为i的人,录入其爱好t,则需要将 i 合并到同样喜欢 t 的人的圈子里——(ⅰ) findFather(hobby[t])就是喜欢t的人所处的社交圈的根结点,(ⅱ) 合并 i 与 同好圈。 - 计算社交圈的个数和圈内人数,即根结点个数和同根结点的结点个数——① 先用数组isRoot按下标统计同根结点的结点个数,② 再用计数器cnt计算数组isRoot中非0元素个数。
知识点
- 并查集的应用——将有相同爱好的人合并到同一个社交圈中;写好三个基本操作,学会合理利用。
- 静态数组——利用随机访问性,可以 按下标 记录 或统计个数。
CODE
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> father, isRoot;
bool cmp(int a, int b){
return a>b; //递减排序
}
int findFather(int x); //找结点x的根结点
void Union(int a, int b); //合并结点a和b所在集合
int main()
{
int n, k, t, cnt = 0;
int hobby[1001] = {0}; //hobby[t]表示任意一个喜欢t爱好的人的编号
cin >> n;
father.resize(n+1);
isRoot.resize(n+1);
for ( int i=1; i<=n; i++ )
father[i] = i; //并查集初始化,初始时各自独立,各自成根结点
for ( int i=1; i<=n; i++ ){
cin >> k;
string s;
cin >> s; //消耗掉一个冒号
for ( int j=0; j<k; j++ ){
cin >> t;
if ( hobby[t]==0 )
hobby[t] = i;
//将爱好为t的人所处社交群的根结点与当前人的编号i结点合并
Union(i, findFather(hobby[t]));
}
}
for ( int i=1; i<=n; i++ )
isRoot[findFather(i)]++; //记录同一个根结点的社交群中的总人数
for ( int i=1; i<=n; i++ ){
if ( isRoot[i]!=0 )
cnt++; //记录社交群的个数
}
cout << cnt << endl;
sort(isRoot.begin(), isRoot.end(), cmp);
for ( int i=0; i<cnt; i++ ){
cout << isRoot[i];
if ( i!=cnt-1 ) cout << " ";
}
return 0;
}
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;
}