并查集(入门+拓展~)
貌似暑期的集训开始停下来了,噗~终于有机会做一下笔记了的说。。之前几乎每天都要吸收许多新的知识,包括各种各样的数据结构和各种各样的算法,还没开始消化完原理就要强迫自己开始刷题,那个才叫做真心的蛋疼。。。=。=最不爽的还是要被那些集训队师兄臭骂水题都做不完,,,不过终于是挨过来了,感觉自己确实神清气爽了不少额(╯‵□′)╯︵┻━┻。
先从并查集入门开始吧额。。。。。非常经典的问题
Q1.HDU 1232
Description:某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
Input:测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
1 #include<iostream> 2 #include<queue> 3 #include<cstdio> 4 #include<string.h> 5 using namespace std; 6 const int N =1010; 7 int father[N]; 8 int n,m,i,j; 9 10 void init(){ 11 for(int i = 1;i <= n;i++) father[i] = i; 12 } 13 int Find(int x){ 14 while(father[x] != x) 15 x = father[x]; 16 return x; 17 } 18 void Merge(int a,int b){ 19 int A = Find(a); 20 int B = Find(b); 21 if(A != B) father[A] = B; 22 } 23 int main(){ 24 //freopen("in.txt","r",stdin); 25 while(~scanf("%d",&n)&&n) { 26 int tmp = 0; 27 init(); 28 scanf("%d",&m); 29 while(m--){ 30 scanf("%d%d",&i,&j); 31 Merge(i,j); 32 } 33 for(int i = 1;i <= n;i++) 34 if(father[i] == i) ++tmp; 35 printf("%d\n",tmp - 1); 36 } 37 return 0; 38 }
新手切记得要为father数组初始化!!!!
我们可以在每次查询的时候可以对树上的路径进行一个压缩,这样就可以减少以后查询路径的长度
思想:每次查找的时候,如果路径较长,则修改信息,以便下次查找的时候速度更快 步骤: 第一步,找到根结点 第二步,修改查找路径上的所有节点,将它们都指向根结点
路径压缩的示意图:
路径压缩示意代码:
1 int Find(int x){ 2 int k = x; 3 int j; 4 while(father[k] != k) 5 k = father[k]; 6 int i = x; 7 while(i!= k){ 8 j = father[i]; 9 father[i] = k; 10 i = j; 11 } 12 return k; 13 }
不过酱紫的压缩对于这道题似乎没什么太大的优化作用~,或许是数据范围么那么大的缘故吧,两份代码的运行时间与内存都是一样的。。
并且我发觉得大部分的题目对于路径压缩貌似都没什么很高的要求,不压缩照样能通过。。。
Q2. 难度增加了那么一点点的并查集~HDU 1856
题目大意就是要你找出最大的那个树,即节点数目最多的那棵树~~,与第一题不同的是,需要开一个weight数组来记录每个节点到起始端的距离(节点数),树与树的归并的时候要将小树归并到大树那里去,以此保持树的平衡~
AC代码:
1 #include<iostream> 2 #include<queue> 3 #include<cstdio> 4 #include<string.h> 5 #include<vector> 6 using namespace std; 7 const int MAX_N = 10000010; 8 bool vis[MAX_N]; 9 int father[MAX_N]; 10 int weight[MAX_N]; 11 int Find(int i) { 12 if(i != father[i]) father[i] = Find(father[i]); 13 return father[i]; 14 } 15 void init(int i) { 16 father[i] = i; 17 weight[i] = 1; 18 } 19 void Merge(int a,int b) { 20 int A = Find(a); 21 int B = Find(b); 22 if(A != B) 23 if(weight[A] >= weight[B]){ 24 father[B] = A; 25 weight[A] += weight[B]; 26 }else{ 27 father[A] = B; 28 weight[B] += weight[A]; 29 } 30 } 31 int main() { 32 //freopen("in.txt","r",stdin); 33 int n; 34 while(~scanf("%d",&n)) { 35 memset(vis,0,sizeof(vis)); 36 int MAX = 0; 37 if(!n) {puts("1");continue;} 38 while(n--){ 39 int i,j; 40 scanf("%d%d",&i,&j); 41 if(!vis[i]){vis[i] = 1;init(i);} 42 if(!vis[j]){vis[j] = 1;init(j);} 43 Merge(i,j); 44 MAX = max(weight[father[i]],weight[father[j]]); 45 } 46 printf("%d\n",MAX); 47 } 48 return 0; 49 }
建议新手好好理解该题的Merge函数的每一个细节~~ps:这道题的输出格式部分有点坑,请注意!
(未完 不定时更新)
浙公网安备 33010602011771号