题目大意
- 给你n个空间直角坐标作为球心,告诉你球的半径,在球表面之间修路,问路最少修多长能够使得球相互可达
练习一下简单的最小生成树 - 首先需要解决几个问题
- 建图
- 如何判断球之间不需要修路
一共只有不到100个输入,所以可以两两之间计算路径长,用并查集判断球之间不需要修路,之后就是正常的最小生成树了
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 50000;
int set[MAXN];
struct st{
double x,y,z,r;
}s[MAXN];
struct Edge{
int x,y;
double z;
}edge[MAXN];
int Find(int x){
if(x == set[x]) return x;
else return set[x] = Find(set[x]);
}
bool cmp(Edge x,Edge y){
return x.z<y.z;
}
void UNION(int x,int y){
x = Find(x);
y = Find(y);
if(x!=y){
set[x] = set[y];
}
}
double DIS(int x,int y){
double a = s[x].x - s[y].x;
double b = s[x].y - s[y].y;
double c = s[x].z - s[y].z;
double d = s[x].r + s[y].r;
double ans = sqrt(a*a+b*b+c*c)-d;
if(ans>=0) return ans;
return 0;
}
void Kruskal(int n,int num){
double ans = 0;
int tot = 0;
for(int i=0;i<num;i++){
int x = edge[i].x;
int y = edge[i].y;
int dx = Find(x);
int dy = Find(y);
if(dx == dy) continue;
set[dx] = set[dy];
ans += DIS(x,y);
tot++;
if(tot == n-1) break;
}
printf("%.3f\n",ans);
}
bool judge(int x,int y){
double a = s[x].x - s[y].x;
double b = s[x].y - s[y].y;
double c = s[x].z - s[y].z;
double d = s[x].r + s[y].r;
if(a*a+b*b+c*c<=d*d) return true;
return false;
}
int main(){
int n;
while(cin>>n&&n){
int num = 0;
for(int i=0;i<n;i++) {
cin>>s[i].x>>s[i].y>>s[i].z>>s[i].r;
}
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
edge[num].x = i;
edge[num].y = j;
edge[num].z = DIS(i,j);
num++;
}
}for(int i=0;i<num;i++){
set[i] = i;
}for(int i=0;i<num;i++){
if(judge(edge[i].x,edge[i].y)) UNION(edge[i].x,edge[i].y);
}
sort(edge,edge+num,cmp);
Kruskal(n,num);
}
return 0;
}