资讯动态

并查集算法解析与UVa 12793解题实践

发布时间:2026/9/10 21:17:23 来源:尧图企业网站定制
1. UVa 12793题目解析与解题思路UVa 12793 Confederation是一道典型的图论与并查集应用题目主要考察选手对连通分量和集合合并操作的理解。题目背景设定在一个由多个省份组成的联邦国家每个省份由若干城市组成我们需要处理城市之间的连通关系以及省份的合并操作。1.1 题目核心需求题目要求实现以下两种操作查询两个城市是否属于同一个省份连通性检测合并两个省份集合合并这正好对应了并查集(Disjoint Set Union, DSU)数据结构的两个基本操作find和union。并查集的高效实现是解决此类问题的关键。1.2 输入输出规格输入格式通常为第一行测试用例数量T每个测试用例包含N M城市数量N操作数量M接下来M行每行一个操作query a b 查询城市a和b是否同省union a b 合并a和b所在省份输出要求对于每个query操作输出1同省或0不同省不同测试用例间用空行分隔2. 并查集算法深度解析2.1 基础并查集实现并查集的核心是维护一个父节点数组parent[]其中parent[i]表示元素i的父节点。初始时每个元素都是自己的父节点parent[i] i。int parent[MAXN]; void init(int n) { for(int i 0; i n; i) parent[i] i; }find操作通过递归查找根节点同时实现路径压缩优化int find(int x) { if(parent[x] ! x) parent[x] find(parent[x]); // 路径压缩 return parent[x]; }union操作合并两个集合void unionSet(int x, int y) { int rootX find(x); int rootY find(y); if(rootX ! rootY) parent[rootX] rootY; }2.2 优化技巧与性能分析路径压缩使查找操作接近O(1)时间复杂度按秩合并记录每个树的深度总是将小树合并到大树下添加按秩合并后的完整实现int parent[MAXN]; int rank[MAXN]; void init(int n) { for(int i 0; i n; i) { parent[i] i; rank[i] 0; } } int find(int x) { if(parent[x] ! x) parent[x] find(parent[x]); return parent[x]; } void unionSet(int x, int y) { int rootX find(x); int rootY find(y); if(rootX rootY) return; if(rank[rootX] rank[rootY]) parent[rootY] rootX; else { parent[rootX] rootY; if(rank[rootX] rank[rootY]) rank[rootY]; } }3. 题目解决方案实现3.1 完整代码框架#include iostream #include string using namespace std; const int MAXN 100010; int parent[MAXN]; int rank[MAXN]; void init(int n) { for(int i 0; i n; i) { parent[i] i; rank[i] 0; } } int find(int x) { if(parent[x] ! x) parent[x] find(parent[x]); return parent[x]; } void unionSet(int x, int y) { int rootX find(x); int rootY find(y); if(rootX rootY) return; if(rank[rootX] rank[rootY]) parent[rootY] rootX; else { parent[rootX] rootY; if(rank[rootX] rank[rootY]) rank[rootY]; } } int main() { int T; cin T; while(T--) { int N, M; cin N M; init(N); while(M--) { string cmd; int a, b; cin cmd a b; if(cmd query) { cout (find(a) find(b)) endl; } else if(cmd union) { unionSet(a, b); } } if(T) cout endl; // 测试用例间空行 } return 0; }3.2 输入处理优化对于大规模输入建议使用更快的IO方式ios::sync_with_stdio(false); cin.tie(0);3.3 边界条件处理需要注意的特殊情况城市编号是否从0或1开始题目通常说明非法输入的处理虽然题目保证输入合法内存限制MAXN大小设置4. 性能测试与优化验证4.1 时间复杂度分析使用路径压缩和按秩合并的并查集find操作接近O(1)union操作接近O(1)总体复杂度O(M α(N))其中α是反阿克曼函数对于N1e5M1e5的测试用例可以在毫秒级完成。4.2 实际测试数据构造极端测试用例验证链式合并依次union(1,2), union(2,3), ..., union(n-1,n)随机合并大量随机union和query操作混合全查询只进行query操作5. 常见问题与调试技巧5.1 典型错误列表初始化不全忘记初始化parent和rank数组症状随机错误结果解决仔细检查init函数调用路径压缩遗漏find函数未更新parent[x]症状超时解决确保递归赋值parent[x]按秩合并实现错误比较的是节点而非根节点症状结果正确但效率低解决比较rootX和rootY的rank5.2 调试技巧小规模测试用例手工验证打印中间状态void debugPrint(int n) { for(int i 1; i n; i) cout Node i : parent parent[i] , rank rank[i] endl; }使用assert检查不变量assert(find(i) i || rank[i] 0); // 非根节点rank应为06. 算法扩展与应用6.1 带权并查集可以扩展记录每个节点到根节点的距离解决更多问题int parent[MAXN]; int dist[MAXN]; // 到父节点的距离 int find(int x) { if(parent[x] ! x) { int root find(parent[x]); dist[x] dist[parent[x]]; parent[x] root; } return parent[x]; } void unionSet(int x, int y, int d) { int rootX find(x); int rootY find(y); if(rootX rootY) return; parent[rootX] rootY; dist[rootX] dist[y] d - dist[x]; }6.2 实际应用场景社交网络好友关系图像连通区域分析最小生成树Kruskal算法动态连通性问题我在实际编程比赛中发现并查集经常与以下算法结合使用离线处理先读入所有操作再处理二分答案结合并查集验证可行性图论算法判断环、连通性等

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价