poj2031:http://poj.org/problem?id=2031
题意:就是给出三维坐标系上的一些球的球心坐标和其半径,搭建通路,使得他们能够相互连通。如果两个球有重叠的部分则算为已连通,无需再搭桥。求搭建通路的最小费用(费用就是边权,就是两个球面之间的距离)。
题解:其实就是图论的最小生成树问题球心坐标和半径是用来求 两点之间的边权 的,求出边权后,把球看做点,用邻接矩阵存储这个无向图,再求最小生成树,非常简单的水题把球A和球B看做无向图图的两个结点,那么边权 = AB球面距离 = A球心到B球心的距离 – A球半径 – B球半径 .注意若边权<=0,说明两球接触,即已连通,此时边权为0
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n,pa[],num,cnt;
struct Edge{
int u;
int v;
double w;
bool operator <(const Edge &a)const{
return w<a.w;
}
}edge[];
struct Node{
double x;
double y;
double z;
double r;
}node[];
void UFset(){
for(int i=;i<=n;i++)
pa[i]=-;
}
int Find(int x){
int s;
for(s=x;pa[s]>=;s=pa[s]);
while(s!=x){
int temp=pa[x];
pa[x]=s;
x=pa[x];
}
return s ;
}
void Union(int R1,int R2){
int r1=Find(R1);
int r2=Find(R2);
int temp=pa[r1]+pa[r2];
if(pa[r1]>pa[r2]){
pa[r1]=r2;
pa[r2]=temp;
}
else{
pa[r2]=r1;
pa[r1]=temp;
}
}
void kruska(){
UFset();
num=;
double sum=0.0;
for(int i=;i<cnt;i++){
int u=edge[i].u;
int v=edge[i].v;
if(Find(u)!=Find(v)){
sum+=edge[i].w;
Union(u,v);
num++;
}
if(num>=n-)break;
}
printf("%.3f\n",sum);
}
int main(){
while(~scanf("%d",&n)&&n){
for(int i=;i<=n;i++){
scanf("%lf%lf%lf%lf",&node[i].x,&node[i].y,&node[i].z,&node[i].r);
}
cnt=;
for(int i=;i<=n;i++)
for(int j=i+;j<=n;j++){
double s1=node[i].x-node[j].x;
double s2=node[i].y-node[j].y;
double s3=node[i].z-node[j].z;
double ss=sqrt(s1*s1+s2*s2+s3*s3);
if(ss>node[i].r+node[j].r){
edge[cnt].u=i;edge[cnt].v=j;edge[cnt++].w=ss-node[i].r-node[j].r;
}
else{
edge[cnt].u=i;edge[cnt].v=j;edge[cnt++].w=;
}
} sort(edge+,edge+cnt);
kruska();
}
}