题目链接:http://poj.org/problem?id=2031
题意:有n个球的坐标及半径,如果两个球相交,则视为连通,否则添加一条边使其连通,求所需要连接的边的最小费用。
题目看起来初觉是计算几何,但是仔细想一想,其实就是MST的一道题,很有意思。
计算任意两个球之间的最小距离即圆心距-两圆半径之和
若最小距离<0,说明两圆相交,则可以视为连通,不需要连接边
若最小距离>0,说明两圆相离,需要添加边,边权即为最小距离
最终使任意两球之间连通,可以把所有球都看成一个点,最小距离即为边权。
最后注意一下精度问题就好了,eps=1e-6。
#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps=1e-6;
const int INF=0x3f3f3f3f;
const int maxn=105;
int n;
struct Point{
double x,y,z;
double r;
}p[maxn];
double getWeight(Point A,Point B){//计算两个圆之间的最小距离
return 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);
}
double map[maxn][maxn];
double low[maxn];
bool vis[maxn];
double Prim(){//MST算法裸的模板
memset(vis,0,sizeof(vis));
int pos=1;
for(int i=1;i<=n;i++)
low[i]=map[pos][i];
vis[pos]=1;
double ans=0;
for(int i=1;i<=n-1;i++){
double min=INF;
for(int j=1;j<=n;j++){
if(!vis[j]&&low[j]<min)
min=low[pos=j];
}
ans+=min;
vis[pos]=1;
for(int j=1;j<=n;j++){
if(!vis[j]&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
}
return ans;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
#endif
while(~scanf("%d",&n)){
if(n<=0) break;
for(int i=1;i<=n;i++)
scanf("%lf%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z,&p[i].r);
memset(map,INF,sizeof(map));
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
double tmp=getWeight(p[i],p[j]);
if(tmp<=eps)//如果是负的,说明两圆相交,距离为0
map[i][j]=map[j][i]=0;
else
map[i][j]=map[j][i]=tmp;
}
}
double ans=Prim();
printf("%.3lf\n",ans);
}
return 0;
}