并查集(算法)

news2024/7/4 4:53:25

目录

  • 一、并查集的概念
  • 二、并查集的使用
    • 合并集合
    • 连通块中点的数量
    • 食物链
      • 带权并查集
      • 扩展域并查集


一、并查集的概念

最裸并查集:

  1. 将两个集合合并。

  2. 询问两个元素是否在一个集合当中 ,近乎 O ( 1 ) O(1) O(1) 时间内支持两个操作

基本原理:每个集合用一棵树来表示,树根的编号就是整个集合的编号,每个节点存储它的父节点,p[x]表示x的父节点。

  • 如何判断树根?
if (p[x] == x)
  • 如何求x的集合编号?
// 暴力遍历
while (p[x] != x) x = p[x];

// 路径压缩优化
int find(int x)
{
	while (p[x] != x) p[x] = find(p[x]);
	return p[x];
}

此方法要层层遍历父节点来得到根节点 O ( n ) O(n) O(n) n - 树的高度
优化方法:①路径压缩(常用) ②按秩合并 ③启发式合并

  • 如何合并两个集合?
    合并两个集合
// p[x]是x的集合编号,p[y]是y的集合编号
p[x] = y;

二、并查集的使用

合并集合

题目描述:
一共有 n n n 个数,编号是 1 ∼ n 1∼n 1n,最开始每个数各自在一个集合中。

现在要进行 m m m 个操作,操作共有两种:

  1. M a b,将编号为 ab 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
  2. Q a b,询问编号为 ab 的两个数是否在同一个集合中;

输入格式:
第一行输入整数 n n n m m m

接下来 m m m 行,每行包含一个操作指令,指令为 M a bQ a b 中的一种。

输出格式:
对于每个询问指令 Q a b,都要输出一个结果,如果 ab 在同一集合内,则输出 Yes,否则输出 No

每个结果占一行。

数据范围:
1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105
1 ≤ m ≤ 1 0 5 1≤m≤10^5 1m105

输入样例:

4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4

输出样例:

Yes
No
Yes

代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int p[N];

int find(int x)
{
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}
int main()
{
	cin.tie(0);
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) p[i] = i;

	while (m--)
	{
		int a, b;
		char op;
		cin >> op >> a >> b;
		switch (op)
		{
		case 'M':
			p[find(a)] = find(b);
			break;
		case 'Q':
			if (find(a) == find(b)) cout << "Yes" << endl;
			else cout << "No" << endl;
			break;
		default:
			cout << "Please enter seriously!" << endl;
			break;
		}
	}
	return 0;
}

连通块中点的数量

题目描述:
给定一个包含 n n n 个点(编号为 1 ∼ n 1∼n 1n)的无向图,初始时图中没有边。

现在要进行 m m m 个操作,操作共有三种:

  • C a b:在点 a a a 和点 b b b 之间连一条边, a a a b b b 可能相等;
  • Q1 a b:询问点 a a a 和点 b b b 是否在同一个连通块中, a a a b b b 可能相等;
  • Q2 a:询问点 a a a 所在连通块中点的数量;

输入格式:
第一行输入整数 n n n m m m。接下来 m m m 行,每行包含一个操作指令,指令为 C a bQ1 a bQ2 a 中的一种。

输出格式:
对于每个询问指令 Q1 a b,如果 a a a b b b 在同一个连通块中,则输出 Yes,否则输出 No

对于每个询问指令 Q2 a,输出一个整数表示点 a a a 所在连通块中点的数量。

每个结果占一行。

数据范围:
1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105
1 ≤ m ≤ 1 0 5 1≤m≤10^5 1m105

输入样例:

5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5

输出样例:

Yes
2
3

分析:

并查集 + 附加信息,附加信息为家族中成员的数量,需要一个额外的数组 c n t [ N ] cnt[N] cnt[N]。记录以结点i为根的的家族成员数量。

与最祼并查集的区别:

  • 初始化时,需要将 c n t [ i ] = 1 cnt[i]=1 cnt[i]=1(每次操作为一个点,故初始化为1)

  • 合并时,需要 c n t [ f i n d ( b ) ] + = c n t [ f i n d ( a ) ] cnt[find(b)] += cnt[find(a)] cnt[find(b)]+=cnt[find(a)](利用根节点来计数)

  • 查询时,返回 c n t [ f i n d ( a ) ] cnt[find(a)] cnt[find(a)]


