题意:
就是给出三维坐标系上的一些球的球心坐标和其半径,搭建通路,使得他们能够相互连通。如果两个球有重叠的部分则算为已连通,无需再搭桥。求搭建通路的最小边长总和是多少。
思路:
先处理空间点之间的距离,要注意的是两个球面相交的情况,相交的话距离是0。两球面距离是球心距离减去两个球的半径(边权 = AB球面距离 = A球心到B球心的距离 – A球半径 – B球半径),相交时距离是算出来是负值,要改为0。其实就是求连接所有球最小生成树的过程。
代码:
kruskal:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std; int n;
double need; struct Station //定义球的结构体
{
double x,y,z;
double r;
};
Station station[]; struct Vdge //构造生成树的点
{
int x,y;
double l;
}; bool cmp(Vdge a,Vdge b){
return a.l < b.l ;
} Vdge edge[];
int road[]; int find(int x)
{
while( x != road[x] )
x = road[x];
return x;
} bool judge(int x,int y){
int fx = find(x);
int fy = find(y);
if( fx==fy )
return false;
else
{
road[fx] = fy;
return true;
}
} double dis(int i,int j)
{
double distance;
double a = pow(station[i].x-station[j].x , );
double b = pow(station[i].y-station[j].y , );
double c = pow(station[i].z-station[j].z , );
distance = sqrt(a+b+c);
distance -= station[i].r + station[j].r;
if( distance <= ) distance = ;
return distance;
} void init()
{
for(int i= ; i<n ; i++)
scanf("%lf %lf %lf %lf",&station[i].x,&station[i].y,&station[i].z,&station[i].r);
for(int i= ; i<n ; i++)
road[i] = i;
need = ;
int k = ;
for(int i= ; i<n ; i++)
{
for(int j= ; j<n ; j++)
{
if( j>i )
{
edge[k].x = i;
edge[k].y = j;
edge[k].l = dis(i,j);
k++;
}
}
}
sort(edge,edge+n*(n-)/,cmp);
} void kruskal()
{
int i=,j=;
while( i<n- )
{
if( judge(edge[j].x,edge[j].y) )
need += edge[j].l , i++;
j++;
}
} int main()
{
while(cin>>n,n)
{
init();
kruskal();
printf("%.3f\n",need );
}
return ;
}