题目
题目链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805361586847744
思路
首先请把题目读懂,读不懂就多读几次,或者看看别人的题解。
如果画图的话,一眼就能看出结果。但是将画图的思路转换成代码,那么考的就是并查集。
并查集,简单合并,但是如果按照维斯的数据结构生搬硬套不行,也就是父指针数组初始化为-1
在这里不行。推荐柳神的初始化策略,每个数字的父指针都初始化为本身。但是集合的大小就需要另外编程,这时候可以对父指针数组S进行一次遍历,S[i]对应的计数器数组++就行。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1005;
//比较函数
bool cmp(int a, int b){
return a>b;
}
int S[maxn];//存放父指针
int isRoot[maxn] = {0};
int hobby[maxn] = {0};
//查找元素的根
int Find(int x){
if(S[x] == x) return x;
else return S[x] = Find(S[x]);//路径压缩
}
//把两个人合并
void SetUnion(int person1, int person2){
if(person1 == person2) return;
int root1 = Find(person1);
int root2 = Find(person2);
// 如果在不同的集合
if(root1 != root2){
S[root1] = root2;//这里不能互换,因为我要跟先前的人成为朋友,这里因为hobby[i]只记录第一个喜欢他的人的值
}
}
int main(){
int n, k, c, cnt = 0;
scanf("%d", &n);
for(int i=1; i<=n; i++){
S[i] = i;//初始化,每个节点本身是一个集合,根是自己
}
for(int i=1; i<=n; i++){//这里i从1开始变化的,debug血泪史
scanf("%d:", &k);//第k个人
for(int j=0; j<k; j++){
scanf("%d", &c);//兴趣的下标
if(hobby[c] == 0){
//如果这个兴趣原来没有人喜欢
hobby[c] = i;//现在这个hobby被第i个人喜欢
}
//如果这个hobby已经有人喜欢,则这两个人可以合并(可以是自己,只不过不用合并)
SetUnion(i, hobby[c]);
}
}
//求每个集合的大小
//从头到位遍历一遍,都找其根,然后对应根的计数器++
for(int i=1; i<=n; i++){
isRoot[Find(i)]++;
}
for(int i=1; i<=n; i++){
if(isRoot[i] != 0) cnt++;
}
sort(isRoot+1, isRoot+n+1, cmp);
printf("%d\n", cnt);
for(int i=1; i<=cnt; i++){
printf("%d", isRoot[i]);
if(i < cnt) printf(" ");
}
return 0;
}
参考文献
- https://blog.youkuaiyun.com/qq_24452475/article/details/100059296?utm_medium=distribute.pc_relevant_right.none-task-blog-BlogCommendFromBaidu-15.channel_param_right&depth_1-utm_source=distribute.pc_relevant_right.none-task-blog-BlogCommendFromBaidu-15.channel_param_right
- https://blog.youkuaiyun.com/liuchuo/article/details/52191082
- 维斯数据结构C语言版