码迷,mamicode.com
首页 > 其他好文 > 详细

读书笔记 之 数据结构(并查集详解)(POJ1703)

时间:2015-11-04 12:47:45      阅读:287      评论:0      收藏:0      [点我收藏+]

标签:

《ACM/ICPC算法训练教程》读书笔记-这一次补上并查集的部分。将对并查集的思想进行详细阐述,并附上本人AC掉POJ1703的Code。

 


 

  在一些有N个元素的集合应用问题中,通常会将每个元素构成单元素集合,然后按照一定顺序将同属一组的集合合并,期间要反复查找每一个元素在哪个集合中。这类问题往往看似简单,但是数据量很大,因此容易造成TLE或MLE,也就是空间度和时间度极其复杂。因此在这里,我们引入一种抽象的特殊数据结构——并查集。

  并查集:类似一个族谱,每个结点均有一个father[x]来表示x结点的父结点,因此,我们在给并查集初始化的时候,先将结点设为自己的父结点,也就是: father[x] = x; ,依次初始所有结点。

  并查集有两种重要的操作:查找合并

 

查找

  并查集为避免时间上和空间上的损耗,在每一轮查找时,都要进行一次路径压缩优化。什么叫做路径压缩呢,简单的说就是将所有子结点都直接归属其根结点,减小代差,这样父辈和子辈就方便"交流"了。

  具体来说,在查找时,如果得到3的父结点为1,而1的父结点为2,2的父结点又为4,就依次完成: father[3] = father[1] = father[2] = 4;

  这样的优势就在于待到下一次查找时,可以直接进行一次操作完成查询,而不需多次操作“寻根问底”。

  在这里我们利用递归的思想让这段代码实现起来简便易行:

 1 /*查找并压缩路径*/
 2 int Find_set(int x)
 3 {
 4     int temp = fa[x];
 5     if(x != fa[x])
 6         fa[x] = Find_set(fa[x]);    //路径压缩
 7     return fa[x];
 8 }
 9 /*使用*/
10 p[x] = Find_set(x);

 

合并

  也就是合并x和y所在的两个集合,简单来说,只需要把其中一个集合的根节点赋给另一个集合的根节点就可以了。因此此时需要进行一次查询操作并查找到x和y所在集合的两个根节点。

  具体实现如下:

1 void Union(int x,int y)
2 {
3     int fx = Find_set(x);
4     int fy = Find_set(y);
5     if (fx == fy)    //根节点Same
6         return;
7     father[fx] = fy;
8 }

  本书在这里给出的Code包括了启发式合并:也就是将深度小的根节点挂在深度大的根节点上,这样每次查询时进行路径压缩的次数就会得到优化。

  但是我认为初学的时候摊上这个就有点麻烦了,而且这种优化在一定程度来说写起来比较繁琐,因此就没有在这里贴出来了,在某些极端情况下可以加上启发式合并试试。

 

  那么像这样利用路径压缩就可以将并查集的时间复杂度看做O(1),空间复杂度为O(n),这样就将一个大规模问题转化为一个空间小,速度极快的简单操作。


 

 

  POJ1703解题报告:

  题目大意:Tadu City中有两个黑帮团伙,一共n名团伙成员(不知道属于哪个组织),现在警察局有一些信息,每条信息包括两个人的编号:

  输入D x y:代表x于y不在一个团伙里
  输入A x y:询问x与y是否在同一团伙或者不确定他们在同一个团伙里

  

  那么在这种题目中,我们用并查集的思想可以避免大规模地遍历每个成员。那么具体来说如何实现呢。

  如果定义两个帮派集合,那么在大量的D x y中也可能无法确认谁属于哪个帮派,而如果定义每个人一个帮派,那么就可以将并查集的思想利用起来,为了表明成员间的关系,因此我们在这里加入一个relate[x]来表明x结点与其父结点的关系。我们用1来表示这两个成员是不同帮派,而用0来表示这两个成员属于同一个帮派。

  大家可以先打个草稿来尝试如何将大量D x y数据合并(注意relate[]的调整)

 

  那么在这里我贴出自己的Code仅供参考:

  

技术分享
 1 //并查集:D x y 表示x和y分属不同帮派,A x y表示查询x和y的关系
 2 //在并查集的基础上加上relate[],表示t与其父结点fa[t]的关系
 3 //Memory:956K Time:360Ms
 4 #include<iostream>
 5 #include<cstdio>
 6 #include<cstring>
 7 using namespace std;
 8 #define MAX 100005
 9 int fa[MAX], relate[MAX];    //父结点-与父结点的关系
10 int N, M;
11 int find(int x)
12 {
13     int tmp = fa[x];
14     if (fa[x] != x)
15     {
16         fa[x] = find(fa[x]);
17         relate[x] ^= relate[tmp];    //关系转变
18     }
19     return fa[x];
20 }
21 void union_set(int a, int b, int pa, int pb)
22 {
23     fa[pa] = pb;
24     relate[pa] = (relate[a] == relate[b]) ? 1 : 0;    //a,b为不同帮派
25 }
26 int main()
27 {
28     int T, a, b;
29     char flag;
30     scanf("%d", &T);
31     while (T--)
32     {
33         scanf("%d%d", &N, &M);
34         memset(relate, 0, sizeof(relate));    //默认关系为0 -- 同类
35         for (int i = 1; i <= N; i++)
36             fa[i] = i;        //Init
37         while (M--)
38         {
39             scanf("\n%c%d%d", &flag, &a, &b);
40             int pa = find(a), pb = find(b);
41             if (flag == D)
42             {    
43                 if (pa != pb)
44                     union_set(a, b, pa, pb);
45             }
46             else   // flag == ‘A‘
47             {
48                 if (pa != pb)
49                     printf("Not sure yet.\n");
50                 else if (relate[a] == relate[b])
51                     printf("In the same gang.\n");
52                 else printf("In different gangs.\n");
53             }
54         }
55     }
56     return 0;
57 }
小墨- -原创

 

读书笔记 之 数据结构(并查集详解)(POJ1703)

标签:

原文地址:http://www.cnblogs.com/Inkblots/p/4935456.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!