PAT (Advanced Level) Practice 1107 Social Clusters

解题方法:并查集
把爱好当成并查集元素。
对于每个人
当爱好只有一个时,不做任何操作。
当爱好大于一个时,两两进行并查集的合并操作。

遍历完后,就可找出,每个cluster的father (father[i]=i)
但是有一个问题:我们为了方便编码,会开辟father[1001] 大小的数组,来记录第 i 个元素的father,初始化时 father[i] = i,
可能爱好 i 并不存在,但是也有father[i]=i。所以我加了个exist数组来记录爱好 i 是否存在。
所以 i 是一个cluster 的根的判别条件为 father[i]==i && exist[i]==true。

还有一个问题,如何知晓每个cluster的人数呢?
我这里用了一个hobby数组,记录第i个人的第一个爱好(每个人至少有一个爱好)
还有一个number数组,记录以 i 为根的cluster 的人数。
接下来遍历hobby数组,对于每个爱好可以找到他的根root,再令number[root]++,即可。

最后排序输出就完事了。

总结:这道题时限是1200ms,但是用并查集3、4ms就可以过,为啥要给这么长时间呢?其实PAT题目做多了,就知道,放宽时间,是为了给暴力解题(或者算法不是很优)的人点分拿。往往最优的代码在很短的时间内就能过,而不那么好的代码,可能会超时1、2个用例。
 

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <vector>
#define maxn 1001
using namespace std;
vector<int> cluster;
int father[maxn],number[maxn]={0},hobby[maxn];
bool exist[maxn]={false};
int findfather(int a){
    int z = a;
    while(a!=father[a]){
        a = father[a];
    }
    while(z!=father[z]){
        int m = z;
        z=father[z];
        father[m]=a;
    }
    return a;
}
void unio(int a,int b){
    int fa=findfather(a);
    int fb = findfather(b);
    if(fa!=fb){
        father[fb]=fa;
    }
}
int main()
{
    for(int i=0;i<maxn;i++){
        father[i]=i;
    }
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        int k,a,b;
        scanf("%d: %d",&k,&a);
        hobby[i]=a;
        exist[a]=true;
        if(k>1){
            for(int i=1;i<k;i++){
                scanf("%d",&b);
                exist[b]=true;
                unio(a,b);
                a=b;
            }
        }
    }
    for(int i=0;i<n;i++){
        int f = findfather(hobby[i]);
        number[f]++;
    }
    for(int i=1;i<maxn;i++){
        if(exist[i]==true&&father[i]==i){
            cluster.push_back(number[i]);
        }
    }
    sort(cluster.begin(),cluster.end());
    cout<<cluster.size()<<endl;
    for(int i=cluster.size()-1;i>=0;i--){
        cout<<cluster[i];
        if(i!=0) cout<<" ";
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值