对这种问题不熟悉的读者 可以先去看一看最小圆覆盖的问题 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 }  
View Code

 

 

posted on 2017-05-08 20:53  Bingsen  阅读(2739)  评论(0编辑  收藏  举报