• L3-003. 社交集群(并查集)


    L3-003. 社交集群

    时间限制
    1000 ms
    内存限制
    65536 kB
    代码长度限制
    8000 B
    判题程序
    Standard
    作者
    陈越

    在社交网络平台注册时,用户通常会输入自己的兴趣爱好,以便找到和自己兴趣相投的朋友。有部分兴趣相同的人们就形成了“社交集群”。现请你编写程序,找出所有的集群。

    输入格式:

    输入的第一行给出正整数N(<=1000),即社交网络中的用户总数(则用户从1到N编号)。随后N行,每行按下列格式列出每个人的兴趣爱好:

    Ki: hi[1] hi[2] ... hi[Ki]

    其中Ki(>0)是第i个人的兴趣的数量,hi[j]是第i个人的第j项兴趣的编号,编号范围为[1, 1000]内的整数。

    输出格式:

    首先在第一行输出整个网络中集群的数量,然后在第二行按非递增的顺序输出每个集群中用户的数量。数字间以1个空格分隔,行首尾不得有多余空格。

    输入样例:
    8
    3: 2 7 10
    1: 4
    2: 5 3
    1: 4
    1: 3
    1: 4
    4: 6 8 1 5
    1: 4
    
    输出样例:
    3
    4 3 1

    上述例子
    4(第2.4.6.8人)
    3(第2,5,7人)
    1(第1人)
    社交集群人数总和=总人数n
    从3(第2,5,7人)可以看出第二人喜欢5,3 第五人喜欢3 第七人喜欢6 8 1 5,三人能连接起来,就是同一集合

    这符合并查集的特点

    用并查集解决。

    分析:并查集。先写好init、findFather、Union。
    0. 每个社交圈的结点号是人的编号,而不是课程。课程是用来判断是否处在一个社交圈的。
    1. course[t]表示任意一个喜欢t活动的人的编号。如果当前的课程t,之前并没有人喜欢过,那么就course[t] = i,i为它自己的编号,表示i为喜欢course[t]的一个人的编号
    2. course[t]是喜欢t活动的人的编号,那么findFather(course[t])就是喜欢这个活动的人所处的社交圈子的根结点,合并根结点和当前人的编号的结点i。即Union(i, findFather(course[t])),把它们处在同一个社交圈子里面
    3. isRoot[i]表示编号i的人是不是它自己社交圈子的根结点,如果等于0表示不是根结点,如果不等于0,每次标记isRoot[findFather(i)]++,那么isRoot保存的就是如果当前是根结点,那么这个社交圈里面的总人数
    4. isRoot中不为0的编号的个数cnt就是社交圈圈子的个数
    5. 把isRoot从大到小排列,输出前cnt个,就是社交圈人数的从大到小的输出顺序

    我们集合元素是人,curse[i]的值指人,下标指兴趣

    我们以兴趣为前提,对人进行操作
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    vector<int> father, isRoot;
    int cmp1(int a, int b){
        return a > b;
    }
    ///找到x的根节点,路径压缩
    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;
    }
    int main() {
        int n, k, t, cnt = 0;
        int course[1001] = {0};
        scanf("%d", &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++) {
            scanf("%d:", &k);
            for(int j = 0; j < k; j++) {
                scanf("%d", &t);
                if(course[t] == 0)
                    course[t] = i;
                Union(i, findFather(course[t]));///将第i个人与有兴趣t的人所在的社交集群合并
            }
        }
        ///计算集合元素个数
        for(int i = 1; i <= n; i++)
            isRoot[findFather(i)]++;
        ///集合个数
        for(int i = 1; i <= n; i++) {
            if(isRoot[i] != 0)
                cnt++;
        }
        printf("%d
    ", cnt);
        sort(isRoot.begin(), isRoot.end(), cmp1);
        for(int i = 0; i < cnt; i++) {
            printf("%d", isRoot[i]);
            if(i != cnt - 1) printf(" ");
        }
        return 0;
    }
    #include "cstdio"
    #include "vector"
    #include "algorithm"
    using namespace std;
    vector<int>father,isRoot;
    bool cmp(const int a,const int b){
        return a>b;
    }
    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 fa=findFather(a);
        int fb=findFather(b);
        if(fa!=fb)
            father[fa]=fb;
    }
    int main()
    {
        int n,course[1001]={0},k,t;
        while(~scanf("%d",&n)&&n){
            father.clear();
            isRoot.clear();
            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++){
                scanf("%d:",&k);
                for(int j=0;j<k;j++){
                    scanf("%d",&t);
                    if(course[t]==0)
                        course[t]=i;
                    Union(i,findFather(course[t]));
                }
            }
            for(int i=1;i<=n;i++){
                isRoot[findFather(i)]++;
            }
            int cnt=0;
            for(int i=1;i<=n;i++)
            {
                if(isRoot[i]!=0)
                    cnt++;
            }
            printf("%d
    ",cnt);
            sort(isRoot.begin(),isRoot.end(),cmp);
            for(int i=0;i<cnt;i++){
                printf("%d",isRoot[i]);
                if(i!=cnt-1)
                    printf(" ");
            }
            printf("
    ");
        }
        return 0;
    }
    View Code
  • 相关阅读:
    洛谷P2334
    线性基
    6.28 模拟赛解题报告
    左偏树
    哈夫曼树 Huffman
    CSP/NOIP 之前还需要学/复习的东西
    CF718C
    6.13 模拟赛结题报告
    关于模拟退火
    『笔记』网络流
  • 原文地址:https://www.cnblogs.com/kimsimple/p/6533923.html
Copyright © 2020-2023  润新知