题目意思:
给你n个球体,输入n,接下俩输入n行中每行有四个数,分别代表球的球心坐标x,y,z 和球径r 。
可以在两球之间建立通道,现在要将所有球体联通,求最小代价。
本题要点:
1、 两个球体 (x1, y1, z1, r1) 和 (x2, y2, z2, r2) 球的表面之间的距离:
d = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2)) - r1 - r1;
如果d < 0 (此时,球有相交的情况), 另 d = 0
2、 然后就是很裸的最小生成树的问题, 用 prim 算法套模板。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
const int MaxN = 110;
int n;
double a[MaxN][MaxN], d[MaxN], ans;
bool vis[MaxN];
struct Station
{
double x, y, z, r;
}s[MaxN];
void prim()
{
for(int i = 1; i <= n; ++i)
{
d[i] = 1e9;
}
memset(vis, false, sizeof vis);
d[1] = 0;
for(int i = 1; i < n; ++i)
{
int x = 0;
for(int j = 1; j <= n; ++j)
{
if(!vis[j] && (0 == x || d[j] < d[x]))
{
x = j;
}
}
vis[x] = 1;
for(int y = 1; y <= n; ++y)
{
if(!vis[y])
{
d[y] = min(d[y], a[x][y]);
}
}
}
ans = 0;
for(int i = 1; i <= n; ++i)
{
ans += d[i];
}
printf("%.3f\n", ans); // %lf 输出会wa, 改为 %f 输出
}
int main()
{
while(scanf("%d", &n) && n)
{
for(int i = 1; i <= n; ++i)
{
scanf("%lf%lf%lf%lf", &s[i].x, &s[i].y, &s[i].z, &s[i].r);
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
{
a[i][j] = 1e9;
}
double x1, y1, z1, r1, x2, y2, z2, r2;
for(int i = 1; i <= n; ++i)
{
for(int j = i + 1; j <= n; ++j)
{
x1 = s[i].x, y1 = s[i].y, z1 = s[i].z, x2 = s[j].x, y2 = s[j].y, z2 = s[j].z;
r1 = s[i].r, r2 = s[j].r;
a[i][j] = a[j][i] = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2)) - r1 - r2;
if(a[i][j] < 0)
{
a[i][j] = a[j][i] = 0;
}
}
}
prim();
}
return 0;
}
/*
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
*/
/*
20.000
0.000
73.834
*/