1 条题解
-
0
#include<bits/stdc++.h> using namespace std; // [1] 全局常量:最大城镇数量限制,适配题目n<1000的范围 const int MAXN = 1010; // [2] 全局数组:并查集父节点数组,存储每个城镇的父节点编号 int fa[MAXN]; // [3] 全局数组:并查集深度数组,用于按秩合并优化,避免树结构退化 int dep[MAXN]; // [16] 跳转至find函数:带路径压缩,查找节点x所在连通分量的根节点 int find(int x) { // [17] 函数参数x:待查找根节点的城镇编号 if(x != fa[x]) { fa[x] = find(fa[x]); // [18] 路径压缩:直接将当前节点指向根节点,大幅优化后续查询效率 } return fa[x]; // [19] 返回找到的根节点,用于判断两个节点是否属于同一连通分量 } // [20] find函数注释完成,返回unite函数继续注释 // [13] 跳转至unite函数:按秩合并x和y所在的两个城镇连通集合 void unite(int x, int y) { // [14] 函数参数x、y:待合并的两个城镇编号 // [15] 调用find函数查找两个城镇的根节点,跳转至该函数完成注释 x = find(x); y = find(y); if(x == y) return; // [21] 回到unite函数,两城镇已属于同一连通分量,无需重复合并 // [22] 按秩合并逻辑:将深度更小的树合并到深度更大的树下,保持树结构平衡,优化查询效率 if(dep[x] > dep[y]) { fa[y] = x; } else if(dep[x] < dep[y]) { fa[x] = y; } else { fa[y] = x; dep[x]++; // 两棵树深度相同时,合并后根节点的深度+1 } } // [23] unite函数注释完成,返回主函数继续注释 // [4] 主函数:程序运行入口 int main() { int n, m; // [5] 循环处理多组测试数据,当n=0时结束输入 while(cin >> n && n != 0) { cin >> m; // [6] 读入当前组的道路总数m // [7] 循环初始化并查集,为每个城镇初始化连通状态 for(int i = 1; i <= n; i++) { fa[i] = i; // [8] 每个城镇初始独立为一个连通分量,父节点设为自身 dep[i] = 1; // [9] 每个连通分量的初始树深度设为1 } // [10] 循环处理所有m条现有道路,合并对应城镇的连通集合 for(int i = 1; i <= m; i++) { int a, b; cin >> a >> b; // [11] 读入当前道路连接的两个城镇编号 // [12] 调用unite函数合并两个城镇所在的连通集合,跳转至该函数完成注释 unite(a, b); } // [23] 回到主函数,所有道路处理完成 int blockCnt = 0; // [24] 统计变量:记录当前的连通分量总数 // [25] 循环统计所有城镇的连通分量数量 for(int i = 1; i <= n; i++) { // [26] 调用已注释的find函数,判断当前节点是否为连通分量的根节点 if(find(i) == i) { blockCnt++; // [27] 根节点代表一个独立的连通分量,计数+1 } } // [28] k个连通分量需要k-1条道路即可全部连通,输出最少需要新建的道路数 cout << blockCnt - 1 << endl; } return 0; }
- 1
信息
- ID
- 1365
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者