题目大意:
有N个人,如果任意2个人的爱好有相同的(就是有交集),那么这2个人就是属于同一个社交网络,要求输出这N个人组成了几个社交网络,并且输出每个社交网络的人数
算法思路:
此题考察的是并查集的使用(并查集主要用来处理 若干个节点,有些节点互相相连,有些节点没有连接,如何判断其中两个节点是否相连这样的问题),目前来看,使用并查集求解此题应该是最好的选择,可以使用构建人与人的关系图,然后遍历有多少个连通分量,每一个连通分量有多少节点,但是这种方式实现起来过于复杂。
并查集主要由1个集合,2个操作所组成,集合就是father保存每一个节点的父节点,2个操作分别是查询节点x的父亲节点和合并2个节点所在的集合。入下面代码所示:
int father[1005];// 每一个节点的父亲节点
// 查询节点x的父亲节点
int findFather(int x){
while(x!=father[x]){
x = father[x];
}
return x;//father[x]==x的才是根节点
}
// 合并a和b节点所在的集合
void Union(int a,int b){
int fa = findFather(a);
int fb = findFather(b);
if(fa!=fb){
father[fa] = fb;
}
}
在此题中,我们需要构建一个人与人之间相互关联的社交网络,媒介是每一个人所拥有的爱好,那么我们使用$hobbyOwner$记录每一个爱好的所有者,这样在输入每一个爱好的时候,如果当前爱好没有所有者,将当前爱好标记为自己所独有,否则就将该爱好所有者和当前人合并到一个社交网络。在每一个社交网络构建完成后,我们需要统计社交网络的个数和每一个社交网络的人数,我们使用$roots(set集合)$保存每一个社交网络的根节点,其个数即为社交网络的个数,$cluster$保存每一个社交网络的人数,具体做法就是,需要遍历每一个人,找到其根节点$root$,然后添加进$roots$中,并且统计当前社交网络的人数++cluster[root]
。最后对cluster进行排序输出即可。
int cluster[1005];// 每一个社交网络的人数
unordered_set<int> roots;// 保存每一个根节点
// 统计社交网络的个数,也就是根节点的个数
for (int k = 1; k <= N; ++k) {
int root = findFather(k);
++cluster[root];
roots.insert(root);
}
注意点:
- 1、有一种情况需要特别注意,如果现在已经组建好了社交网络A和B,那么现在有一个人的爱好既有A又有B,那么他将作为连接A和B的桥梁,需要将每一个人的所有爱好都要进行处理,并且由于合并后,只更新了一个社交网络的根节点的父亲,该社交网络的其他节点的父亲依然没有变化,所以在统计社交网络的人数的时候不能直接使用father数组,一定得从新遍历每一个节点获取其父亲,否则测试点1,4,5会错误。
提交结果:
AC代码:
#include <cstdio>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
using namespace std;
int father[1005];// 每一个节点的父亲节点
unordered_map<int,int> hobbyOwner;//每一个爱好的所有者
int cluster[1005];// 每一个社交网络的人数
// 初始化每一个节点的父亲
void init(){
for (int i = 1; i <= 1000; ++i) {
father[i] = i;
}
}
// 查询节点x的父亲节点
int findFather(int x){
while(x!=father[x]){
x = father[x];
}
return x;//father[x]==x的才是根节点
}
// 合并a和b节点所在的集合
void Union(int a,int b){
int fa = findFather(a);
int fb = findFather(b);
if(fa!=fb){
father[fa] = fb;
}
}
bool cmp(int a,int b){
return a>b;
}
int main()
{
init();
int N;// 节点个数
scanf("%d",&N);
for (int i = 1; i <= N; ++i) {
int K;
scanf("%d: ",&K);
for (int j = 0; j < K; ++j) {
int hobby;
scanf("%d",&hobby);
if(hobbyOwner[hobby]==0){
// 当前爱好没有人拥有
hobbyOwner[hobby] = i;
} else {
// 已经拥有了,合并拥有者和i
Union(hobbyOwner[hobby],i);
}
}
}
unordered_set<int> roots;// 保存每一个根节点
// 统计社交网络的个数,也就是根节点的个数
for (int k = 1; k <= N; ++k) {
int root = findFather(k);
++cluster[root];
roots.insert(root);
}
printf("%lu\n",roots.size());
sort(cluster+1,cluster+N+1,cmp);
for(int i=1;i<=roots.size();++i){
printf("%d",cluster[i]);
if(i<roots.size()) printf(" ");
}
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。