【POJ】并查集算法题目

一、并查集算法模板

#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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值