1. 并查集基础与问题背景
第一次接触并查集是在解决图论中的连通性问题时。当时我需要处理一个社交网络中的好友关系,发现传统的深度优先搜索虽然能解决问题,但在处理动态关系时效率太低。这时一位前辈推荐我学习并查集(Disjoint Set Union,DSU)这个数据结构,从此打开了新世界的大门。
并查集的核心思想其实非常贴近现实生活。想象你在整理一堆杂乱无章的书籍,开始时每本书都是独立的个体(各自为一个集合)。当你发现两本书属于同一类别时,就把它们放在同一个书架上(合并集合)。之后想要查询某本书是否和另一本在同一类别,只需要看它们是否在同一个书架上即可。这种"动态归类"的能力正是并查集的精髓。
在本题P1892中,我们需要处理的是一个典型的团伙识别问题。题目描述了两个关键关系:"朋友"和"敌人"。朋友关系具有传递性(A与B是朋友,B与C是朋友,则A与C也是朋友),而敌人关系则具有对称性(A与B是敌人,则B与A也是敌人)和间接性(敌人的敌人是朋友)。这种复杂的关系网络正是并查集大显身手的场景。
提示:理解题目中的关系传递规则是解题的关键。在实际编码前,建议先用小规模测试案例手工模拟合并过程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 并查集的核心操作实现
2.1 数据结构设计与初始化
标准的并查集通常只需要两个核心数组:parent[]和rank[](或size[])。parent数组记录每个元素的父节点,rank数组则用于优化合并操作。在本题中,我们有n个人,编号从1到n,因此初始化如下:
cpp复制const int MAXN = 1005; // 根据题目n≤1000设定
int parent[MAXN];
int rank[MAXN];
void init(int n) {
for (int i = 1; i <= n; ++i) {
parent[i] = i; // 初始时每个人自成一个集合
rank[i] = 0; // 初始高度为0
}
}
这里有个细节需要注意:题目中人的编号是从1开始的,所以我们的数组也从1开始使用,避免浪费下标0的空间。虽然现代计算机内存充足,但这种细节处理能体现程序员的专业素养。
2.2 查找操作与路径压缩
查找操作的目的是确定某个元素所属的集
