题目链接: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; }