给定N个三维圆心及其半径(x,y,z,r);求连通它们的最小生成树。两个球若相交视为连通。 N<101,坐标非负且小于100.000。
Input
多组输入,以0结束。
Output
最短连通距离,三位小数。
Sample Input
3 10.000 10.000 50.000 10.000 40.000 10.000 50.000 10.000 40.000 40.000 50.000 10.000 2 30.000 30.000 30.000 20.000 40.000 40.000 40.000 20.000 5 5.729 15.143 3.996 25.837 6.013 14.372 4.818 10.671 80.115 63.292 84.477 15.120 64.095 80.924 70.029 14.881 39.472 85.116 71.369 5.553 0
Sample Output
20.000 0.000 73.834
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
int n;
double res;
int a[105];
struct Cell{
double x, y, z, r;
}cell[105];
struct Edges{
int a, b;
double cost;
} edges[10005];
bool cmp(Edges a, Edges b) {
return a.cost < b.cost;
}
int find(int x) {
if(x != a[x])
a[x] = find(a[x]);
return a[x];
}
void kruskal(int m) {
res = 0;
sort(edges + 1, edges + 1 + m, cmp);
memset(a, 0, sizeof(a));
for (int i = 1; i <= n; i ++) a[i] = i;
for (int i = 1; i <= m; i ++) {
int x = edges[i].a, y = edges[i].b;
double z = edges[i].cost;
x = find(x);
y = find(y);
if(x != y) {
a[x] = y;
res += z;
}
}
printf("%.3lf\n", res);
}
int main() {
while(cin >> n) {
if(n == 0)
return 0;
for (int i = 1; i <= n; i ++) {
scanf("%lf%lf%lf%lf", &cell[i].x, &cell[i].y, &cell[i].z, &cell[i].r);
}
int k = 0;
double t;
for (int i = 1; i <= n; i ++) {
for (int j = i + 1; j <= n; j ++) {
t = sqrt(pow((cell[j].x - cell[i].x), 2) + pow((cell[j].y - cell[i].y), 2) + pow((cell[j].z - cell[i].z), 2));
++k;
t -= (cell[j].r + cell[i].r);
if(t < 0) t = 0;
edges[k].a = i;
edges[k].b = j;
edges[k].cost = t;
}
}
kruskal(k);
}
return 0;
}
思路:
先对三维的点之间进行处理,算出点与点之间的距离,然后就转换成了kruskal的模板题。
需要注意的是,100个点互相连起来需要的边的数目就是10000左右了,数组不要开小了。