小明正在参加一场爆破工作。人们在地面上放置了 n 个爆炸魔法阵,第 i 个魔法阵的圆心坐标为 (xi , yi),半径为 ri。如果两个魔法阵相交,则它们可以 一起引爆;如果两个魔法阵不相交,则可以再使用一条魔法回路将它们的边缘 连接起来。小明想知道最少需要布置总长度多长的魔法回路才能使得所有的魔 法阵可以一起引爆?
输入格式
输入共 n + 1 行。
第一行为一个正整数 n。
后面 n 行,每行三个整数表示 xi , yi ,ri。
输出格式
输出共 1 行,一个浮点数表示答案(四舍五入保留两位小数)。
样例输入
4
0 0 1
2 0 2
-3 0 1
4 4 1
样例输出
2.47
提示
【样例说明】
使用魔法回路连接第 1、3 个魔法阵,长度为 1。
使用魔法回路连接第 2、4 个魔法阵,长度为 2 √ 5 − 3 = 1.47。
总长度 2.47。
【评测用例规模与约定】
对于 40% 的评测用例,n ≤ 500。
对于 100% 的评测用例,n ≤ 5000,|xi |, |yi | ≤ 2000,0 < ri ≤ 20。
//我居然想不到最小生成树
//这道题是将每一个圆(x,y,r)看作图上一个点的最小生成树的题目,这题更适合用prim算法去写,为啥呢?因为如果用kruskal算法,我们是需要收集图中各个顶点的边集的,这题都没给,需要算出每两个圆之间的边权,这就是一个n方的复杂度,而且一个完全图的边数量是相当多的,后面排序复杂度跟边数有直接关系,基于数据规模,这样写绝对会爆。用prim算法是更优的,因为prim是找点的,这题写个无优化的prim算法就能直接过,以下是代码
#include <bits/stdc++.h>
using namespace std;
struct Circle {
double x, y, r;
};
int main() {
int n;
cin >> n;
vector<Circle> c(n);
for (int i = 0; i < n; i++) {
cin >> c[i].x >> c[i].y >> c[i].r;
}
const double INF = 1e18;
vector<double> dist(n, INF);
vector<bool> vis(n, false);
dist[0] = 0; // 从 0 号点开始
double ans = 0;
for (int i = 0; i < n; i++) {
int u = -1;
double mind = INF;
// 找当前未加入、dist 最小的点
for (int j = 0; j < n; j++) {
if (!vis[j] && dist[j] < mind) {
mind = dist[j];
u = j;
}
}
vis[u] = true;
ans += dist[u];
// 用 u 更新其他点
for (int v = 0; v < n; v++) {
if (!vis[v]) {
double d = hypot(c[u].x - c[v].x,
c[u].y - c[v].y);
double w = max(0.0, d - c[u].r - c[v].r);
dist[v] = min(dist[v], w);
}
}
}
cout << fixed << setprecision(2) << ans << "\n";
return 0;
}