【BZOJ1336】[Balkan2002]Alien最小圆覆盖
Description
给出N个点,让你画一个最小的包含所有点的圆。
Input
先给出点的个数N,2<=N<=100000,再给出坐标Xi,Yi.(-10000.0<=xi,yi<=10000.0)
Output
输出圆的半径,及圆心的坐标
Sample Input
6
8.0 9.0
4.0 7.5
1.0 2.0
5.1 8.7
9.0 2.0
4.5 1.0
8.0 9.0
4.0 7.5
1.0 2.0
5.1 8.7
9.0 2.0
4.5 1.0
Sample Output
5.00
5.00 5.00
5.00 5.00
题解:特地学了一发随机增量法,期望复杂度据说是O(n),但不会证。
起初以1为圆心。先枚举一个点,如果这个点在圆外,则改为以哪个点为圆心;再枚举一个点,如果这个点再圆外,则圆心改为这两个点的中点;再枚举一个点,如果这个点再圆外,则将圆心改为三角形的内心。如何求内心?本人懒得推式子所以写的高斯消元。
由于是期望复杂度所以一开始要将原序列随机排序。
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <algorithm> using namespace std; const int maxn=500010; const double eps=1e-12; struct point { double x,y; point() {} point(double a,double b){x=a,y=b;} }p[maxn],O; int n; double v[10][10],R; point calc(point a,point b,point c) { v[1][1]=2*b.x-2*a.x; v[1][2]=2*b.y-2*a.y; v[1][3]=b.x*b.x-a.x*a.x+b.y*b.y-a.y*a.y; v[2][1]=2*c.x-2*b.x; v[2][2]=2*c.y-2*b.y; v[2][3]=c.x*c.x-b.x*b.x+c.y*c.y-b.y*b.y; int i,j,k; for(i=1;i<=2;i++) { for(j=i+1;j<=2;j++) if(fabs(v[j][i])>fabs(v[i][i])) for(k=i;k<=3;k++) swap(v[i][k],v[j][k]); double tmp=v[i][i]; for(k=i;k<=3;k++) v[i][k]/=tmp; for(j=1;j<=2;j++) if(i!=j) for(tmp=v[j][i],k=i;k<=3;k++) v[j][k]-=tmp*v[i][k]; } point ret(v[1][3],v[2][3]); return ret; } double dis(point a,point b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int main() { scanf("%d",&n); int i,j,k; for(i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); random_shuffle(p+1,p+n+1); O=p[1],R=0; for(i=2;i<=n;i++) if(dis(O,p[i])>R+eps) { O=p[i],R=0; for(j=1;j<i;j++) if(dis(O,p[j])>R+eps) { O=point((p[i].x+p[j].x)/2,(p[i].y+p[j].y)/2),R=dis(O,p[i]); for(k=1;k<j;k++) if(dis(O,p[k])>R+eps) O=calc(p[i],p[j],p[k]),R=dis(O,p[k]); } } printf("%.2lf %.2lf %.2lf",R,O.x,O.y); return 0; }