并查集(模板&典型例题整理)
2017-04-13 21:15
330 查看
并查集,并查集是一种树形结构,又叫“不相交集合”,保持了一组不相交的动态集合,每个集合通过一个代表来识别,代表即集合中的某个成员,通常选择根做这个代表。
也就是说,并查集是用来处理不相交集合类型问题,如问不相交集合有几个。给定节点,找到该节点所在集合元素个数,当然这只是水题。并查集会与其他算法结合着考,如LCA中的tarjian算法。后续博客会整理。
并查集,顾名思义,主要分三部分。
一:合并:给出两点关系,如果属于同一集合,进行merge
二:查:在合并时,需要先写出查,即找到该点的祖先点
三:集:merge后,将新加入的点的祖先点更新
然后,点集就因为共同的祖先点被分为不同的集合啦
结合例题更容易理解
裸题模板:
hdu1232畅通工程
畅通工程
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 52315 Accepted Submission(s): 27902
Problem Description
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
Input
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最少还需要建设的道路数目。
Sample Input
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0
Sample Output
1
0
2
998
就是说将所有独立的集合连接起来还需要几条路,那只要找到独立集合个数-1就可以啦
再看一个引申题,poj1611
The Suspects
Time Limit: 1000MS Memory Limit: 20000K
Total Submissions: 37109 Accepted: 17992
Description
Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. To minimize transmission to others, the best strategy is to separate the suspects from others.
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP).
Once a member in a group is a suspect, all members in the group are suspects.
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.
Input
The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number of groups. You may assume that 0 < n <= 30000 and 0 <= m <= 500. Every student is numbered by a unique integer between 0 and n−1, and initially student 0 is recognized as a suspect in all the cases. This line is followed by m member lists of the groups, one line per group. Each line begins with an integer k by itself representing the number of members in the group. Following the number of members, there are k integers representing the students in this group. All the integers in a line are separated by at least one space.
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.
Output
For each case, output the number of suspects in one line.
Sample Input
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0
Sample Output
4
1
1
题意是,一些学生被分组,0号可能感染病毒,跟他同一集合的也可能感染,那么给出几个分组,问感染的人数。所以,0作为祖先节点,只要与0同集合就将人数数组增加。
在find函数采用了状态压缩,将每个点的父节点都更新为合并的祖先节点,这样查询速度将更快。
也就是说,并查集是用来处理不相交集合类型问题,如问不相交集合有几个。给定节点,找到该节点所在集合元素个数,当然这只是水题。并查集会与其他算法结合着考,如LCA中的tarjian算法。后续博客会整理。
并查集,顾名思义,主要分三部分。
一:合并:给出两点关系,如果属于同一集合,进行merge
二:查:在合并时,需要先写出查,即找到该点的祖先点
三:集:merge后,将新加入的点的祖先点更新
然后,点集就因为共同的祖先点被分为不同的集合啦
结合例题更容易理解
裸题模板:
hdu1232畅通工程
畅通工程
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 52315 Accepted Submission(s): 27902
Problem Description
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
Input
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最少还需要建设的道路数目。
Sample Input
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0
Sample Output
1
0
2
998
就是说将所有独立的集合连接起来还需要几条路,那只要找到独立集合个数-1就可以啦
#include<stdio.h> int father[1005]; int Find(int x) { while(x!=father[x]) x=father[x]; return x; } void Combine(int a,int b) { int fa=Find(a); int fb=Find(b); if(fa!=fb) { father[fa]=fb; } } int main() { int n,m; int i; int a,b; while(~scanf("%d",&n)) { if(n==0) break; scanf("%d",&m); int sum=0; for(i=1;i<=n;i++) father[i]=i; for(i=0;i<m;i++) { scanf("%d%d",&a,&b); Combine(a,b); } for(i=1;i<=n;i++) { if(father[i]==i) sum++; } printf("%d\n",sum-1); } return 0; }
再看一个引申题,poj1611
The Suspects
Time Limit: 1000MS Memory Limit: 20000K
Total Submissions: 37109 Accepted: 17992
Description
Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. To minimize transmission to others, the best strategy is to separate the suspects from others.
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP).
Once a member in a group is a suspect, all members in the group are suspects.
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.
Input
The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number of groups. You may assume that 0 < n <= 30000 and 0 <= m <= 500. Every student is numbered by a unique integer between 0 and n−1, and initially student 0 is recognized as a suspect in all the cases. This line is followed by m member lists of the groups, one line per group. Each line begins with an integer k by itself representing the number of members in the group. Following the number of members, there are k integers representing the students in this group. All the integers in a line are separated by at least one space.
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.
Output
For each case, output the number of suspects in one line.
Sample Input
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0
Sample Output
4
1
1
题意是,一些学生被分组,0号可能感染病毒,跟他同一集合的也可能感染,那么给出几个分组,问感染的人数。所以,0作为祖先节点,只要与0同集合就将人数数组增加。
#include<stdio.h> #define MAX 30005 int a[MAX],pre[MAX]; int find(int x) { if(x!=pre[x]) //找到其祖先节点 pre[x] = find(pre[x]); //由父节点继续向上递归查询 ,并将其父节点变成找到的 return pre[x]; } void merge(int x,int y) { //分别查询两点的祖先节点。 int prex = find(x); int prey = find(y); //如果二者的祖先节点不一致,那么任意让二者中某一个认另一个为祖先,保证同集合。 if(prex == prey) { return ; } //应该是祖先节点进行组合。而不是当前节点! pre[prey] = prex; a[prex] += a[prey]; } int main() { int n,m; int k,x,y; while(~scanf("%d%d",&n,&m)) { if(n==0&&m==0) { return 0; } for(int i=0;i<n;i++) { //先将自身作为祖先节点。 pre[i] = i; a[i] = 1; } for(int i=0;i<m;i++) { //给出集合每个集合人数,以及第一个人的编号 scanf("%d%d",&k,&x); k--; while(k--) { scanf("%d",&y); merge(x,y); } } printf("%d\n",a[find(0)]); } return 0; }
在find函数采用了状态压缩,将每个点的父节点都更新为合并的祖先节点,这样查询速度将更快。
int find(int x) { if(x!=pre[x]) //找到其祖先节点,并将其父节点变成找到的祖先节点 pre[x] = find(pre[x]); //由父节点继续向上递归查询 return pre[x]; }
相关文章推荐
- 并查集讲解(按秩合并与路径压缩),模板与典型例题
- POJ 1703 && poj 1182【典型并查集】
- 并查集详细讲解(转载) && 模板
- kruskal模板及例题(并查集)
- 字典树(Tire树)模板 & 例题
- python典型应用场景、domo及模板之一-----------配置&日志
- 欧拉函数模板及例题整理
- 整理的树状数组模板 & 敌兵布阵 HDU - 1166
- 最小生成树Kruskal算法【模板】 与 并查集 例题:简单 poj 2485 Highways
- kruskal_并查集_代码模板 &hdu1232
- 【例题】【并查集(带权)&题干有诈】NKOJ 3764 树上间距
- 并查集模板 && 带权并查集模板
- 并查集总结【模板】 例题:①简单POJ - 1611 The Suspects ②一般HDU - 1272 小希的迷宫
- 我整理的Ruby on Rails教程 和Ruby&Rails 入门大全,对新手很有用
- 读>整理-第一章对安全系统的需求
- [整理] 常用正则表达式收集&勘误
- Tips&Tricks系列二:找不到Asp.net模板?
- Pku acm 2492 A Bug's Life数据结构题目解题报告(十)---- 并查集的应用
- JDBC | 模板模式&策略模式
- 整理的C++ primer TextQuery 例题