最小圆覆盖学习
今天学习最小圆覆盖。
算法
实际上算法就三个循环。
%%%!
void solve(){
for( int i = 1 ; i <= N ; i ++ )
if( !inside( p[i] ) ){
O = p[i] ; R = 0 ;
for( int j = 1 ; j < i ; j ++ )
if( !inside( p[j] ) ){
O = ( p[i] + p[j] ) / 2 ;
R = dis( p[i] , O ) ;
for( int k = 1 ; k < j ; k ++ )
if( !inside( p[k] ) ){
O = cal( p[i] , p[j] , p[k] ) ;
R = dis( p[i] , O ) ;
}
}
}
printf( "%f\n" , R ) ;
printf( "%f %f\n" , O.x , O.y ) ;
}
复杂度分析
略
正确性分析
首先假设我们求出了i个点的最小圆覆盖
显然如果第i+1个点在里面就忽略
否则第i+1个点一定在圆上(可以用凸包的性质考虑)
然后由于我们之前枚举了i和j的性质,那么当前以i为直径的一个端点,j为直径的另一个端点的圆必然只会不能包含一个点,并且将圆的边界强行拉到这个点后其他点编号小于j的点一定不会到圆的外面去。
啥意思?
我们把圆的边界拉到(离当前直径最远的)k上是肯定合法的。
否则就会变成这样:
并且此时k1、k2<i,j
但是由于我们之前已经枚举了i、j,那么凭啥(k1,k2)不能构成直径呢?
它构成直径后i、j就直接被包在圆里不用考虑了。
所以这个算法的正确性是有保证的。