- 给定(n)个点,找出两个半径相等的圆覆盖所有点,且半径最小。
- (nle10^3)
最小覆盖圆
必须要先去看看这道题:【洛谷1742】最小圆覆盖(随机增量法)。
通过上面这题,默认你已经知道如何求最小覆盖圆了。
旋转+二分
考虑我们找一条分界线把点集划分成两部分,分别求出两部分的最小覆盖圆,那么只要能找到一个最优的划分,使得两个最小覆盖圆中较大的半径即为答案。
由于分界线可能是斜线,似乎不太好操作。
因此我们第一步是将整张图不断旋转,每次转大概(frac{pi}{100}),总有一种情况使得最优的分界线垂直于(x)轴。
然后,我们把所有点按照横坐标排序,接着通过二分,每次取中间位置作为划分线更新答案,再就看左右区间哪边最小覆盖圆半径大就往哪边走。
代码:(O(200nlogn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
#define DB double
using namespace std;
int n;struct P
{
DB x,y;I P(Con DB& a=0,Con DB& b=0):x(a),y(b){}
I P operator + (Con P& o) Con {return P(x+o.x,y+o.y);}
I P operator - (Con P& o) Con {return P(x-o.x,y-o.y);}
I P operator * (Con DB& o) Con {return P(x*o,y*o);}
I P operator / (Con DB& o) Con {return P(x/o,y/o);}
I DB operator * (Con P& o) Con {return x*o.x+y*o.y;}
I DB operator ^ (Con P& o) Con {return x*o.y-y*o.x;}
I bool operator < (Con P& o) Con {return x<o.x;}
I DB L() {return sqrt((*this)*(*this));}
I P R(Con DB& g) {return P(x*cos(g)-y*sin(g),x*sin(g)+y*cos(g));}
}s[N+5],s_[N+5];
struct Cir
{
P O;DB r;I Cir(Con P& o=P(),Con DB& x=0):O(o),r(x){}
I bool Chk(Con P& p) {return (O-p).L()<=r;}
}C;
struct S {P A,V;I S(Con P& a=P(),Con P& v=P()):A(a),V(v){}};
I S MidVer(Con P& A,Con P& B) {return S((A+B)/2,(B-A).R(acos(-1)/2));}
I P GetO(Con P& A,Con P& B,Con P& C)//三点确定一个圆
{
S S1=MidVer(A,B),S2=MidVer(A,C);if(fabs(S2.V^S1.V)<1e-12) return P(-1e9,-1e9);
return S1.A+S1.V*((S2.V^(S2.A-S1.A))/(S2.V^S1.V));
}
P p[N+5];I DB Calc(CI l,CI r)//计算[l,r]中点的最小覆盖圆
{
RI i,j,k;for(i=l;i<=r;++i) p[i-l+1]=s[i];random_shuffle(p+1,p+r-l+2),C=Cir();
for(i=1;i<=r-l+1;++i) if(!C.Chk(p[i])) for(C=Cir(p[i],0),
j=1;j^i;++j) if(!C.Chk(p[j])) for(C=Cir((p[i]+p[j])/2,(p[i]-p[j]).L()/2),
k=1;k^j;++k) if(!C.Chk(p[k])) C.O=GetO(p[i],p[j],p[k]),C.r=(p[i]-C.O).L();
return C.r;
}
int main()
{
RI i,k,l,r,mid;DB t,L,R;srand(302627441);W(scanf("%d",&n),n)
{
for(i=1;i<=n;++i) scanf("%lf%lf",&s_[i].x,&s_[i].y);
for(t=1e9,k=0;k^200;++k)//不断旋转
{
for(i=1;i<=n;++i) s[i]=s_[i].R(acos(-1)/100*k);sort(s+1,s+n+1);//旋转后按横坐标排序
l=1,r=n;W(l<=r) mid=l+r>>1,(L=Calc(1,mid))<(R=Calc(mid+1,n))?l=mid+1:r=mid-1,t=min(t,max(L,R));//二分最优分界线
}printf("%.2lf
",t);
}return 0;
}