P1991 无线通讯网
解题思路
问题重述
我们需要连接P个边防哨所,其中有S个可以配备卫星电话(任意距离通信),其余哨所需要通过无线电收发器连接(距离不超过D)。目标是找到最小的D,使得所有哨所都能互相通信。
关键思路
-
最小生成树(MST)变形:本质上是要构建一个生成树,其中可以用S-1条卫星电话连接替代最贵的S-1条边
-
D的确定:在最小生成树中,第(P-S)大的边的长度就是所需的最小D
-
因为最大的S-1条边可以用卫星电话替代
-
剩下的最长边就是无线电需要的最大距离D
-
算法选择
-
Kruskal算法:适合处理边排序和连通性问题
-
修改策略:在构建MST时,只需要选择(P-S)条边而非传统的(P-1)条
实现步骤
-
计算所有边:生成完全图,计算每对哨所间的距离
-
边排序:按距离从小到大排序
-
修改的Kruskal:
-
使用并查集管理连通分量
-
只选择前(P-S)条最短的有效边
-
记录这些边中的最大值作为D
-
-
输出结果:保留两位小数输出D
#include<bits/stdc++.h> using namespace std; const int N = 1e6 + 10; // 边结构体:存储两个哨所编号和它们之间的距离 struct node { int x, y; double z; }; node t[N]; // 存储所有可能的边 int x[N], y[N]; // 存储每个哨所的坐标 int f[N]; // 并查集数组 int s, p, cnt; // s:卫星电话数量, p:哨所数量, cnt:边数 int sum; // 已选边数计数器 double ans; // 最终结果(最小D值) // 计算两点间欧氏距离 double dis(int i, int j) { return sqrt((x[i]-x[j])*(x[i]-x[j]) + (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() { 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++; // 边数增加 ans = max(ans, t[i].z); // 更新当前最大边 if(sum == p - s) break; // 关键点:只需要选p-s条边 } } } int main() { cin >> s >> p; // 初始化并查集和读取坐标 for(int i = 1; i <= p; i++) { cin >> x[i] >> y[i]; f[i] = i; } // 生成所有可能的边 for(int i = 1; i <= p; i++) { for(int j = i + 1; j <= p; j++) { t[++cnt] = {i, j, dis(i,j)}; } } // 按边权升序排序 sort(t + 1, t + 1 + cnt, cmp); // 执行修改后的Kruskal算法 Kruskal(); // 输出结果(保留两位小数) printf("%.2f", ans); return 0; }

浙公网安备 33010602011771号