Building a Space Station POJ

You are a member of the space station engineering team, and are assigned a task in the construction process of the station. You are expected to write a computer program to complete the task. 
The space station is made up with a number of units, called cells. All cells are sphere-shaped, but their sizes are not necessarily uniform. Each cell is fixed at its predetermined position shortly after the station is successfully put into its orbit. It is quite strange that two cells may be touching each other, or even may be overlapping. In an extreme case, a cell may be totally enclosing another one. I do not know how such arrangements are possible. 

All the cells must be connected, since crew members should be able to walk from any cell to any other cell. They can walk from a cell A to another cell B, if, (1) A and B are touching each other or overlapping, (2) A and B are connected by a `corridor', or (3) there is a cell C such that walking from A to C, and also from B to C are both possible. Note that the condition (3) should be interpreted transitively. 

You are expected to design a configuration, namely, which pairs of cells are to be connected with corridors. There is some freedom in the corridor configuration. For example, if there are three cells A, B and C, not touching nor overlapping each other, at least three plans are possible in order to connect all three cells. The first is to build corridors A-B and A-C, the second B-C and B-A, the third C-A and C-B. The cost of building a corridor is proportional to its length. Therefore, you should choose a plan with the shortest total length of the corridors. 

You can ignore the width of a corridor. A corridor is built between points on two cells' surfaces. It can be made arbitrarily long, but of course the shortest one is chosen. Even if two corridors A-B and C-D intersect in space, they are not considered to form a connection path between (for example) A and C. In other words, you may consider that two corridors never intersect. 

Input

The input consists of multiple data sets. Each data set is given in the following format. 


x1 y1 z1 r1 
x2 y2 z2 r2 
... 
xn yn zn rn 

The first line of a data set contains an integer n, which is the number of cells. n is positive, and does not exceed 100. 

The following n lines are descriptions of cells. Four values in a line are x-, y- and z-coordinates of the center, and radius (called r in the rest of the problem) of the sphere, in this order. Each value is given by a decimal fraction, with 3 digits after the decimal point. Values are separated by a space character. 

Each of x, y, z and r is positive and is less than 100.0. 

The end of the input is indicated by a line containing a zero. 

Output

For each data set, the shortest total length of the corridors should be printed, each in a separate line. The printed values should have 3 digits after the decimal point. They may not have an error greater than 0.001. 

Note that if no corridors are necessary, that is, if all the cells are connected without corridors, the shortest total length of the corridors is 0.000. 

Sample Input

3
10.000 10.000 50.000 10.000
40.000 10.000 50.000 10.000
40.000 40.000 50.000 10.000
2
30.000 30.000 30.000 20.000
40.000 40.000 40.000 20.000
5
5.729 15.143 3.996 25.837
6.013 14.372 4.818 10.671
80.115 63.292 84.477 15.120
64.095 80.924 70.029 14.881
39.472 85.116 71.369 5.553
0

Sample Output

20.000
0.000
73.834

题意:在一个三维空间内,有几个球,现在要在球与球之间修建走廊,问在让所有的球都能被连接时要使修建的走廊的长度最小,最小为多少。球的数量不超过100个,结果保留三维小数。
输入多组测试样例,以0结束,每个测试样例第一行一个数字n表示有n个球,然后n行,每行四个数,表示这个球的坐标x,y,z,以及这个球的半径,

思路:首先要求每两个球之间的距离,两个距离比两个球的半径和小则不需要再修走廊,如果比半径和大则需要修长度为距离-半径和的走廊,对那个球一个编号,两个球之间的走廊长度为点之间的距离
因此转换成了保证所有点相连的前提下球最小总长度的问题,用最小生成树的思想,先对所有的边进行从小到大的排序,然后从小开始排查每个边,如果相连的两个点不在同一个集合中,则连接这两个点,
并保留这个边。以此类推。

代码:
  1 #include <cstdio>
  2 #include <fstream>
  3 #include <algorithm>
  4 #include <cmath>
  5 #include <deque>
  6 #include <vector>
  7 #include <queue>
  8 #include <string>
  9 #include <cstring>
 10 #include <map>
 11 #include <stack>
 12 #include <set>
 13 #include <sstream>
 14 #include <iostream>
 15 #define mod 998244353
 16 #define eps 1e-6
 17 #define ll long long
 18 #define INF 0x3f3f3f3f
 19 using namespace std;
 20 
 21 //u表示起点,v表示终点,cost表示花费
 22 struct node
 23 {
 24     int u,v;
 25     double cost;
 26 };
 27 //排序,从小到大
 28 bool cmp(node a,node b)
 29 {
 30     return a.cost<b.cost;
 31 }
 32 //存放边的信息
 33 vector<node> ve;
 34 
 35 //fa表示当前i的最远祖先
 36 int fa[110];
 37 //初始化fa,开始时自己是自己的祖先
 38 void init(int qwq)
 39 {
 40     for(int i=0;i<=qwq;i++)
 41     {
 42         fa[i]=i;
 43     }
 44 }
 45 //查找最远祖先,同时进行路径压缩
 46 int find(int x)
 47 {
 48     if(fa[x]==x)
 49     {
 50         return x;
 51     }
 52     return fa[x]=find(fa[x]);
 53 }
 54 //判断最远祖先是否相同
 55 bool che(int x,int y)
 56 {
 57     return find(x)==find(y);
 58 }
 59 //合并x,y,把他们放到同一个家族中
 60 void mer(int x,int y)
 61 {
 62     if(!che(x,y)) 
 63     {
 64         fa[fa[x]]=fa[y];
 65     }
 66     return ;
 67 }
 68 //n表示点数m表示边数
 69 int n,m;
 70 //保存点之间的数据
 71 struct spot
 72 {
 73     double x,y,z;
 74     double r;
 75 };
 76 spot sp[105];
 77 //求球与球之间要修建走廊的掺长度
 78 double dance(spot a,spot b)
 79 {
 80     double dan = 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));
 81     double s = dan-a.r-b.r;
 82     if(s<=0)
 83     {
 84         return 0;
 85     }
 86     return s;
 87 }
 88 int main()
 89 {
 90     while(scanf("%d",&n)&&n!=0)
 91     {
 92         //初始化
 93         node no;
 94         double ans=0;
 95         init(n);
 96         for(int i=0;i<n;i++)
 97         {
 98             scanf("%lf %lf %lf %lf",&sp[i].x,&sp[i].y,&sp[i].z,&sp[i].r);
 99             //遍历其他球,连接这个球与其他球之间的走廊
100             for(int j=i-1;j>=0;j--)
101             {
102                 no.u=i;
103                 no.v=j;
104                 no.cost=dance(sp[j],sp[i]);
105                 ve.push_back(no);
106             }
107         }
108         //排序
109         sort(ve.begin(),ve.end(),cmp);
110         for(int i=0;i<ve.size();i++)
111         {
112             //如果这两个点不在同一集合
113             if(!che(ve[i].u,ve[i].v))
114             {
115                 //合并这两个点
116                 mer(ve[i].u,ve[i].v);
117                 ans+=ve[i].cost;
118             }
119         }
120         //输出保留三位小数
121         printf("%.3lf
",ans);
122          //清空vector容器的数据
123         ve.clear();
124     }
125 }



原文地址:https://www.cnblogs.com/mzchuan/p/11722897.html