代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int p[N], cnt[N];
int find(int x)
{
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}
int main()
{
	cin.tie(0);
	int m, n;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) p[i] = i, cnt[i] = 1;

	while (m--)
	{
		int a, b;
		string op;
		cin >> op;
		if (op == "C")
		{
			cin >> a >> b;
			cnt[find(b)] += cnt[find(a)];
			p[find(a)] = find(b);
		}
		else if (op == "Q1")
		{
			cin >> a >> b;
			if (find(a) == find(b)) cout << "Yes" << endl;
			else cout << "No" << endl;
		}
		else if (op == "Q2")
		{
			cin >> a;
			cout << cnt[find(a)] << endl;
		}
		else
			cout << "Please enter seriously!" << endl;
	}

	return 0;
}

食物链

题目描述:
动物王国中有三类动物 A , B , C A,B,C A,B,C,这三类动物的食物链构成了有趣的环形。
A A A B B B B B B C C C C C C A A A

现有 n n n 个动物,以 1 ∼ n 1∼n 1n 编号。

每个动物都是 A , B , C A,B,C A,B,C 中的一种,但是我们并不知道它到底是哪一种。

有人用两种说法对这 n n n 个动物所构成的食物链关系进行描述:

第一种说法是 1 X Y,表示 X X X Y Y Y 是同类。

第二种说法是 2 X Y,表示 X X X Y Y Y

此人对 n n n 个动物,用上述两种说法,一句接一句地说出 m m m 句话,这 m m m 句话有的是真的,有的是假的。

当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

当前的话与前面的某些真的话冲突,就是假话;
当前的话中 X X X Y Y Y N N N 大,就是假话;
当前的话表示 X X X X X X,就是假话。

你的任务是根据给定的 n n n m m m 句话,输出假话的总数。

输入格式:
第一行是两个整数 n n n m m m,以一个空格分隔。

以下 K K K 行每行是三个正整数 D , X , Y D,X,Y D,X,Y,两数之间用一个空格隔开,其中 D D D 表示说法的种类。

D = 1 D = 1 D=1,则表示 X X X Y Y Y 是同类。

D = 2 D = 2 D=2,则表示 X X X Y Y Y

输出格式:
只有一个整数,表示假话的数目。

数据范围:
1 ≤ n ≤ 50000 1≤n≤50000 1n50000
0 ≤ m ≤ 100000 0≤m≤100000 0m100000

输入样例:

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

输出样例:

3

带权并查集

思路:

功能:查询祖先+修改父节点为祖先+更新节点到根的距离(通过到父节点的距离累加和)

d [ i ] d[i] d[i] 的含义:第 i i i 个节点到其父节点距离。

演示


代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
const int N = 5e4 + 10;
int p[N], d[N]; // p[]寻找祖宗节点,d[]求到祖宗节点的距离

int find(int x)
{
	if (x != p[x])
	{
		// 路径的长度(高度),是需要自上而下加起来的,从根节点往下走
		// 先触发递归,再从根节点往后增加距离
		int t = find(p[x]); // t暂时存一下p[x]根节点,辅助变量
		d[x] += d[p[x]]; // 更新距离
		p[x] = t;
	}
	return p[x];
}
int main()
{
	cin.tie(0);
	int n, m, res = 0; // res-记录错误数
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) p[i] = i;
	while (m--)
	{
		int t, x, y;
		cin >> t >> x >> y;
		if (x > n || y > n) res++; // 当前话语中x或y比N大,是假话
		else
		{
			int px = find(x), py = find(y); // 查找根节点,每次循环时可以更新节点
			{
				if (t == 1) // 判断是否同类
				{
					// 若 x 与 y 在同一个集合中
					// 两数到根节点距离之差的模不为 0,说明不是同一类,是假话
					if (px == py && (d[x] - d[y] + 3) % 3) res++; // +3来处理负数取模的情况
					else if (px != py) // x 与 y 不在同一个集合中
					{
						p[px] = py;
						d[px] = d[y] - d[x]; // 暂时不更新,下轮循环调用 find 时会更新
					}
				}
				else if (t == 2)
				{
					// 若距离之差 - 1 的模不为 0,说明吃不掉,是假话
					if (px == py && (d[x] - d[y] - 1) % 3) res++;
					else if (px != py)
					{
						p[px] = py;
						d[px] = d[y] - d[x] + 1;
					}
				}
				else cout << "error" << endl;
			}
		}
	}
	cout << res << endl;
	return 0;
}

扩展域并查集

思路:

1 ∼ n 1∼n 1n个元素扩大为 1 ∼ 3 n 1∼3n 13n个元素,使用 [ 1 ∼ 3 n ] [1∼3n] [13n]个并查集(每一个并查集中的所有元素都具有同一种特性,不同并查集中不存在相同元素)来维护3n元素彼此的关系。

为了形象化思考问题,我们假设三种动物,互为食物链: A 、 B 、 C A、B、C ABC关系为:

  • A A A 捕食 B B B
  • B B B 捕食 C C C
  • C C C 捕食 A A A

