一、并查集算法模板
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn = 110;//点数最大值
int parent[maxn];//parent[i]=j——点i的父节点下标为j,初始均为-1(表示根节点)
int rank_v[maxn];//自己根节点树的层数,初始为0
int union_v(int x, int y);
int find(int x);
//判断图是否有环
int main(){
int n, m;
while (cin >> n >> m && (n != 0 || m != 0)){
//输入、初始化
memset(parent, -1, sizeof(parent));
memset(rank_v, 0, sizeof(rank_v));
//判断图是否有环
int edges[6][2] = { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 2, 4 }, { 3, 4 }, { 2, 5 } };
for (int i = 0; i < 6; i++){
int x = edges[i][0], y = edges[i][1];//边上的两个点
if (union_v(x, y) == 0){
cout << "Yes" << endl;
return 0;
}
}
cout << "No" << endl;
}
return 0;
}
//合并两个点
int union_v(int x, int y){
int x_root = find(x);
int y_root = find(y);
//表示同一组的点(或表示有环)
if (x_root == y_root)
return 0;
//不是同一组的,和并
else{
if (rank_v[x_root] > rank_v[y_root])
parent[y_root] = x_root;
else if (rank_v[x_root] < rank_v[y_root])
parent[x_root] = y_root;
else{
parent[x_root] = y_root;
rank_v[y_root]++;
}
}
}
//找x节点的根节点
int find(int x){
int x_root = x;
while (parent[x_root] != -1)
x_root = parent[x_root];
return x_root;
}
二、POJ题
1、模B:The Suspects
http://algorithm.openjudge.cn/2020prefinaltest/B/
- 题目大意:找到点0的同组点数。
- 思路:union_v()——将同组的点合并;find()——找到与0点同组的点。
方法1:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 30010;
int parent[maxn], rank_v[maxn];
int union_v(int x, int y);
int find(int x);
int main(){
int n, m;
while (cin >> n >> m&&(n != 0 || m != 0)){
//输入、初始化
memset(parent, -1, sizeof(parent));
memset(rank_v, 0, sizeof(rank_v));
//1.将同组点放入数组 2.将数组中的同组点依次合并 3.遍历所有点,与0点root相同的为同组的点
while (m--){
int t[maxn], k; cin >> k;
for (int i = 0; i < k; i++)//1
cin >> t[i];
for (int i = 0; i < k - 1; i++)//2
union_v(t[i], t[i + 1]);
}
//3
int ans = 1;
for (int i = 1; i < n; i++)
if (find(0) == find(i))
ans++;
//输出
cout << ans << endl;
}
return 0;
}
int union_v(int x, int y){
int x_root = find(x);
int y_root = find(y);
if (x_root == y_root)
return 0;
else{
if (rank_v[x_root]>rank_v[y_root])
parent[y_root] = x_root;
else if (rank_v[x_root]<rank_v[y_root])
parent[x_root] = y_root;
else{
parent[x_root] = y_root;
rank_v[y_root]++;
}
}
}
int find(int x){
int x_root = x;
while (parent[x_root] != -1)
x_root = parent[x_root];
return x_root;
}
方法2:
- 注:
1.int siz[maxn];//size是节点的集合中元素个数(只有根节点有意义)
2.memset(num, 1, sizeof(num));这样会出错。memset不能将数组元素初始化为1
3.siz处理时是siz[y_root] += siz[x_root];
4.siz数组命名不要用size(容易出错)
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 30010;
int parent[maxn], rank_v[maxn];
int siz[maxn];
int union_v(int x, int y);
int find(int x);
int main(){
int n, m;
while (cin >> n >> m && (n != 0 || m != 0)){
//输入、初始化
memset(parent, -1, sizeof(parent));
memset(rank_v, 0, sizeof(rank_v));
for (int i = 0; i <= n; i++)
siz[i] = 1;
while (m--){
int t[maxn], k; cin >> k;
for (int i = 0; i < k; i++)//1
cin >> t[i];
for (int i = 0; i < k - 1; i++)//2
union_v(t[i], t[i + 1]);
}
//输出
cout << siz[find(0)] << endl;
}
return 0;
}
int union_v(int x, int y){
int x_root = find(x);
int y_root = find(y);
if (x_root == y_root)
return 0;
else{
if (rank_v[x_root]>rank_v[y_root]){
parent[y_root] = x_root;
siz[x_root] += siz[y_root];
}
else if (rank_v[x_root] < rank_v[y_root]){
parent[x_root] = y_root;
siz[y_root] += siz[x_root];
}
else{
parent[x_root] = y_root;
rank_v[y_root]++;
siz[y_root] += siz[x_root];
}
}
}
int find(int x){
int x_root = x;
while (parent[x_root] != -1)
x_root = parent[x_root];
return x_root;
}
2、Percolation
http://algorithm.openjudge.cn/2020hw1/2C/
- 题目大意:判断第一行的点和最后一行的点是否属于同一集合。
- 思路:
每读入一个点(代号为n*(x-1)+y),若为第一行的点,与点0合并;
若为第n行的点,与点(n*n+1)合并;
再与上下左右已打开的点合并。
最后判断点0和点(n*n+1)是否属于同一集合。 - 注:若点0和点(n*n+1)已经属于同一集合,后续的点就不需要再合并了。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1000010;
int parent[maxn], rank_v[maxn];
bool opened[maxn];
int union_v(int x, int y);
int find(int x);
int main(){
int t; cin >> t;
while (t--){
memset(parent, -1, sizeof(parent));
memset(rank_v, 0, sizeof(rank_v));
memset(opened, false, sizeof(opened));
int n, m; cin >> n >> m;
bool flag = false;
for (int i = 1; i <= m; i++){
int x, y; cin >> x >> y;
//合并
if (!flag){
int loc = (x - 1)*n + y;
opened[loc] = true;
if (x == 1){
union_v(loc, 0);
opened[0] = true;
}
if (x == n){
union_v(loc, n*n + 1);
opened[n*n + 1] = true;
}
if (x>1 && opened[loc - n])//上
union_v(loc, loc - n);
if (x<n&&opened[loc + n])//下
union_v(loc, loc + n);
if (y>1 && opened[loc - 1])//左
union_v(loc, loc - 1);
if (y<n && opened[loc + 1])//右
union_v(loc, loc + 1);
//判断
if (find(0) == find(n*n + 1)){
cout << i << endl;
flag = true;
}
}
}
if (!flag)
cout << -1 << endl;
}
return 0;
}
int union_v(int x, int y){
int x_root = find(x);
int y_root = find(y);
if (x_root == y_root)
return 0;
else{
if (rank_v[x_root]>rank_v[y_root]){
parent[y_root] = x_root;
}
else if (rank_v[x_root] < rank_v[y_root]){
parent[x_root] = y_root;
}
else{
parent[x_root] = y_root;
rank_v[y_root]++;
}
}
}
int find(int x){
int x_root = x;
while (parent[x_root] != -1)
x_root = parent[x_root];
return x_root;
}