题目描述
链接
有n个人,每个人喜欢k个活动,如果两个人有任意一个活动相同,就称为他们处于同一个社交网络。求这n个人一共形成了多少个社交网络,降序输出每个社交网络的人数
分析
一看就知道是并查集的。关键在于什么时候进行合并。以及每个社交网络人数怎么统计。
先看问题二:每个社交网络人数怎么统计。
本质就是每个并查集的元素个数怎么统计。可以用一个数组(root)记录。代码如下!!!是套路!!要记住
for(int i=1;i<=n;i++){
root[find(i)]++;
}
如果要知道哪些是根的话,再遍历,然后(root[i]!=0)就好
问题一:什么时候合并,当然是有公共活动的时候。
朴素想法:每个(i)用一个(h[i])记录爱好,(h[i])是一个vector数组。然后遍历1到n,对每个人,遍历1到(i-1)个人,再二重遍历两个人的所有爱好,如果有相同的,则执行合并(merge(i,j)) 同时break出来。四重循环(O(n^4))。
优化思路:用(course[k])记录爱好是活动(k)的第一个人的编号,对于其他同样爱好的人,则执行(merge(i, course[k]))
- 朴素版
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int fa[maxn], root[maxn];
int n,k;
vector<int> h[maxn];
int find(int x){
int r = x;
while(x != fa[x]){
x = fa[x];
}
while(r != fa[r]){
int t = r;
r = fa[r];
fa[t] = x;
}
return x;
}
void merge(int x, int y){
fa[find(x)] = find(y);
}
void init(){
for(int i=1;i<=n;i++){
fa[i] = i;
}
}
bool cmp(int a, int b){
return a > b;
}
int main(){
scanf("%d",&n);
init(); //不要忘记初始化
for(int i=1;i<=n;i++){ //读入数据
scanf("%d:",&k);
for(int j=0;j<k;j++){
int t;
scanf("%d",&t);
h[i].push_back(t);
}
}
bool flag;
for(int i=1;i<=n;i++){ //遍历每个人
for(int j=1;j<i;j++){ //遍历前1到i个人
flag = false;
for(int p=0;p<h[i].size();p++){ //遍历他们的爱好
for(int q=0;q<h[j].size();q++){
if(h[i][p] == h[j][q]){
merge(i,j);
flag = true;
break;
}
}
if(flag) break;
}
}
}
for(int i=1;i<=n;i++){ //统计每个集合的元素个数
root[find(i)]++;
}
sort(root+1, root+n+1, cmp); //注意排序从1开始
int cnt = 0;
for(int i=1;i<=n;i++){
if(root[i]){
cnt++;
}
}
printf("%d
",cnt);
for(int i=1;i<=cnt;i++){ //注意输出也是1到cnt,因为排好序了,不为0的值全跑前面了
if(i == 1) printf("%d",root[i]);
else printf(" %d",root[i]);
}
printf("
");
}
- 优化版
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int fa[maxn], root[maxn], course[maxn];
vector<int> h[maxn];
int n,k;
int find(int x){
int r = x;
while(x != fa[x]){
x = fa[x];
}
while(r != fa[r]){
int t = r;
r = fa[r];
fa[t] = x;
}
return x;
}
void merge(int x, int y){
fa[find(x)] = find(y);
}
void init(){
for(int i=1;i<=n;i++){
fa[i] = i;
}
}
bool cmp(int a, int b){
return a > b;
}
int main(){
scanf("%d",&n);
init();
for(int i=1;i<=n;i++){
scanf("%d:",&k);
for(int j=0;j<k;j++){
int t;
scanf("%d",&t);
if(course[t] == 0) course[t] = i; //关键!
else merge(i, course[t]);
}
}
for(int i=1;i<=n;i++){
root[find(i)]++;
}
sort(root+1, root+n+1, cmp);
int cnt = 0;
for(int i=1;i<=n;i++){
if(root[i]){
cnt++;
}
}
printf("%d
",cnt);
for(int i=1;i<=cnt;i++){
if(i == 1) printf("%d",root[i]);
else printf(" %d",root[i]);
}
printf("
");
}