并查集与最小生成树
并查集可以提供找两部分有没有通路的问题
官方解释:并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题
主要思路:
建立一个数组记录每个数的父亲,初始的时候把每个人的父亲都设为他们自己
之后如果两个数有关系,就把其中一个数的父亲修改为另一个数
最后查询的时候只要递归查询父亲,找到最后的那个父亲,看看是否一样
并查集模板
https://www.luogu.com.cn/problem/P3367
1 #include <iostream> 2 #include<cstring> 3 using namespace std; 4 int pre[1000000], height[1000000]; 5 int find(int x)//递归查询父亲的操作 6 { 7 if (x != pre[x]) pre[x] = find(pre[x]);//如果他的父亲不是他自己,说明递归并没有到尽头 8 return pre[x]; 9 } 10 void uni(int b, int c) 11 { 12 int d = find(b); 13 int v = find(c); 14 if (height[d] == height[v])//如果高度一样,就默认选把右侧并到左侧 15 { 16 height[d] = height[d] + 1; 17 pre[v] = d; 18 } 19 else 20 { 21 if (height[d] < height[v])//谁小就把谁并到大的里面 22 { 23 pre[d] = v; 24 } 25 else pre[v] = d; 26 } 27 } 28 int main() 29 { 30 ios::sync_with_stdio(false); 31 cin.tie(0); 32 cout.tie(0); 33 int n, m; 34 cin >> n >> m; 35 for (int i = 1; i <= n; i++) {//初始化 36 pre[i] = i; 37 height[i] = 0; 38 } 39 for (int i = 0; i < m; i++) 40 { 41 int a, b, c; 42 cin >> a >> b >> c; 43 if (a == 1) 44 { 45 uni(b, c); 46 } 47 if (a == 2) 48 { 49 int d = find(b); 50 int v = find(c); 51 if (d == v)cout << "Y" << endl; 52 else cout << "N" << endl; 53 } 54 } 55 }
-------------------------------------------------------------------------------------------------------------------------
团队题目:洛谷P1551亲戚
https://www.luogu.com.cn/problem/P1551
1 #include <iostream> 2 #include<cstring> 3 using namespace std; 4 int pre[1000000], height[1000000]; 5 int find(int x) 6 { 7 if (x != pre[x]) pre[x] = find(pre[x]); 8 return pre[x]; 9 } 10 void uni(int b, int c) 11 { 12 int d = find(b); 13 int v = find(c); 14 if (d == v) return; 15 if (height[d] == height[v]) 16 { 17 height[d] = height[d] + 1; 18 pre[v] = d; 19 } 20 else 21 { 22 if (height[d] < height[v]) 23 { 24 pre[d] = v; 25 } 26 else pre[v] = d; 27 } 28 } 29 int main() 30 { 31 ios::sync_with_stdio(false); 32 cin.tie(0); 33 cout.tie(0); 34 int n, m, p; 35 cin >> n >> m >> p; 36 for (int i = 1; i <= n; i++) { 37 pre[i] = i; 38 height[i] = 0; 39 } 40 for (int i = 0; i < m; i++) 41 { 42 int a, b; 43 cin >> a >> b; 44 uni(a, b); 45 } 46 for (int i = 0; i < p; i++) 47 { 48 int c, d; 49 cin >> c >> d; 50 int mm = find(c); 51 int nn = find(d); 52 if (mm == nn)cout << "Yes" << endl; 53 else cout << "No" << endl; 54 } 55 return 0; 56 }
-------------------------------------------------------------------------------------------------------------------------
最小生成树
https://www.luogu.com.cn/problem/P3366
一个struct要记录左点,右点,长度(权值)三个信息
输入完根据长度从小到大排列
每次先处理最小的,把左点和右点用并查集合并,表示他们已经有关联
如果左点和右点已经有同一个祖父亲,就说明他们已经相连了,就不需要再处理
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 int pre[300000], height[300000]; 5 struct line 6 { 7 int a, b, v; 8 }; 9 line li[300000]; 10 bool cmp(line x, line y) 11 { 12 return x.v < y.v; 13 } 14 int find(int a) 15 { 16 if (a != pre[a]) pre[a] = find(pre[a]); 17 return pre[a]; 18 } 19 void uni(int a, int b) 20 { 21 int x = find(a); 22 int y = find(b); 23 if (height[x] == height[y]) 24 { 25 height[x]++; 26 pre[y] = x; 27 } 28 if (height[x] < height[y]) 29 { 30 pre[x] = y; 31 } 32 else pre[y] = x; 33 } 34 int main() 35 { 36 ios::sync_with_stdio(false); 37 cin.tie(0); 38 cout.tie(0); 39 int n, m; 40 cin >> n >> m; 41 for (int i = 1; i <= n; i++) 42 { 43 pre[i] = i; 44 height[i] = 0; 45 } 46 for (int i = 0; i <m; i++) 47 { 48 cin >> li[i].a >> li[i].b >> li[i].v; 49 } 50 sort(li, li + m, cmp); 51 int sum = 0; 52 for (int i = 0; i < m; i++) 53 { 54 int x = find(li[i].a); 55 int y = find(li[i].b); 56 if (x != y) { 57 uni(x, y); 58 sum += li[i].v; 59 } 60 } 61 cout << sum << endl; 62 return 0; 63 }
团队教学视频 @AE酱 (感谢大犇讲解)