题意:就是给出三维坐标系上的一些球的球心坐标和其半径,搭建通路,使得他们能够相互连通。如果两个球有重叠的部分则算为已连通,无需再搭桥。求搭建通路的最小费用(费用就是边权,就是两个球面之间的距离)。
最小生成树问题,边权 = AB球面距离 = A球心到B球心的距离 – A球半径 – B球半径
边权 <= 0时已经联通 边权为0
链接:poj 2031
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <vector>
#include <sstream>
#define ll long long
using namespace std;
const int maxn = 10000 + 10;
const int maxm = 1000000 + 10;
const int inf = 0x3f3f3f3f;
int fa[maxn];
int rnk[maxn];
int n, m;
struct node {
int a, b;
double c;
friend bool operator < (node x, node y) {
return x.c < y.c;
}
}q[maxn];
void init() {
for(int i = 0; i <= n; i++) {
fa[i] = i;
rnk[i] = 0;
}
}
int getf(int x) {
if(x != fa[x]) {
fa[x] = getf(fa[x]);
}
return fa[x];
}
void unions(int x, int y) {
x = getf(x);
y = getf(y);
if(x == y) return;
if(rnk[x] < rnk[y]) {
fa[x] = y;
}
else {
fa[y] = x;
if(rnk[x] == rnk[y]) rnk[x]++;
}
}
bool same(int x, int y) {
return getf(x) == getf(y);
}
double kruskal(int n, int m) {
init();
sort(q + 1, q + 1 + m);
double ans = 0;
int nedge = 0;
for(int i = 1; i <= m && nedge != n - 1; i++) {
if(getf(q[i].a) != getf(q[i].b)) {
unions(q[i].a, q[i].b);
ans += q[i].c;
nedge++;
}
}
//if(nedge < n - 1) ans = -1;
return ans;
}
double x[maxn], y[maxn], z[maxn], r[maxn];
double dis(int i, int j) {
return sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]) + (z[i] - z[j]) * (z[i] - z[j]));
}
int main()
{
int T, kcase = 0;
while(scanf("%d", &n) && n) {
m = 0;
for(int i = 1; i <= n; i++) {
scanf("%lf %lf %lf %lf", &x[i], &y[i], &z[i], &r[i]);
for(int j = 1; j < i; j++) {
q[++m].a = i;
q[m].b = j;
if(dis(i, j) - r[i] - r[j] > 0) {
q[m].c = dis(i, j) - r[i] - r[j];
}
else {
q[m].c = 0;
}
}
}
double ans = kruskal(n, m);
printf("%.3lf\n", ans);
}
return 0;
}