题意:
就是给出三维坐标系上的一些球的球心坐标和其半径,搭建通路,使得他们能够相互连通。如果两个球有重叠的部分则算为已连通,无需再搭桥。求搭建通路的最小边长总和是多少。
思路:
先处理空间点之间的距离,要注意的是两个球面相交的情况,相交的话距离是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[110]; struct Vdge //构造生成树的点 { int x,y; double l; }; bool cmp(Vdge a,Vdge b){ return a.l < b.l ; } Vdge edge[5500]; int road[110]; 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 , 2); double b = pow(station[i].y-station[j].y , 2); double c = pow(station[i].z-station[j].z , 2); distance = sqrt(a+b+c); distance -= station[i].r + station[j].r; if( distance <= 0 ) distance = 0; return distance; } void init() { for(int i=0 ; i<n ; i++) scanf("%lf %lf %lf %lf",&station[i].x,&station[i].y,&station[i].z,&station[i].r); for(int i=0 ; i<n ; i++) road[i] = i; need = 0; int k = 0; for(int i=0 ; i<n ; i++) { for(int j=0 ; 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-1)/2,cmp); } void kruskal() { int i=0,j=0; while( i<n-1 ) { if( judge(edge[j].x,edge[j].y) ) need += edge[j].l , i++; j++; } } int main() { while(cin>>n,n) { init(); kruskal(); printf("%.3f ",need ); } return 0; }