Building a Space Station poj-2031
题目大意:在一个三维平面内,给出n个球,我想把两个球连起来,使得两两之间联通,连起来的代价是两个球的球心距减去半径之和。如果两球相切或相交,则代价是0。
注释:n<=100,坐标以及半径是double。
想法:我们首先两个球之间的距离的运算规则Dist。运算之后我们暴力枚举每两个球之间的距离,并将他们存入到单独的边得结构体里。然后,将点的编号设为1-n,即可。最后,由于两两联通,我们用kruskal算法求最小生成树即可。
最后,附上丑陋的代码... ...
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm>//C-Free时默认开全库的...蜜汁CE 5 #define N 110 6 using namespace std; 7 double x[N],y[N],z[N],r[N]; 8 int fa[N]; 9 struct Node 10 { 11 int a; 12 int b; 13 double val; 14 }f[N*N];//由于我们f数组记录的是边的数量,所以要开到n*n。 15 bool cmp(Node a,Node b) 16 { 17 return a.val<b.val; 18 } 19 int find(int x) 20 { 21 return fa[x]==x?x:(fa[x]=find(fa[x])); 22 } 23 inline bool merge(int x,int y) 24 { 25 x=find(x),y=find(y); 26 if(x==y)return 0; 27 fa[x]=y;return 1; 28 } 29 inline double Dist(int a,int b)//运算Dist 30 { 31 double ans; 32 ans=sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b])+(z[a]-z[b])*(z[a]-z[b])); 33 if(ans<=r[a]+r[b]) return 0; 34 else return ans-r[a]-r[b]; 35 } 36 int main() 37 { 38 int n; 39 int cnt=0; 40 while(1) 41 { 42 scanf("%d",&n); 43 if(n==0) return 0; 44 cnt=0; 45 for(int i=1;i<=n;i++) 46 { 47 scanf("%lf%lf%lf%lf",&x[i],&y[i],&z[i],&r[i]); 48 } 49 for(int i=1;i<=n;i++) fa[i]=i; 50 for(int i=1;i<=n;i++) 51 { 52 for(int j=i+1;j<=n;j++) 53 { 54 f[++cnt].a=i; 55 f[cnt].b=j; 56 f[cnt].val=Dist(i,j); 57 } 58 } 59 sort(f+1,f+cnt+1,cmp);//这步很重要,别问我是怎么知道的... 60 int count=0; 61 double ans=0; 62 for(int i=1;i<=cnt;i++)//kruskal 63 { 64 if(merge(f[i].a,f[i].b)) 65 { 66 ans+=f[i].val; 67 count++; 68 } 69 if(count==n-1) break; 70 } 71 printf("%.3lf ",ans); 72 } 73 }
小结:kruskal算法比较的方便,虽然这是prim算法的课后练习题。
错误:1.C-Free是默认开全库的,我排序的时候忘记开algorithm库了...
2.由于这是一个选择的代价最小问题,我们可以清楚地发现这是没有误解情况的。
3.kruskal必须保证每条边被存了且只被存了一次。