这个题是一道模拟退火题,但我用的是最小覆盖圆解决的;
这是我认为写得比较好的文章,在这里与大家分享。
这道题其实就是求一个最小外接圆圆心和半径。所求点即是圆心,距离就是半径。点集的最小外接圆,其实就是点集的最小圆覆盖,就是找一个最小的圆,将所有点覆盖掉。这道题的题意是求一个点,使得到点集的最远点距离最近,下边我用我的方式,不严谨的证明一下。先证明最小外接圆的圆心到其最远的点距离最近。可知最小外接圆上最少有两个点,如果是两个点,必然在一条直径上,否则就至少有三个点,且这三个点之间相对于圆心的夹角两两不超过180度。这点很好证明。如果不满足上述的情况,那么肯定能找到半个圆上没有任何点,
如上图所示,只要按照箭头方向移动,然后就可以缩小这个圆,直到满足这个条件为止。其实刚开始的那句话可以如此概括,点集的最小外切圆上,任意一条直径将圆分成两部分,要么两半圆上两部分上都有点,要么直径的两端都有点。先把解放在圆心上。这样的话,拥有最大长度的必定是那些在圆周上的点。然后我们看看移动解,能不能得到更优的结果,但是由于上述的性质,所以不管向哪里移动,总有一个圆周上的点到解的距离会变大,所以可知,圆心上的就是最优解。反过来证也差不多,所以我也不再赘述了。求最小外切圆目前看来最好的算法莫过于随机增量算法,其实就是增量算法,每次加一点进去,然后不断维护最小外切圆,但是裸的增量算法,最坏的时间复杂度是O(n^3)的,但是考虑到每次的更新发生的概率都较小,所以先给点集随机洗牌,这样可以做到概率上收敛于O(n)。(p.s:某些oj居然不能播种。。。但是不播种也无所谓了。。。反正都是伪随机)
这次就不上代码了,代码比较简单,主要是思维有点麻烦。
View Code
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<cmath>
5 using namespace std;
6 const double PI = acos( -1.0 );
7 class Point
8 {
9 public:
10 double x,y;
11 };
12 Point point[1024],mid,mincoorde,maxcoorde,midstep;
13 double ans , ansmin , X ,Y ,R;
14 int n;
15 double Distance( Point a, Point b )
16 {
17 return sqrt(( a.x - b.x )*( a.x - b.x ) + ( a.y - b.y )*( a.y - b.y ));
18 }
19 void Solve( double x, double y )
20 {
21 Point tp;
22 double max = 0 ;
23 tp.x = x ; tp.y = y;
24 for( int i = 0; i < n ; i ++ )
25 {
26 max += Distance( tp , point[i] );
27 }
28 if( max < ans )
29 {
30 ans = max;
31 midstep = tp;
32 }
33 }
34 int main( )
35 {
36 while( scanf( "%d",&n )==1 )
37 {
38 maxcoorde.x = maxcoorde.y = 0;
39 mincoorde.x = mincoorde.y = 10000;
40 for( int i = 0 ; i < n ; i++ )
41 {
42 scanf( "%lf %lf",&point[i].x , &point[i].y );
43 if( point[i].x > maxcoorde.x ) maxcoorde.x = point[i].x;
44 if( point[i].y > maxcoorde.y ) maxcoorde.y = point[i].y;
45 if( point[i].x < mincoorde.x ) mincoorde.x = point[i].x;
46 if( point[i].y < mincoorde.y ) mincoorde.y = point[i].y;
47 }
48 mid.x = ( maxcoorde.x + mincoorde.x )/2.0;
49 mid.y = ( maxcoorde.y + mincoorde.y )/2.0;
50 R=sqrt((maxcoorde.x-mincoorde.x)*(maxcoorde.y-mincoorde.y) + (maxcoorde.x-mincoorde.x)*(maxcoorde.y-mincoorde.y))/2.0;
51 ans = 0;
52 for( int i = 0; i < n; i++ )
53 {
54 ans += Distance( mid , point[i] );
55 }
56 ansmin = ans;
57 while( 1 )
58 {
59 for( double i = 0; i <= 2*PI; i += 0.1 )
60 {
61 Solve( mid.x + R*sin( i ) , mid.y + R*cos( i ) );
62 }
63 if( ( ansmin - ans ) < 0.1 && R < 0.01 ) break;
64 if( ansmin > ans )
65 {
66 ansmin = ans;
67 mid = midstep;
68 }
69 else R*=0.7;
70 }
71 // printf( "(%.1lf,%.1lf).\n",mid.x, mid.y );
72 printf( "%.lf\n",ansmin);
73 }
74 return 0;
75 }