PAT (Advanced Level) Practice——Social Clusters

题目
这是昨天的博客。(拆东墙补西墙。。)
今天的博客将会在明天凌晨一点打完比赛后发。
昨天在帮我的一位同学看题,所以就顺便谈谈这道题。
这道题的解题思路还是很明显的,赤裸裸的并查集。
这里直接用最经典的并查集就可以处理,无需创新。
也就是直接初始化每个爱好在各自的集合里,每扫描到一个人,就寻找他的所有的爱好的祖先,然后选取标号最小的祖先作为公共祖先,同时更新祖先对应的集合的人数,注意是人数不是爱好数。
有一种创新是有些许的缺陷的,可以修复,但目前还不知道较为好的修复方法,但可以分享一下。也就是以人来表示集合序号,比如第i的人就对应初始集合i。但这是有缺陷的,缺陷在于,它将集合的序号和元素(爱好)序号之间的关系打破了,也就是说,我们要想通过当前元素的集合序号找到其他处于这个集合里的元素,则需要遍历所有元素。而一般的并查集,则是关联集合序号和元素序号,即在每个集合中取一代表元,其标号就是该集合的标号。这样的话,我们只需要通过转移代表元的关系就可以直接转移集合关系。这也是并查集的巧妙之处。
代码如下:

#include<stdio.h>
int r[1010];
int h[1010];
int k[1010];
int m[1010];
int p[1010];
int find(int a){
	if(r[a]==a){
		return a;
	}
	int p=find(r[a]);
	k[p]+=k[a];
	k[a]=0;
	r[a]=p;
	return p;
}
int n,K,a,t,min;

int main(){
	for(int i=0;i<1010;i++){
		r[i] = i;
	}
	
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d:",&K);
		for(int j=0;j<K;j++){
			scanf("%d",&a);
			m[j] = a;
		}
		p[0]=min=find(m[0]);
		for(int j=1;j<K;j++){
			p[j]=find(m[j]);
			if(p[j]<min)min=p[j];
		}
		for(int j=0;j<K;j++){
			r[p[j]]=min;
			if(p[j]!=min){
				k[min]+=k[p[j]];
				k[p[j]]=0;
			}
		}
		k[min]++;

	}
	for(int i=0;i<1010;i++){
		find(i);
		if(r[i]!=i){
			k[r[i]]+=k[i];
			k[i]=0;
		}
	}
	for(int i=0;i<1010;i++){
		if(k[i]){
			int j;
			for(j=0;j<t;j++)
				if(k[i] > k[h[j]])break;
			t++;
			for(int p=t-1;p>j;p--)
				h[p] = h[p-1];
			h[j] = i;
			
		}
	}
	printf("%d\n",t);
	for(int i=0;i<t;i++)
		if(!i)printf("%d",k[h[i]]);
		else printf(" %d",k[h[i]]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值