题意:有n个space station,每个space station都是一个球体,已知球的坐标,半径,(x,y,z,r都是double型的)现在要求在space station之间建个通道让所有space station都连接起来,求最小需要建立的通道;注意:如果两个space station相接触或相交就不需要建立通道,即通道为0;很明显求最小生成树;
space station间的距离:(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2)-r1-r2;
这次用Kruskal算法,需要用到并查集;
下面是代码:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;
const int M=105; //最多100个space station;
int uset[M]; //uset数组存该点的祖先;
int n, cnt; //n是space station的个数;cnt表示边的条数;
struct cell{ //struct cell中是space station的坐标和半径;
double x, y, z, r;
}q[M];
struct edge{ //边的两端点s,e,边权w(通道的长度);
int s, e;
double w;
}E[M*M]; //存边;
bool cmp(edge a, edge b){ //sort中的cmp函数,对结构体排序;
return a.w < b.w;
}
double between(cell a, cell b){//计算两space station通道长度函数;
double dis;
dis=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z))-a.r-b.r;
if(dis<=0) return 0; //if 距离小于等于0,说明两space station相交或接触,通道为0;
else return dis; //否则通道值即为距离值;
}
int find(int x){ //并查集中find()函数的递归写法;
return x==uset[x]? x : uset[x]=find(uset[x]);
}
bool link(int x, int y){ //连接两点;
int fx, fy;
fx=find(x);
fy=find(y);
if(fx==fy) return false;
else{
uset[fy]=find(fx);
return true;
}
}
void Kruskal(){
int i, j, k;
sort(E+1, E+1+cnt, cmp);//对边排序;
for(i=1; i<=n; i++)
uset[i]=i;
double sum=0.0;
int ct=0;
for(i=1; i<=cnt; i++){ //遍历边;
if(link(E[i].s, E[i].e)){ //判断该边的两端点是否已经相连,没连就加上该边,已连就看下一条边;
sum+=E[i].w;
ct++;
if(ct==n-1) //直到找到n-1条边,跳出循环;
break;
}
}
printf("%.3f\n",sum);
return;
}
int main(){
while(scanf("%d",&n),n){
int i, j;
cnt=0;
for(i=1; i<=n; i++){
scanf("%lf%lf%lf%lf",&q[i].x,&q[i].y,&q[i].z,&q[i].r);
for(j=1; j<i; j++){
double dis;
dis=between(q[j], q[i]);
E[++cnt].s=j;
E[cnt].e=i;
E[cnt].w=dis;
}
}
Kruskal();
}
return 0;
}