对这种问题不熟悉的读者 可以先去看一看最小圆覆盖的问题 ZOJ1450
现在我们来看最小球覆盖问题POJ2069 题目很裸,给30个点 求能覆盖所有点的最小球的半径。
先给出以下几个事实:
1.对于一个点,球心就是这个点且半径无穷小。
2.对于两个点,球心是两个点线段的中点,半径就是线段长度的一半。
3.对于三个点,三个点构成的平面必为球的大圆,所以球心是三角形的外心,半径就是球心到某个点的距离。
4.对于四个点,若四个点共面则转化到3,只需考虑某三个点的情况,若四点不共面,四面体可以唯一确定一个外接球。
5.对于五个及以上点,其最小球必为其中某4个点的外接球(假设不全共面)。
C(30,4)是可以接受的复杂度。在编程实现的时候,碰到不在球内的点,就让它成为球面上的点,期望复杂度为O(n)。
-----------------------------------------------------------------------------------------------------------------------------------------------
以上我们给出了一般的几何解法,但是求三角形外心和四面体的外界球,方程很复杂,代码量也很大,有没有简单的方法呢?
我们根据以上5个事实,可以知道所谓最小球的球心,它必然处于一个稳定态,也就是与它距离最远的点最多有4个且等距离。
于是,我们首先任选一个点作为球心,并找到点集中与它距离最远的点,我们让球心靠近最远的点,不断重复此过程,就可以让球心达到稳定态了!此时我们就找到了最小球。
1 #include <iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 const double eps=1e-7; 8 struct point3D 9 { 10 double x,y,z; 11 } data[35]; 12 int n; 13 double dis(point3D a,point3D b) 14 { 15 return 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)); 16 } 17 double solve() 18 { 19 double step=100,ans=1e30,mt; 20 point3D z; 21 z.x=z.y=z.z=0; 22 int s=0; 23 while(step>eps) 24 { 25 for(int i=0; i<n; i++) 26 if(dis(z,data[s])<dis(z,data[i])) s=i; 27 mt=dis(z,data[s]); 28 ans=min(ans,mt); 29 z.x+=(data[s].x-z.x)/mt*step; 30 z.y+=(data[s].y-z.y)/mt*step; 31 z.z+=(data[s].z-z.z)/mt*step; 32 step*=0.98; 33 } 34 return ans; 35 } 36 int main() 37 { // freopen("t.txt","r",stdin); 38 double ans; 39 while(~scanf("%d",&n),n) 40 { 41 for(int i=0; i<n; i++) 42 scanf("%lf%lf%lf",&data[i].x,&data[i].y,&data[i].z); 43 ans=solve(); 44 printf("%.5f\n",ans); 45 } 46 return 0; 47 }