题意:
给定一些球的圆心与半径,如果两个球之间相交,则他们之间连通,
否则不连通,问还需要连多长的边使所有球都连通
思路:
任意两个球,如果连通则边权为0,
否则边权为d-r1-r2,d是两球圆心坐标之间的距离,r1与r2分别为两个球的半径,
这样求一个最小生成树,将所有球连接起来
数据double类型的时候,用G++的时候scanf要用%lf,而printf的时候要用%f,否则会WA
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#define maxn 110
#define maxm 100110
#define INF 0x7f7f7f7f
using namespace std;
struct Edge {
int u, v;
double dis;
Edge () {}
Edge ( int u, int v, double d )
: u ( u )
, v ( v )
, dis ( d ) {}
bool operator< ( const Edge &rhs ) const { return dis < rhs.dis; }
};
struct Kruskal {
int n, m;
int fa[ maxn ];
Edge edg[ maxm ];
void init ( int n ) {
this->n = n;
m = 0;
memset ( fa, -1, sizeof ( fa ) );
}
int findset ( int x ) { return fa[ x ] == -1 ? x : fa[ x ] = findset ( fa[ x ] ); }
void AddEdge ( int u, int v, double dis ) { edg[ m++ ] = Edge ( u, v, dis ); }
double kruskal () {
double sum = 0;
int cnt = 0;
sort ( edg, edg + m );
for ( int i = 0; i < m; ++i ) {
int u = edg[ i ].u;
int v = edg[ i ].v;
if ( findset ( u ) != findset ( v ) ) {
sum += edg[ i ].dis;
// UNION
fa[ findset ( u ) ] = findset ( v );
if ( ++cnt >= n - 1 )
break;
}
}
return sum;
}
} KK;
struct Point {
double x, y, z, r;
} p[ maxn ];
//求两个cell之间的距离是否能直接连通
double getdis ( int i, int j ) {
double len = sqrt ( ( p[ i ].x - p[ j ].x ) * ( p[ i ].x - p[ j ].x ) +
( p[ i ].y - p[ j ].y ) * ( p[ i ].y - p[ j ].y ) +
( p[ i ].z - p[ j ].z ) * ( p[ i ].z - p[ j ].z ) );
double ans = len - p[ i ].r - p[ j ].r;
if ( ans < 0 )
return 0;
else
return ans;
}
int main () {
int n;
while ( ~scanf ( "%d", &n ) && n ) {
for ( int i = 0; i < n; ++i )
scanf ( "%lf%lf%lf%lf", &p[ i ].x, &p[ i ].y, &p[ i ].z, &p[ i ].r );
KK.init ( n );
for ( int i = 0; i < n; ++i )
for ( int j = i + 1; j < n; ++j )
KK.AddEdge ( i, j, getdis ( i, j ) );
double sol = KK.kruskal ();
printf ( "%.3f\n", sol );
}
return 0;
}