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
原文地址:https://www.cnblogs.com/kimsimple/p/6533923.html