首页 > 其他分享 >PAT (Advanced Level) Practice——Social Clusters

PAT (Advanced Level) Practice——Social Clusters

时间:2022-11-18 11:04:33浏览次数:78  
标签:PAT min int Practice 序号 集合 Level 1010 find


​题目​​​ 这是昨天的博客。(拆东墙补西墙。。)
今天的博客将会在明天凌晨一点打完比赛后发。
昨天在帮我的一位同学看题,所以就顺便谈谈这道题。
这道题的解题思路还是很明显的,赤裸裸的并查集。
这里直接用最经典的并查集就可以处理,无需创新。
也就是直接初始化每个爱好在各自的集合里,每扫描到一个人,就寻找他的所有的爱好的祖先,然后选取标号最小的祖先作为公共祖先,同时更新祖先对应的集合的人数,注意是人数不是爱好数。
有一种创新是有些许的缺陷的,可以修复,但目前还不知道较为好的修复方法,但可以分享一下。也就是以人来表示集合序号,比如第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;
}


标签:PAT,min,int,Practice,序号,集合,Level,1010,find
From: https://blog.51cto.com/u_9368800/5867928

相关文章