1107. Social Clusters (30)

距离PAT考试还有17天,最重要的是做透每一题

(1)

思路:

基本就是利用并查集

以人的标号为集合元素然后通过爱好来合并各个集合

这里有个技巧是用一个数组hobby来做映射,hobby数组全部初始为0,然后一旦有一个人有这个hobby就将这个人的标号存进去

下次一旦再有人是这个hobby就和hobby中存的那个人的集合合并从而达到相应的效果

#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;

#define M 1010
int p[M];
int hobby[M];
int num[M];
int cnt;

bool cmp(int a,int b) {return a>b;}
void makeset(int x) {
  p[x]=x;
  num[x]=1;
}

int findset(int x) {
  int px=x,i;
  while(px != p[px]) px=p[px]; // find root
  while(x != px) { //path compression
    i=p[x];
    p[x]=px;
    x=i;
  }
  return px;
}

void unionset(int x,int y) {
  x=findset(x);
  y=findset(y);
  p[x]=y;
  cnt--;
}

int main() {
  int n;
  scanf("%d",&n);
  cnt=n;
  memset(hobby,0,sizeof(hobby));
  memset(p,0,sizeof(p));
  memset(num,0,sizeof(num));
  for(int i=1;i<=n;i++) {
    int k;
    scanf("%d:",&k);
    makeset(i);
    for(int j=0;j<k;j++) {
      int h;
      scanf("%d",&h);
      if(hobby[h] != 0) unionset(hobby[h],i);
      hobby[h]=i;
    }
  }
  printf("%d
",cnt);

  for(int i=1;i<=n;i++){
    num[findset(i)]++;
  }
  sort(num,num+M,cmp);
//这里这样做是因为根节点的值一定是最大的其他节点最多也就是1
//排序后,大的cnt个节点一定在前面    
  for(int i=0;i<cnt;i++) {//减一是因为父节点自己也会把自己再算一次
    i==0? printf("%d",num[i]-1):printf(" %d",num[i]-1);
  }
  return 0;
}

这里研究一下并查集

它维护一些不想交的集合,它是集合的集合

接口是

void makeset(int i);

int findset(int x);

void unionset(int x,int y);

具体如下:

void makeset(int x) {
  p[x]=x;
  rank[x]=0;
}

这里rank是为了记录深度以提高效率而做的

int findset(int x) {
  int px=x;
  while(px!=p[px]) px=p[px];//find root
  while(x!=px) { //root compression
    i=p[x];
    p[x]=px;
    x=i;
  }
  return px;
}

为了效率进行路径压缩

void unionset(int x,int y) {
  x=findset(x);
  y=findset(y);
  if(rank[x] > rank[y]) {
    p[x]=y;
  }else {
    p[y]=x;
    if(rank[x]==rank[y]) rank[x]++;
  }
}

将较浅的合并到较深的以提高效率

原文地址:https://www.cnblogs.com/tclan126/p/8488125.html