题目链接:https://ac.nowcoder.com/acm/problem/19959
题目大意:
思路:我们考虑有一条直线把这些点,分割成两个部分。分别做一个最小圆覆盖。半径取max(R1, R2)。
我们可以考虑旋转坐标系来枚举直线的斜率。那么对一条特定的坐标系。按坐标轴就可以二分一下。得到最优的半径。
只要旋转180度就够了。
#include<bits/stdc++.h>
#define db double
#define eps 1e-9
const db ki=1.0/180*acos(-1);
const db co=cos(ki),si=sin(ki);
using namespace std;
const int N=1e3+5;
db r=0.0;
struct point{db x,y;}o,a[N],p[N];
inline point rotate(const point &b){
point t;
t.x=b.x*co+b.y*si;
t.y=b.y*co-b.x*si;
return t;
}
struct line{db a,b,c;};
inline db pow2(db x){return x*x;}
inline db dis(point a,point b){return sqrt(pow2(a.x-b.x)+pow2(a.y-b.y));}
inline bool check(point x){return dis(o,x)<=r+eps;}
inline point con(line a,line b){return point{(b.c*a.b-a.c*b.b)/(a.a*b.b-a.b*b.a),(b.c*a.a-a.c*b.a)/(a.b*b.a-b.b*a.a)};}
inline db check(int ll,int rr){
if(ll>rr) return 0;
for(int i=ll;i<=rr;i++) p[i]=a[i];
for(int i=ll; i<=rr; i++){
//cout<<p[i].x<<" "<<p[i].y<<endl;
}
random_shuffle(p+ll,p+rr+1);
o=p[ll];r=0.0;
for(int i=ll;i<=rr;i++)
if(!check(p[i])){
o.x=p[i].x,o.y=p[i].y;r=0;
for(int j=ll;j<i;j++)
if(!check(p[j])){
o.x=(p[i].x+p[j].x)/2,o.y=(p[i].y+p[j].y)/2;
r=dis(o,p[j]);
for(int k=ll;k<j;k++)
if(!check(p[k])){
o=con(line{2*(p[i].x-p[k].x),2*(p[i].y-p[k].y),pow2(p[k].x)+pow2(p[k].y)-pow2(p[i].x)-pow2(p[i].y)},line{2*(p[i].x-p[j].x),2*(p[i].y-p[j].y),pow2(p[j].x)+pow2(p[j].y)-pow2(p[i].x)-pow2(p[i].y)});
r=dis(p[k],o);
}
}
}
return r;
}
int main()
{
int n;
while(scanf("%d", &n), n){
double R=1e9;
for(int i=1; i<=n; i++) scanf("%lf%lf", &a[i].x, &a[i].y);
for(int i=1; i<=180; i++){//坐标系旋转
for(int i=1; i<=n; i++) a[i]=rotate(a[i]);
sort(a+1, a+n+1, [](point &a, point &b){return a.x<b.x;});
int l=1, r=n;
while(l<=r){
int mid=(l+r)/2;
double R1=check(1, mid);
double R2=check(mid+1, n);
if(min(R1, R2)>R){//剪枝
break;
}
if(R1<R2){
l=mid+1;
}
else{
r=mid-1;
}
R=min(R, max(R1, R2));
}
}
printf("%.2f\n", R);
}
return 0;
}