【做练习】宗教信仰(并查集)

这篇博客探讨了一个基于并查集算法的问题,即如何通过同学之间的共同宗教信仰信息来估算学校中不同宗教的上限数量。作者首先介绍了问题背景和输入输出格式,然后详细解释了并查集的概念,包括其作为等价关系表示的优势以及Find和Merge操作。接着,作者提供了一个简单的并查集模板,并指出在某些变种问题中,可能需要维护额外信息。最后,推荐了一个练习题以加深对并查集的理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

描述

世界上有许多宗教,你感兴趣的是你学校里的同学信仰多少种宗教。
你的学校有n名学生(0 < n <= 50000),你不太可能询问每个人的宗教信仰,因为他们不太愿意透露。但是当你同时找到2名学生,他们却愿意告诉你他们是否信仰同一宗教,你可以通过很多这样的询问估算学校里的宗教数目的上限。你可以认为每名学生只会信仰最多一种宗教。

输入

输入包括多组数据。
每组数据的第一行包括n和m,0 <= m <= n(n-1)/2,其后m行每行包括两个数字i和j,表示学生i和学生j信仰同一宗教,学生被标号为1至n。输入以一行 n = m = 0 作为结束。

输出

对于每组数据,先输出它的编号(从1开始),接着输出学生信仰的不同宗教的数目上限。

样例输入

10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0

样例输出

Case 1: 1
Case 2: 7


分析

“信仰同一宗教”这一关系是一个等价关系(自反、传递、对称),因此,很容易想到,最大的信仰数量是这一等价关系所导出的等价类的数量。
看到这里,立刻明白这道题是非常原始的并查集问题。

并查集

并查集是一种高效的记录、查询等价类的方式,它把属于同一个等价类的集合通过前驱记录的形式构成一棵树,并用根节点来代表整个等价类。

并查集支持Find(查询)、Merge(合并)两种基本的操作。

  • Find(i)通过迭代前驱寻找节点 i 的祖宗,也就是它所在树的根——其所在等价类的代表元。通常,我们用路径压缩来提高性能。
  • Merge(i, j)将 i 节点和 j 节点各自所在的等价类合并成同一等价类。它非常简单:直接将把Find(j)的前驱设置为Find(i)即可。

在并查集中,路径压缩是一项非常巧妙的设计,我们在Find的同时,将迭代(或递归)查询路径上的节点都改为直接挂在根之下,这样,不仅不影响的查询的正确性,又缩减树的深度,使得后续的Find操作的代价趋近于一个常数。

我会在代码实现中,实现一个基本的并查集模板。

代码实现

#include <stdio.h>
#pragma warning(disable: 4996) //make Visual Studio happy


class MergeFindSet
{
   
private:
	int* parents;

public:
	const int size;

public:
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值