P2872 [USACO07DEC] Building Roads S 最小生成树
解题思路
问题分析
-
问题描述:给定平面上的n个点,其中部分点之间已有道路连接,需要添加最少的道路使所有点连通,且添加的道路总长度最小。
-
关键点:这是一个典型的最小生成树(MST)问题,需要考虑已有的道路。
算法选择
-
Kruskal算法:适合稀疏图,通过并查集高效管理连通分量,按边权排序后逐步选择最小边。
-
优势:能直接利用已有道路,只需处理未连接的顶点。
实现步骤
-
预处理:
-
计算所有点对间的距离(完全图边)
-
将已有道路加入并查集,减少后续处理量
-
-
Kruskal核心:
-
按边权升序排序
-
依次选择不会形成环的最小边(即连接不同连通分量的边)
-
使用并查集高效判断和合并连通分量
-
-
终止条件:当选中n-1条边时,所有顶点已连通
复杂度分析
-
时间复杂度:
-
计算所有边:O(n²)
-
排序:O(n² log n)(主要瓶颈)
-
Kruskal算法:O(n² α(n))(α为反阿克曼函数)
-
-
空间复杂度:O(n²)存储所有边
优化点
-
精度处理:使用double类型存储距离,确保计算精度
-
提前终止:当已形成生成树时立即退出循环
-
已有道路处理:直接合并相关连通分量,减少后续处理量
注意事项
-
输入规模:n≤1000,完全图边数约50万条,在题目限制内
-
输出格式:保留两位小数,避免四舍五入误差
-
重复道路:题目保证输入合法,无需特殊处理
#include<bits/stdc++.h> using namespace std; const int N = 1e6 + 10, inf = 0x3f3f3f3f; // 定义边的结构体:x和y是顶点,z是边的长度 struct node { int x, y; double z; }; node t[N]; // 存储所有可能的边 int x[N], y[N]; // 存储每个点的坐标 int f[N]; // 并查集数组 int n, m, cnt, sum; // n:点数,m:已有边数,cnt:总边数,sum:已连接边数 // 计算两点间欧几里得距离 double dis(int i, int j) { return (double)sqrt((double)(x[i] - x[j]) * (x[i] - x[j]) * 1.0 + (double)(y[i] - y[j]) * (y[i] - y[j])); } // 边按长度从小到大排序的比较函数 bool cmp(node a, node b) { return a.z < b.z; } // 并查集查找函数(带路径压缩) int find(int x) { if(f[x] != x) f[x] = find(f[x]); return f[x]; } // 并查集合并函数 void merge(int x, int y) { int fx = find(x), fy = find(y); f[fy] = fx; } // Kruskal算法实现 void kruskal() { double ans = 0; // 存储需要修建的道路总长度 // 遍历所有边(已按长度排序) for(int i = 1; i <= cnt; i++) { int u = t[i].x, v = t[i].y; // 如果两个顶点不在同一连通分量 if(find(u) != find(v)) { merge(u, v); // 合并两个连通分量 sum++; // 已连接边数+1 ans += t[i].z; // 累加道路长度 // 如果已经形成生成树(n-1条边),提前退出 if(sum == n - 1) break; } } // 输出结果,保留两位小数 printf("%.2f", ans); } int main() { cin >> n >> m; // 读取每个点的坐标,并初始化并查集 for(int i = 1; i <= n; i++) { cin >> x[i] >> y[i]; f[i] = i; // 每个点初始时自成一个连通分量 } // 生成所有可能的边(完全图) for(int i = 1; i <= n; i++) { for(int j = i + 1; j <= n; j++) { if(i == j) continue; // 跳过自环 t[++cnt] = {i, j, dis(i, j)}; // 存储边信息 } } // 处理已经存在的道路 for(int i = 1; i <= m; i++) { int u, v; cin >> u >> v; if(find(u) != find(v)) { merge(u, v); // 合并连通分量 sum++; // 已连接边数+1 } } // 按边长度排序 sort(t + 1, t + 1 + cnt, cmp); // 执行Kruskal算法 kruskal(); return 0; }

浙公网安备 33010602011771号