在这里 x x x元素, x + n x+n x+n元素, x + 2 n x+2n x+2n元素三者的关系被定义为:

  • x x x元素的 p [ x ] p[x] p[x]代表 x x x家族

  • x + n x+n x+n元素的 p [ x + n ] p[x+n] p[x+n]代表 x x x的天敌家族

  • x + 2 n x+2n x+2n元素的 p [ x + 2 n ] p[x+2n] p[x+2n]代表 x x x的猎物家族

对于一句真话:

  • x x x y y y是同类

    • 将他们的天敌集合( x + n x+n x+n y + n y+n y+n所在集合)合并
    • 将猎物集合( x + 2 n x+2n x+2n元素与 y + 2 n y+2n y+2n元素所在集合)合并
    • x , y x,y x,y所在的集合 合并

  • x x x y y y的天敌

    • 将x家族与y的天敌家族合并
    • 将y家族和x的猎物家族合并
    • 将x的天敌家族和y的猎物家族合并

代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

const int N = 5e4 + 10;
int p[N];

int find(int x)
{
	if (x != p[x]) p[x] = find(p[x]);
	return p[x];
}
void join(int x, int y)
{
	int px = find(x), py = find(y);
	if (px != py) p[px] = py;
}
int main()
{
	cin.tie(0);
	int n, m, res = 0;
	cin >> n >> m;
	for (int i = 1; i <= 3 * n; ++i) p[i] = i;
	while (m--)
	{
		int t, x, y;
		cin >> t >> x >> y;
		if (x > n || y > n) res++;
		else
		{
			if (t == 1)
			{
				if (find(x + n) == find(y) || find(x + 2 * n) == find(y)) res++;
				else
				{
					join(x, y);
					join(x + n, y + n);
					join(x + 2 * n, y + 2 * n);
				}
			}
			else if (t == 2)
			{
				if (find(x) == find(y) || find(x + n) == find(y)) res++;
				else
				{
					join(x + 2 * n, y);
					join(x + n, y + 2 * n);
					join(x, y + n);
				}
			}
			else cout << "error" << endl;
		}
	}
	cout << res << endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/575732.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python文件大小:如何优化和管理您的文件大小

Python 文件大小&#xff1a;如何优化和管理您的文件大小 Python 是世界上最流行的编程语言之一&#xff0c;被广泛用于各种不同的应用程序。但是&#xff0c;随着项目变得越来越复杂&#xff0c;并且在需要处理大量数据的情况下&#xff0c;文件大小经常成为一个问题。因此&a…

数据包伪造、替换、劫持,https劫持之探索和测试

&#xff08;一&#xff09;数据包替换攻击 该攻击过程如下&#xff1a;伪造服务器响应客户端的数据包。监听客户端的数据包&#xff0c;用预先伪造的数据包&#xff0c;伪装成服务器返回的数据发送给客户端。 因为攻击者跟目标在同一个局域网&#xff0c;所以攻击者发送的数…

算法27:最长公共子序列——样本模型(4)

目录 简介 题目&#xff1a; 思路&#xff1a; 递归版本&#xff1a; 根据递归 分析推导 动态规划版本&#xff1a; 简介 前面刷了几道题目&#xff0c;都是从暴力递归到递归动态规划的版本&#xff0c;最后演变成纯动态规划的版本。接下来的题目&#xff0c;将会跳过 递…

chatgpt赋能python:Python找出列表中出现最多的元素

Python找出列表中出现最多的元素 介绍 在Python的编程过程中&#xff0c;经常需要处理列表&#xff0c;而处理列表时最常见的问题之一就是如何找出列表中出现最多的元素。在某些情况下&#xff0c;我们可能需要确定列表中重复出现最多的元素&#xff0c;并将其提取出来。Pyth…

[机器学习]线性回归

准备入门一下机器学习算法。 今天学习了线性回归&#xff0c;都是理论的东西&#xff0c;没有对于代码的实现&#xff0c;代码也会跟着进度好好搞一下。 对于线性回归的基础概念&#xff0c;我感觉很依靠概率论和线性代数两门课&#xff0c;作为刚准备完数学一考研的我&#xf…

Systrace系列12 —— CPU Info 解读

本文主要是对 Systrace 中的 CPU 信息区域(Kernel)进行简单介绍,简单介绍了如何在 Systrace 中查看 Kernel 模块输出的 CPU 相关的信息,了解 CPU 频率、调度、锁频、锁核相关的信息。 CPU 区域图例 下面是高通骁龙 845 手机 Systrace 对应的 Kernel 中的 CPU Info 区域(底下…

人工智能轨道交通行业周刊-第46期(2023.5.22-5.28)

本期关键词&#xff1a;数字孪生、AI铁路人、道岔、施封锁、图像质量评价、大模型小型化 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro轨道…

在VIVADO下烧写ZC706板载FLASH的操作步骤

1&#xff0c;原理图分析 首先看原理图&#xff0c;我们兼容ZC706的板子有两片 FLASH&#xff0c;型号是S25FL128A,连接方式如下&#xff1a; 可以看到两片是分别接在了XC7Z045芯片的引脚上&#xff0c;是互不相干的并联方式&#xff0c;每个FLASH芯片支持X4模式&#xff0c;也…

Systrace系列11 —— Triple Buffer 解读

本文主要是对 Systrace 中的 Triple Buffer 进行简单介绍,简单介绍了如何在 Systrace 中判断卡顿情况的发生,进行初步的定位和分析,以及介绍 Triple Buffer 的引入对性能的影响。 怎么定义掉帧? Systrace 中可以看到应用的掉帧情况,我们经常看到说主线程超过 16.6 ms 就会…

第一个Vue程序

什么是MVVM MVVM是Model-View-ViewModel的缩写&#xff0c;是一种软件架构模式&#xff0c;用于将用户界面&#xff08;UI&#xff09;的开发与业务逻辑和数据分离开来。 在MVVM架构中&#xff0c;Model代表数据模型层&#xff0c;View代表用户界面层&#xff0c;ViewModel充…

基于Java+控制台实现教材管理系统

基于Java控制台实现教材管理系统 一、系统介绍二、功能展示1.教材订购2.教材出售3.教材统计4.库存管理 四、其它1.其他系统实现2.获取源码 一、系统介绍 系统主要包括了教材订购、教材出售、教材统计、库存管理几大部分&#xff1b; 其中功能主要包括&#xff1a; 一、教材订购…

English Learning - L3 作业打卡 Lesson3 Day22 2023.5.26 周五

English Learning - L3 作业打卡 Lesson3 Day22 2023.5.26 周五 引言&#x1f349;句1: He would never pour salt on a wound, or make someone feel worse about something that was already a painful experience.成分划分弱读连读爆破语调 &#x1f349;句2: However, some…

字符串最后一个单词的长度

描述 计算字符串最后一个单词的长度&#xff0c;单词以空格隔开&#xff0c;字符串长度小于5000。&#xff08;注&#xff1a;字符串末尾不以空格为结尾&#xff09; 输入描述&#xff1a; 输入一行&#xff0c;代表要计算的字符串&#xff0c;非空&#xff0c;长度小于500…

JavaScript处理移动web交互

touch对象和touchevent touch事件 touch对象 每一次发生touch事件时就会产生一个touch对象&#xff0c;类似事件处理函数中的事件对象。 <div class" "><button class"child" style"height: 400px; width: 400px">我是按钮</b…

OneNote:隐藏OneNote笔记右边的作者和更新时间

OneNote在其他电脑登录后同步笔记&#xff0c;会在笔记右边显示用户名称和更新时间&#xff0c;消除方法如下&#xff1a; 在顶部找到历史记录&#xff0c;点击隐藏作者即可&#xff1a; 隐藏后效果&#xff1a; 说明&#xff1a; 1、用于window10系统。

Mybatis源码的理解

文章目录 0.核心的包1.1 配置文件mybatis-config.xml1.2 配置文件解析将配置文件转化为输入流,将 xml转化Configuration类.解析配置对应的标签为Configuration的属性Configuration的核心类的属性 1.3 解析完成查询之后的configurationenvironment类sqlFragments类mapperRegistr…

(转载)基于量子遗传算法的函数寻优算法

8.1 理论基础 8.1.1 量子遗传算法概述 量子遗传算法(quantum genetic algorithm,QGA)是量子计算与遗传算法相结合的产物&#xff0c;是一种新发展起来的概率进化算法。遗传算法是处理复杂优化问题的一种方法&#xff0c;其基本思想是模拟生物进化的优胜劣汰规则与染色体的交…

结构型设计模式02-代理模式

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 代理模式 1、不使用代理模式 举例说明&#xff1a;小明喜欢一个女生&#xff08;小红&#xff09;&#xff0c;因为小红不认识小明…

【数据结构】初步了解排序

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 目录 1.排序的概念及其运用 1.1排序的概念 2.常见排序算法的实现 2.1插入排序 2.2希尔排序 问题:gap是多少合适&#xff1f; 1.排序的概念及其运用 1.1排序的概念 排序&#xff1a;所…

App上架流程

准备 开发者账号完工的项目 上架步骤 一、创建App ID二、创建证书请求文件 &#xff08;CSR文件&#xff09;三、创建发布证书 &#xff08;CER&#xff09;四、创建Provisioning Profiles配置文件 &#xff08;PP文件&#xff09;五、在App Store创建应用六、打包上架 一、…