PAT 甲级 1107 Social Clusters(并查集)(c++版)(python版)(附代码注释)

1107 Social Clusters (30 分)

原文链接:http://kakazai.cn/index.php/Kaka/Pat/query/id/197

题目

题目链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805361586847744

When register on a social network, you are always asked to specify your hobbies in order to find some potential friends with the same hobbies. A social cluster is a set of people who have some of their hobbies in common. You are supposed to find all the clusters.

Input Specification:

Each input file contains one test case. For each test case, the first line contains a positive integer N (≤1000), the total number of people in a social network. Hence the people are numbered from 1 to N. Then N lines follow, each gives the hobby list of a person in the format:

Ki: hi[1] hi[2] … hi[Ki]

where Ki (>0) is the number of hobbies, and hi[j] is the index of the j-th hobby, which is an integer in [1, 1000].

Output Specification:

For each case, print in one line the total number of clusters in the network. Then in the second line, print the numbers of people in the clusters in non-increasing order. The numbers must be separated by exactly one space, and there must be no extra space at the end of the line.

Sample Input:

8
3: 2 7 10
1: 4
2: 5 3
1: 4
1: 3
1: 4
4: 6 8 1 5
1: 4

Sample Output:

3
4 3 1

题意分析

假设人A属于社交圈a,人B属于社交圈B,如果人A和人B具有共同爱好,则它们属于同一个社交圈,要把社交圈A和社交圈B合并。

现在有n个人,要找出有多少个社交圈,并将社交圈的人数降序排列。

把社交圈看成集合,人看成元素,问题变成,如果两个元素之间存在关系(这里指它们具有共同爱好),则应该把它们所在的集合合并。因此可以用并查集。

知识点与坑点

  • 知识点

1)并查集

  • 坑点

1)

一、并查集

算法思路

1 假设每个人一开始独立成为一个社交圈

2 存好每个爱好下的第一个人,合并它与其他该爱好下的人的社交圈

3 做完所有合并后,遍历所有人,并累计它们所在的社交圈的人数

代码-c++版
#include <iostream>
#include<algorithm>
#include<set>
using namespace std;

/*数值范围*/ 
const int maxn = 1001;	//最多有1000个人,1000个爱好 

/*所有变量*/ 
int father[maxn];	//并查集数组
int hobby[maxn];  //存储每个爱好下的第一人
int social[maxn]; //存储社交圈的人数 
 
/* 并查集-初始化*/
void initiate(){
  for(int i=0;i<maxn;i++){
   father[i]=i;
  }
}
/* 并查集-找根结点+压缩路径*/
int findroot(int a){
 int x = a;
 while(a != father[a]) {
  a = father[a];
 }
 int temp; 
 while(x != father[x]){
  temp = father[x];
  father[x] = a;
  x = temp;
 }
 return a;
}
/* 并查集-合并集合+保证根结点编号最小*/ 
 void union_ab(int a,int b){
  int fa = findroot(a);
  int fb = findroot(b);
  if(fa <= fb){ 
   father[fb] = fa;
  }else{
   father[fa] = fb;
  }
 }
 
/* 比较规则 */ 
int cmp(int a, int b) {
    return a > b;
}

int main(){
	
 initiate();
 int n;
 scanf("%d",&n);
 
 /* 存好每个爱好下的第一个人,合并它与其他该爱好下的人的社交圈 */
 int k,h1; 
 for (int i = 1; i <= n; i++) {
        scanf("%d:", &k);
        for (int j = 0; j < k; j++) {
            scanf("%d", &h1);
            if (hobby[h1] == 0) { //i为h1爱好下的第一个人 
                hobby[h1] = i;
            }
            else { //i是h1爱好下的其他人 
                union_ab(hobby[h1],i); //合并第一个人与其他人的社交圈 
            }
        }
    }
 
set<int> root;	//存好所有的根结点 
for (int i = 1; i <= n; i++) {
        int fa = findroot(i);
        root.insert(fa);
        social[fa]++; //该人所在的社交圈人数+1 
    }
    sort(social + 1, social + 1 + n, cmp); //按社交圈人数降序
    
    /* 按要求输出 */ 
    printf("%d\n", root.size()); //社交圈个数
	 
    printf("%d", social[1]);
    for (int i = 2; social[i] != 0 && i <= n; i++)
        printf(" %d", social[i]);
 return 0;
}
代码-python版
#!/usr/bin/python3
#code-python(3.6)

# 并查集-初始化
father = []
for i in range(1001):
    father.append(i)

#并查集-找根结点
def findroot(a):
    while(father[a]!=a):
        a = father[a]
    return a

#并查集-合并集合+保证根结点最小
def union_ab(a,b):
    fa = findroot(a)
    fb = findroot(b)
    if(fa <= fb):
        father[fb] = fa
    else:
        father[fa] = fb

#存好每个爱好下的第一个人,并合并它与其他该爱好下的人的社交圈
n = int(input())
hobby = {}    #存好每个爱好下的第一个人
for i in range(1,n+1):
    line = input().split(" ")   #接受每行,并用空格分开
    line = line[1:] #不要第一个字符串,即爱好人数
    line = list(map(int,line))  #将字符串转为整数
    for h in line:  #遍历该行所有爱好
        if h not in hobby:  #该爱好第一次出现
            hobby[h] = i    #i是该爱好的第一人
        else:   #该爱好不是第一次出现,即该爱好下已经有人
            union_ab(hobby[h],i) #合并第一个人与其他人的社交圈

#找出所有社交圈及其人数,并排序           
root = {} #存好所有的根结点和对应人数
for i in range(1,1+n):
    root_i = findroot(i)
    if root_i not in root:
        root[root_i] = 0
    root[root_i] += 1

x = sorted(root.items(),key = lambda x:(-x[1]))

#按要求输出
print(len(root))
social = [] #存好社交圈的人数
for i in x:
    social.append(str(i[1]))
print(' '.join(social))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值