题目意思:
给你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 */