1. 畅通工程问题解析
这道题目描述了一个典型的图论连通性问题:给定一组城镇和它们之间已有的道路连接,我们需要计算最少还需要修建多少条道路,才能使得所有城镇之间都能互相到达(直接或间接连接)。
1.1 问题本质理解
这个问题可以抽象为一个图论中的连通分量问题。将每个城镇看作图中的一个顶点,每条道路看作图中的一条边。题目要求的是:在现有图的基础上,最少需要添加多少条边,才能使整个图变成一个连通图。
从连通性的角度来看:
- 如果图已经是连通的(只有一个连通分量),则不需要修建任何道路
- 如果有k个连通分量,则需要至少k-1条边将它们连接起来
1.2 并查集的应用场景
并查集(Disjoint Set Union,DSU)是解决这类连通性问题的理想数据结构,因为它提供了以下高效操作:
- Find:查找元素所属的集合(连通分量)
- Union:合并两个集合(连通分量)
并查集特别适合处理动态连通性问题,因为它可以在近乎常数时间内完成合并和查询操作。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 并查集实现详解
2.1 数据结构初始化
cpp复制int father[1005]; // 父节点数组
void InitDisjointSet(int n) {
// 初始化,每个节点的父节点指向自己
for (int i = 0; i < n; i++) {
father[i] = i;
}
}
初始化时,每个城镇都是自己的父节点,表示初始状态下每个城镇自成一个独立的集合。
2.2 查找操作(Find)
cpp复制int Find(int u) {
if (u == father[u]) {
return u; // 找到根节点
}
else {
father[u] = Find(father[u]); // 路径压缩
return father[u];
}
}
查找操作使用了路径压缩优化,使得后续查询更加高效。路径压缩通过在查找过程中将节点直接连接到根节点,可以显著减少后续查询的时间。
2.3 合并操作(Union)
cpp复制int setcount; // 全局变量,记录当前集合数量
void Union(int u, int v) {
int uroot = Find(u);
int vroot = Find(v);
if (uroot != vroot) {
setcount--; // 合并后集合数量减少
}
father[vroot] = uroot; // 将v的根节点指向u的根节点
}
合并操作首先找到两个元素的根节点,如果它们不在同一个集合中,则进行合并,并更新集合计数器。
3. 算法流程解析
3.1 主程序逻辑
cpp复制
