POJ 2031 Building a Space Station(MST)

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


你可能感兴趣的:(POJ 2031 Building a Space Station(MST))