这题目,用G++ WA,用C++ AC。
题目要求,现给出n个球,然后要使每两个球直接或者间接连通,可以在任意两球之间做管道(在表面),最后的要求是,如果使得都连通的话,管道最小长度是多少。
思路简单,就是求出来每两个球之间的距离,球心差减去两球的半径,注意如果这里是负的则处理为0。然后就是克鲁斯卡尔求最小生成树就可以了。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 #include <string.h> 5 #include <algorithm> 6 #define eps 0.00000001 7 struct point{ 8 double x,y,z,r; 9 }p[105]; 10 struct edge{ 11 int u,v; 12 double w; 13 }e[105*105]; 14 int parent[105]; 15 int n,cnt; 16 void ufset(){ 17 int i; 18 for(i=0;i<=n;++i) parent[i]=-1; 19 return; 20 } 21 int find(int x){ 22 int s; 23 for(s=x;parent[s]>=0;s=parent[s]); 24 while(s!=x){ 25 int tmp=parent[x]; 26 parent[x]=s; 27 x=tmp; 28 } 29 return s; 30 } 31 void Union(int R1,int R2){ 32 int r1=find(R1),r2=find(R2); 33 int tmp=parent[r1]+parent[r2]; 34 if(parent[r1]>parent[r2]){ 35 parent[r1]=r2; parent[r2]=tmp; 36 }else{ 37 parent[r2]=r1; parent[r1]=tmp; 38 } 39 return; 40 } 41 int cmp(struct edge a,struct edge b){ 42 return eps<b.w-a.w; 43 } 44 double kruskal(){ 45 double sumweight=0; 46 int i; 47 int num=0; 48 int u,v; 49 ufset(); 50 for(i=0;i<cnt;++i){ 51 u=e[i].u; v=e[i].v; 52 if(find(u)!=find(v)){ 53 sumweight+=e[i].w; num++; 54 // printf("sumweight=%lf,u=%d,v=%d ",sumweight,u,v); 55 Union(u,v); 56 } 57 if(num>=n-1) break; 58 } 59 return sumweight; 60 } 61 62 63 64 65 66 67 68 double count(struct point a,struct point b){ 69 double res; 70 res=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) ) - a.r - b.r ; 71 if( 0-res > eps ) res=0; 72 return res; 73 } 74 75 76 77 int main(){ 78 int i,j; 79 while(~scanf("%d",&n)){ 80 if(n==0) break; 81 for(i=0;i<n;++i){ 82 scanf("%lf%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z,&p[i].r); 83 } 84 cnt=0; 85 for(i=0;i<n;++i){ 86 for(j=0;j<i;++j){ 87 e[cnt].u=i; 88 e[cnt].v=j; 89 e[cnt].w=count(p[i],p[j]); 90 cnt++; 91 } 92 } 93 std::sort(e,e+cnt,cmp); 94 // for(i=0;i<cnt;++i){ 95 // printf("%lf ",e[i].w); 96 // } 97 // printf("------------ "); 98 double res=kruskal(); 99 printf("%.3lf ",res); 100 } 101 return 0; 102 }