PAT A1107 Social Clusters [并查集]

题目描述

链接
有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\n",cnt);
    for(int i=1;i<=cnt;i++){  //注意输出也是1到cnt,因为排好序了,不为0的值全跑前面了
        if(i == 1) printf("%d",root[i]);
        else printf(" %d",root[i]);
    }
    printf("\n");
}

  • 优化版
#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\n",cnt);
    for(int i=1;i<=cnt;i++){
        if(i == 1) printf("%d",root[i]);
        else printf(" %d",root[i]);
    }
    printf("\n");
}

posted @ 2019-07-31 14:54  Doragd  阅读(131)  评论(0编辑  收藏  举报