种类并查集和带权并查集所产生的森林的区别

种类并查集和带权并查集所产生的森林的区别

并查集可以将元素划分成若干个树,形成森林。
网络上对于种类并查集和带权并查集的划分并不严格,事实上二者对于信息处理后所产生的森林结构不同,而不同的森林结构有时候会对做题产生很大的影响。
本文介绍这一题,并从中分析二种并查集对于数据处理所形成的森林结构的区别。

题目链接: http://poj.org/problem?id=1417
POJ 1417 True Liars

容易分析出,回答yes表明两人属于同一族,回答no表明两人属于不同族,首先利用并查集,将所有的人划分成若干个彼此不相交的集合。

假设经过并查集处理过后,一共得到了m个集合,每个集合的人数为\(W_i\),我们要想唯一的确定所有的神族名单,就必须要从这m个集合中挑选若干个集合作为神族。并且,挑选方法必须有且仅有一种。

因此,每一个集合就面临着挑选,不挑选的抉择,这是一个01背包问题。

可是,这一题该使用哪一种并查集呢?

森林的结构

对于数据

5 4 3
1 2 yes
1 3 no
4 5 yes
5 6 yes
6 7 no

  • 种类并查集
int f[2*MaxN];
// 总人数k

// 如果同族
// union(i,j)
// union(i+n,j+n)

// 如果不同族
// union(i,j+n)
// union(i+n,j)

最终得到的森林结构:

截屏2020-07-20 上午11.07.56
  • 带权并查集
int f[MaxN];
int v[MaxN];
// init
// for i: 0~k
// v[i] = 1 表示0与其父结点为同族,1为异族

int find_f(int a){
  if(f[a] == a){
    return a;
  }else{
    int ori_f = f[a];
    f[a] = find_f(f[a]);
    v[a] = v[ori_f]^v[a] // 异或
  }
}

void union_f(int a,int b,int flag){
  int af = find_f(a);
  int bf = find_f(b);
  f[bf] = af;
  v[bf] = a^b^flag;
}

最终得到的森林结构:

截屏2020-07-20 上午11.38.11

两种森林结构的区别

  • 第一种森林结构
截屏2020-07-20 上午11.07.56

无法确定两颗树之间是否存在敌对关系

如第一棵树和第二棵树之间存在敌对关系,第二棵树和第四棵树之间存在敌对关系。而第一棵树和第三棵树之间不存在敌对关系,第二棵树和第四棵树之间不存在敌对关系。而在本题中,我们需要对森林做01背包,这就需要各个物品之间不存在敌对关系。因此,这种森林结构不能满足要求。

  • 第二种森林结构
截屏2020-07-20 上午11.38.11

这样的森林结构能够保证树与树之间不存在敌对关系,在做01背包的时候可以放心地选择任何树。然而,每一棵树内部却存在着敌对关系,解决很简单:给树内部分类:

截屏2020-07-20 下午5.28.09

我们将每棵树的根节点都标记为1号,和根节点同族的结点都为1号,和根节点异族的结点都为0号。在进行01背包的选择过程中,每一件物品的价值和重量相同,且有两种情况(选择第一类和第二类)。

由于本章的重点在于讲解种类并查集和带权并查集所产生的森林的区别,本章的内容到此为止。如果对POJ 1417感兴趣,可以看相应的题解。

posted @ 2020-07-20 17:35  popozyl  阅读(197)  评论(0编辑  收藏  举报