题目链接

题面:

题解:
求最小圆覆盖。
随机增量法,期望复杂度O(n)。
先将所有的点随机打乱。
定理1: 如果点p不在集合S的最小覆盖圆内,则p一定在S∪{p}的最小覆盖圆上。

根据这个定理,我们可以分三个for确定前i个点的最小覆盖圆。

1.令前i-1个点的最小覆盖圆为C
2.如果第i个点在C内(包括边界),则前i个点的最小覆盖圆也是C
3.如果不在,那么第i个点一定在前i个点的最小覆盖圆上,接着确定前i−1个点中还有哪两个在最小覆盖圆上。因此,设当前圆心为Pi,半径为0,做固定了第i个点的前i个点的最小圆覆盖。
4.固定了一个点–去找第二个点:不停地在范围内找到第一个不在当前最小圆上的点Pj ,当前圆心为(Pi+Pj)/2半径为1/2 | Pi Pj | 做固定了两个点的,前 j 个点外加第 i 个点的最小圆覆盖。
5.固定了两个点–去找第三个点:不停地在范围内找到第一个不在当前最小圆上的点Pk,当前圆为Pi , Pj , Pk 的外接圆。

用random_shuffle随机打乱会wa两个点。。。
真感谢知乎上见到的O(n)随机数列。

从后往前遍历,对于每一个 p [ i ],随机与它前面的一个数交换。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#define ll long long
#define llu unsigned ll
using namespace std;

const double eps=1e-8;
const double dnf=1e20;
const double pi=acos(-1.0);
const int maxp=100100;

//浮点型数值是否为0
int sgn(double x)
{
    if(abs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}


//返回x的平方
double sqr(double x)
{
    return x*x;
}

//二维点
struct Point
{
    double  x,y;
    Point(){}
    Point(double xx,double yy)
    {
        x=xx,y=yy;
    }

    //输入输出
    void input(void)
    {
        scanf("%lf%lf",&x,&y);
    }
    void output(void)
    {
        printf("%.2f %.2f\n",x,y);
    }

    //重载比较运算符
    bool operator == (const Point &b) const
    {
        return sgn(x-b.x)==0&&sgn(y-b.y)==0;
    }

    bool operator < (const Point &b) const
    {
        if(sgn(x-b.x)!=0) return x<b.x;
        else return sgn(y-b.y)<0;
    }

    //重载加减乘除
    Point operator + (const Point &b) const
    {
        return Point(x+b.x,y+b.y);
    }

    Point operator - (const Point &b) const
    {
        return Point(x-b.x,y-b.y);
    }

    Point operator * (const double &k) const
    {
        return Point(x*k,y*k);
    }

    Point operator / (const double &k) const
    {
        return Point(x/k,y/k);
    }

    //叉乘,叉积
    double operator ^ (const Point &b) const
    {
        return x*b.y-y*b.x;
    }
    //点乘,点积
    double operator * (const Point &b) const
    {
        return x*b.x+y*b.y;
    }

    //长度
    double len(void)
    {
        return hypot(x,y);
    }

    //长度的平方
    double len2(void)
    {
        return x*x+y*y;
    }

    //两点距离
    double dis(const Point &b) const
    {
        return hypot(x-b.x,y-b.y);
    }

    //计算pa,pb的夹角,就是这个点看a,b形成的角度
    double rad(const Point &a,const Point &b)const
    {
        Point p=*this;
        return abs(atan2(abs((a-p)^(b-p)),(a-p)*(b-p)));
    }

    //化向量长度为r
    Point turn_to_r(double r)
    {
        double l=len();
        if(!sgn(l)) return *this;
        r/=l;
        return Point(x*r,y*r);
    }

    //逆时针转90
    Point turn_left(void)
    {
        return Point(-y,x);
    }

    //顺时针转90
    Point turn_right(void)
    {
        return Point(y,-x);
    }

    //绕p点逆时针转angle
    Point turn_p_angle(const Point &p,double angle)
    {
        Point v=(*this)-p;
        double c=cos(angle),s=sin(angle);
        return Point(p.x+v.x*c-v.y*s,p.y+v.x*s+v.y*c);
    }

};

//****************************************************************
//****************************************************************

struct Line
{
    Point s,e;
    Line(){}

    //两点
    Line(const Point ss,const Point ee)
    {
        s=ss,e=ee;
    }

    //点斜
    Line(const Point p,const double angle)
    {
        s=p;
        if(sgn(angle-pi/2)==0)
            e=(s+Point(0,1));
        else e=(s+Point(1,tan(angle)));
    }

    //ax+by+c=0
    Line(double a,double b,double c)
    {
        if(sgn(a)==0)
        {
            s=Point(0,-c/b);
            e=Point(1,-c/b);
        }
        else if(sgn(b)==0)
        {
            s=Point(-c/a,0);
            e=Point(-c/a,1);
        }
        else
        {
            s=Point(0,-c/b);
            e=Point(1,(-c-a)/b);
        }
    }

    void input(void)
    {
        s.input();
        e.input();
    }

    void output(void)
    {
        s.output();
        e.output();
    }

    //两点应该是由s——>e,
    //由x负向指向x正向。
    void adjust(void)
    {
        if(e<s) swap(s,e);
    }

    //判断两线段是否重合
    bool operator == (Line b)
    {
        adjust(),b.adjust();
        return (s==b.s)&&(e==b.e);
    }

    //求线段长度
    double len(void)
    {
        return s.dis(e);
    }

    //返回直线倾斜角
    //0<=angle<pi
    double angle(void)
    {
        double k=atan2(e.y-s.y,e.x-s.x);
        if(sgn(k)<0) k+=pi;
        if(sgn(k-pi)==0) k-=pi;
        return k;
    }

    //点和直线的关系
    //1 在左侧 (在s——>e的逆时针侧)
    //2 在右侧 (在s——>e的顺时针侧)
    //3 在直线上
    int relation(const Point &p)
    {
        int c=sgn((p-s)^(e-s));
        if(c<0) return 1;
        else if(c>0) return 2;
        else return 3;
    }

    //点是否在线段上
    bool p_is_on_seg(const Point &p)
    {
        return sgn((p-s)^(e-s))==0&&sgn((p-s)*(p-e))<=0;
    }

    //两向量平行,对应直线平行或者重合
    bool parallel(const Line &v)
    {
        return sgn((e-s)^(v.e-v.s))==0;
    }

    //两线段相交判断
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int seg_cross_seg(const Line &v)
    {
        int d1=sgn((e-s)^(v.s-s));
        int d2=sgn((e-s)^(v.e-s));
        int d3=sgn((v.e-v.s)^(s-v.s));
        int d4=sgn((v.e-v.s)^(e-v.s));
        if((d1^d2)==-2&&(d3^d4)==-2) return 2;
        d1=(d1==0&&sgn((v.s-s)*(v.s-e))<=0);
        d2=(d2==0&&sgn((v.e-s)*(v.e-e))<=0);
        d3=(d3==0&&sgn((s-v.s)*(s-v.e))<=0);
        d4=(d4==0&&sgn((e-v.s)*(e-v.e))<=0);
        return d1||d2||d3||d4;
    }

    //直线和线段相交
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int line_cross_seg(const Line &v)
    {
        int d1=sgn((e-s)^(v.s-s));
        int d2=sgn((e-s)^(v.e-s));
        if(d1^d2==-2) return 2;
        return (d1==0||d2==0);
    }

    //两直线的关系
    //0 平行
    //1 重合
    //2 相交
    int line_cross_line(Line &v)
    {
        if((*this).parallel(v))
            return v.relation(s)==3;
        return 2;
    }

    //求两直线的交点
    //要保证两直线不平行或重合
    Point cross_point(Line v)
    {
        double a1=(v.e-v.s)^(s-v.s);
        double a2=(v.e-v.s)^(e-v.s);
        return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
    }

    //点到直线的距离
    double dis_point_to_line(const Point &p)
    {
        return abs((p-s)^(e-s))/len();
    }

    //点到线段的距离
    double dis_point_to_seg(const Point &p)
    {
        if(sgn((p-s)*(e-s))<0||sgn((p-e)*(s-e))<0)
            return min(p.dis(s),p.dis(e));
        return dis_point_to_line(p);
    }

    //返回线段到线段的距离
    //前提是两线段不相交,相交就是0了
    double dis_seg_to_seg(Line &v)
    {
        double s1=min(dis_point_to_seg(v.s),dis_point_to_seg(v.e));
        double s2=min(v.dis_point_to_seg(s),v.dis_point_to_seg(e));
        return min(s1,s2);
    }

    //返回点p在直线上的投影
    Point point_proj_line(Point &p)
    {
        return s+(((e-s)*((e-s)*(p-s)))/((e-s).len2()));
    }

    //返回点p关于直线的对称点
    Point point_sym_line(Point &p)
    {
        Point q=point_proj_line(p);
        return Point(2*q.x-p.x,2*q.y-p.y);
    }
};

//****************************************************************
//****************************************************************

struct circle
{
    Point p;
    double r;
    circle(){}
    circle(Point pp,double rr)
    {
        p=pp,r=rr;
    }
    circle(double xx,double yy,double rr)
    {
        p=Point(xx,yy);
        r=rr;
    }

    //三角形外接圆
    //利用两条边的中垂线得到圆心
    circle(Point a,Point b,Point c)
    {
        Line u=Line((a+b)/2,((a+b)/2)+((b-a).turn_left()));
        Line v=Line((b+c)/2,((b+c)/2)+((c-b).turn_left()));
        p=u.cross_point(v);
        r=p.dis(a);
    }

    //三角形内切圆,bool t只是为了重载
    circle(Point a,Point b,Point c,bool t)
    {
        Line u,v;
        double m=atan2(b.y-a.y,b.x-a.x),n=atan2(c.y-a.y,c.x-a.x);
        u.s=a;
        u.e=u.s+Point(cos((n+m)/2),sin((n+m)/2));
        m=atan2(a.y-b.y,a.x-b.x),n=atan2(c.y-b.y,c.x-b.x);
        v.s=b;
        v.e=v.s+Point(cos((n+m)/2),sin((n+m)/2));
        p=u.cross_point(v);
        r=Line(a,b).dis_point_to_seg(p);
    }

    circle max(circle b)
    {
        if(r>b.r) return *(this);
        return b;
    }

    void input(void)
    {
        p.input();
        scanf("%lf",&r);
    }

    void output(void)
    {
        printf("%.2f %.2f %.2f\n",p.x,p.y,r);
    }

    bool operator == (const circle &v)
    {
        return (p==v.p)&&sgn(r-v.r)==0;
    }

    bool operator <(const circle &v) const
    {
        return ((p<v.p)||((p==v.p)&&sgn(r-v.r)<0));
    }

    double area(void)
    {
        return pi*r*r;
    }

    double cir(void)
    {
        return 2*pi*r;
    }

    //点和圆的关系,
    //0 圆外
    //1 圆上
    //2 圆内
    int rel_of_point(const Point &b)
    {
        double dist=b.dis(p);
        if(sgn(dist-r)<0) return 2;
        if(sgn(dist-r)==0) return 1;
        return 0;
    }

    //线段和圆的关系
    //比较的是圆心到线段的距离和半径的关
    int rel_of_seg(Line &v)
    {
        double dist=v.dis_point_to_seg(p);
        if(sgn(dist-r)<0) return 2;
        if(sgn(dist-r)==0) return 1;
        return 0;
    }

    //直线和圆的关系
    //比较的是圆心到直线的距离和半径的关系
    int rel_of_line(Line &v)
    {
        double dist=v.dis_point_to_line(p);
        if(sgn(dist-r)<0) return 2;
        if(sgn(dist-r)==0) return 1;
        return 0;
    }
    //两圆的关系
    //5 相离
    //4 外切
    //3 相交
    //2 内切
    //1 内含
    int rel_of_circle(const circle &v)
    {
        double d=p.dis(v.p);
        if(sgn(d-r-v.r)>0) return 5;
        if(sgn(d-r-v.r)==0) return 4;
        double l=abs(r-v.r);
        if(sgn(d-r-v.r)<0&&sgn(d-l)>0) return 3;
        if(sgn(d-l)==0) return 2;
        if(sgn(d-l)<0) return 1;
    }

    //求两个圆的交点,
    //0 表示没有交点,
    //1 是一个交点,
    //2 是两个交点
    int point_two_circle(const circle &v,Point &p1,Point &p2)
    {
        int rel=rel_of_circle(v);
        if(rel==1||rel==5) return 0;
        double d=p.dis(v.p);
        double l=(d*d+r*r-v.r*v.r)/(2*d);
        double h=sqrt(r*r-l*l);
        Point tmp=p+(v.p-p).turn_to_r(l);
        p1=tmp+((v.p-p).turn_left().turn_to_r(h));
        p2=tmp+((v.p-p).turn_right().turn_to_r(h));
        if(rel==2||rel==4) return 1;
        return 2;
    }

    //求直线和圆的交点,返回交点个数
    int point_line_circle(Line &v,Point &p1,Point &p2)
    {
        if(!(*this).rel_of_line(v)) return 0;
        Point a=v.point_proj_line(p);
        double d=v.dis_point_to_line(p);
        d=sqrt(r*r-d*d);
        if(sgn(d)==0)
        {
            p1=a;
            p2=a;
            return 1;
        }
        p1=a+(v.e-v.s).turn_to_r(d);
        p2=a-(v.e-v.s).turn_to_r(d);
        return 2;
    }

    //得到过 a,b 两点,半径为 r1 的两个圆
    int circle_by_point_point_r(Point a,Point b,double r1,circle &c1,circle &c2)
    {
        circle x(a,r1),y(b,r1);
        int t=x.point_two_circle(y,c1.p,c2.p);
        if(!t) return 0;
        c1.r=c2.r=r;
        return t;
    }

    //得到与直线 u 相切,过点 q, 半径为 r1 的圆
    int circle_by_line_point_r(Line u,Point q,double r1,circle &c1,circle &c2)
    {
        double dis=u.dis_point_to_line(q);
        if(sgn(dis-r1*2)>0) return 0;
        if(sgn(dis)==0)
        {
            c1.p=q+((u.e-u.s).turn_left().turn_to_r(r1));
            c2.p=q+((u.e-u.s).turn_right().turn_to_r(r1));
            c1.r=c2.r=r1;
            return 2;
        }
        Line u1=Line((u.s+(u.e-u.s).turn_left().turn_to_r(r1)),(u.e+(u.e-u.s).turn_left().turn_to_r(r1)));
        Line u2=Line((u.s+(u.e-u.s).turn_right().turn_to_r(r1)),(u.e+(u.e-u.s).turn_right().turn_to_r(r1)));
        circle cc=circle(q,r1);
        Point p1,p2;
        if(!cc.point_line_circle(u1,p1,p2)) cc.point_line_circle(u2,p1,p2);
        c1=circle(p1,r1);
        if(p1==p2)
        {
            c2=c1;
            return 1;
        }
        c2=circle(p2,r1);
        return 2;
    }

    //同时与直线 u,v 相切,半径为 r1 的圆
    int circle_by_line_line_r(Line u,Line v,double r1,circle &c1,circle &c2,circle &c3,circle &c4)
    {
        if(u.parallel(v)) return 0;
        Line u1=Line(u.s+(u.e-u.s).turn_left().turn_to_r(r1),u.e+(u.e-u.s).turn_left().turn_to_r(r1));
        Line u2=Line(u.s+(u.e-u.s).turn_right().turn_to_r(r1),u.e+(u.e-u.s).turn_right().turn_to_r(r1));
        Line v1=Line(v.s+(v.e-v.s).turn_left().turn_to_r(r1),v.e+(v.e-v.s).turn_left().turn_to_r(r1));
        Line v2=Line(v.s+(v.e-v.s).turn_right().turn_to_r(r1),v.e+(v.e-v.s).turn_right().turn_to_r(r1));

        c1.r=c2.r=c3.r=c4.r=r1;
        c1.p=u1.cross_point(v1);
        c2.p=u1.cross_point(v2);
        c3.p=u2.cross_point(v1);
        c4.p=u2.cross_point(v2);
        return 4;
    }

    //同时与不相交圆 cx,cy 相切,半径为 r1 的圆
    int circle_by_circle_circle_r(circle cx,circle cy,double r1,circle &c1,circle &c2)
    {
        circle x(cx.p,r1+cx.r),y(cy.p,r1+cy.r);
        int t=x.point_two_circle(y,c1.p,c2.p);
        if(!t) return 0;
        c1.r=c2.r=r1;
        return t;
    }

    //过一点作圆的切线 (先判断点和圆的关系)
    int tangentline(Point q,Line &u,Line &v)
    {
        int x=rel_of_point(q);
        if(x==2) return 0;
        if(x==1)
        {
            u=Line(q,q+(q-p).turn_left());
            v=u;
            return 1;
        }
        double d=p.dis(q);
        double l=r*r/d;
        double h=sqrt(r*r-l*l);
        u=Line(q,p+((q-p).turn_to_r(l)+(q-p).turn_left().turn_to_r(h)));
        v=Line(q,p+((q-p).turn_to_r(l)+(q-p).turn_right().turn_to_r(h)));
        return 2;
    }

    //求两圆相交的面积
    double area_circle(circle v)
    {
        int rel=rel_of_circle(v);
        if(rel>=4) return 0.0;
        if(rel<=2) return min(area(),v.area());
        double d=p.dis(v.p);
        double hf=(r+v.r+d)/2.0;
        double ss=2*sqrt(hf*(hf-r)*(hf-v.r)*(hf-d));
        double a1=acos((r*r+d*d-v.r*v.r)/(2.0*r*d));
        a1=a1*r*r;
        double a2=acos((v.r*v.r+d*d-r*r)/(2.0*v.r*d));
        a2=a2*v.r*v.r;
        return a1+a2-ss;
    }

    //求圆和三角形 pab 的相交面积
    double area_triangle(Point a,Point b)
    {
        if(sgn((p-a)^(p-b))==0) return 0.0;
        Point q[10];
        int len=0;
        q[len++]=a;
        Line l(a,b);
        if(point_line_circle(l,q[1],q[2])==2)
        {
            if(sgn((a-q[1])*(b-q[1]))<0) q[len++]=q[1];
            if(sgn((a-q[2])*(b-q[2]))<0) q[len++]=q[2];
        }
        q[len++]=b;
        if(len==4&&sgn((q[0]-q[1])*(q[2]-q[1]))>0) swap(q[1],q[2]);
        double res=0;
        for(int i=0;i<len-1;i++)
        {
            if(rel_of_point(q[i])==0||rel_of_point(q[i+1])==0)
            {
                double arg=p.rad(q[i],q[i+1]);
                res+=r*r*arg/2.0;
            }
            else res+=abs((q[i]-p)^(q[i+1]-p))/2.0;
        }
        return res;
    }

};

//****************************************************************
//****************************************************************

struct polygon
{
    int n;
    Point p[maxp];
    Line l[maxp];

    void input(int nn)
    {
        n=nn;
        for(int i=0;i<n;i++)
            p[i].input();
    }

    void add(Point q)
    {
        p[n++]=q;
    }

    void getline(void)
    {
        for(int i=0;i<n;i++)
            l[i]=Line(p[i],p[(i+1)%n]);
    }

    struct cmp
    {
        Point p;
        cmp(const Point &p0) { p=p0;}
        bool operator()(const Point &aa,const Point &bb)
        {
            Point a=aa,b=bb;
            int d=sgn((a-p)^(b-p));
            if(d==0)
                return sgn(a.dis(p)-b.dis(p))<0;
            return d>0;
        }
    };

    //进行极角排序
    //首先需要找到最左下角的点
    //需要重载号好 Point 的 < 操作符 (min 函数要用)
    void norm(void)
    {
        Point mi=p[0];
        for(int i=1;i<n;i++) mi=min(mi,p[i]);
        sort(p,p+n,cmp(mi));
    }

    //得到凸包
    //得到的凸包里面的点编号是 0∼n-1 的
    //两种凸包的方法
    //注意如果有影响,要特判下所有点共点,或者共线的特殊情况
    void get_convex(polygon &convex)
    {
        sort(p,p+n);
        convex.n=n;
        for(int i=0;i<min(n,2);i++)
            convex.p[i]=p[i];
        if(convex.n==2&&(convex.p[0]==convex.p[1])) convex.n--;
        if(n<=2) return ;
        int &top=convex.n;
        top=1;
        for(int i=2;i<n;i++)
        {
            while(top&&sgn((convex.p[top]-p[i])^(convex.p[top-1]-p[i]))<=0)
                top--;
            convex.p[++top]=p[i];
        }
        int temp=top;
        convex.p[++top]=p[n-2];
        for(int i=n-3;i>=0;i--)
        {
            while(top!=temp&&sgn((convex.p[top]-p[i])^(convex.p[top-1]-p[i]))<=0)
                top--;
            convex.p[++top]=p[i];
        }
        if(convex.n==2&&(convex.p[0]==convex.p[1])) convex.n--;
        convex.norm();//原来得到的是顺时针的点,排序后逆时针

    }

    //得到凸包的另外一种方法
    void get_convex_graham(polygon &convex)
    {
        norm();
        int &top=convex.n;
        top=0;
        if(n==1)
        {
            top=1;
            convex.p[0]=p[0];
            return ;
        }
        if(n==2)
        {
            top=2;
            convex.p[0]=p[0];
            convex.p[1]=p[1];
            if(convex.p[0]==convex.p[1]) top--;
            return ;
        }

        convex.p[0]=p[0];
        convex.p[1]=p[1];
        top=2;
        for(int i=2;i<n;i++)
        {
            while(top>1&&sgn((convex.p[top-1]-convex.p[top-2])^(p[i]-convex.p[top-2]))<=0)
                top--;
            convex.p[top++]=p[i];
        }
        if(convex.n==2&&(convex.p[0]==convex.p[1])) convex.n--;
    }

    //判断是不是凸的
    bool is_convex(void)
    {
        bool s[8];
        memset(s,false,sizeof(s));
        for(int i=0;i<n;i++)
        {
            int j=(i+1)%n;
            int k=(j+1)%n;
            s[sgn((p[j]-p[i])^(p[k]-p[i]))+1]=true;
            if(s[0]&&s[2]) return false;
        }
        return true;
    }

    //判断点和任意多边形的关系
    // 3 点上
    // 2 边上
    // 1 内部
    // 0 外部
    int rel_of_point(Point q)
    {
        for(int i=0;i<n;i++)
            if(p[i]==q) return 3;
        getline();
        for(int i=0;i<n;i++)
            if(l[i].p_is_on_seg(q)) return 2;
        int cnt=0;
        for(int i=0;i<n;i++)
        {
            int j=(i+1)%n;
            int k=sgn((q-p[j])^(p[i]-p[j]));
            int u=sgn(p[i].y-q.y);
            int v=sgn(p[j].y-q.y);
            if(k>0&&u<0&&v>=0) cnt++;
            if(k<0&&v<0&&u>=0) cnt--;
        }
        return cnt!=0;
    }

    //直线 u 切割凸多边形左侧
    //注意直线方向
    void convex_cut_line(Line u,polygon &po)
    {
        int &top=po.n;
        top=0;
        for(int i=0;i<n;i++)
        {
            int d1=sgn((u.e-u.s)^(p[i]-u.s));
            int d2=sgn((u.e-u.s)^(p[(i+1)%n]-u.s));
            if(d1>=0) po.p[top++]=p[i];
            if(d1*d2<0) po.p[top++]=u.cross_point(Line(p[i],p[(i+1)%n]));
        }
    }

    //得到周长
    double get_cir(void)
    {
        double sum=0;
        for(int i=0;i<n;i++)
            sum+=p[i].dis(p[(i+1)%n]);
        return sum;
    }

    //得到面积
    double get_area(void)
    {
        double sum=0;
        for(int i=0;i<n;i++)
            sum+=(p[i]^p[(i+1)%n]);
        return abs(sum)/2;
    }

    //得到方向
    //1 逆时针
    //2顺时针
    bool get_dir(void)
    {
        double sum=0;
        for(int i=0;i<n;i++)
            sum+=(p[i]^p[(i+1)%n]);
        if(sgn(sum)>0) return 1;
        return 0;
    }

    //得到重心
    Point get_bary_centre(void)
    {
        Point ret(0,0);
        double area=0.0;
        for(int i=1;i<n-1;i++)
        {
            double tmp=(p[i]-p[0])^(p[i+1]-p[0]);
            if(sgn(tmp)==0) continue;
            area+=tmp;
            ret.x+=(p[0].x+p[i].x+p[i+1].x)/3*tmp;
            ret.y+=(p[0].y+p[i].y+p[i+1].y)/3*tmp;
        }
        if(sgn(area)) ret=ret/area;
        return ret;
    }

    //多边形和圆交的面积
    double area_circle(circle c)
    {
        double ans=0;
        for(int i=0;i<n;i++)
        {
            int j=(i+1)%n;
            if(sgn((p[j]-c.p)^(p[i]-c.p))>=0)
                ans+=c.area_triangle(p[i],p[j]);
            else ans-=c.area_triangle(p[i],p[j]);
        }
        return abs(ans);
    }

    //多边形和圆关系
    // 2 圆完全在多边形内
    // 1 圆在多边形里面,碰到了多边形边界
    // 0 其它
    int rel_of_circle(circle c)
    {
        getline();
        int x=2;
        if(rel_of_point(c.p)!=1) return 0;
        for(int i=0;i<n;i++)
        {
            if(c.rel_of_seg(l[i])==2) return 0;
            if(c.rel_of_seg(l[i])==1) x=1;
        }
        return x;
    }
    void random(void)
    {
        srand(19981031);
        for(int i=n-1;i>0;i--)
            swap(p[i],p[rand()%i]);
    }
    //最小圆覆盖
    circle smallest_circle(void)
    {
        random();
        circle cir(Point(0,0),0.0);
        for(int i=0;i<n;i++)
        {
            if(cir.rel_of_point(p[i])>0) continue;
            //如果不在,那么第i个点一定在前i个点的最小覆盖圆上,
            //接着确定前i−1个点中还有哪两个在最小覆盖圆上。
            //因此,设当前圆心为Pi,半径为0,做固定了第i个点的
            //前i个点的最小圆覆盖。
            cir=circle(p[i],0.0);
            for(int j=0;j<i;j++)
            {
                if(cir.rel_of_point(p[j])>0) continue;
                //固定了一个点--去找第二个点:
                //不停地在范围内找到第一个不在当前最小圆上的点Pj ,
                //当前圆心为(Pi+Pj)/2半径为1/2 | Pi Pj | 做固定了两个点的,
                //前 j 个点外加第 i 个点的最小圆覆盖。
                cir=circle((p[i]+p[j])/2,p[j].dis((p[i]+p[j])/2));
                for(int k=0;k<j;k++)
                {
                    if(cir.rel_of_point(p[k])>0) continue;
                    //固定了两个点--去找第三个点:
                    //不停地在范围内找到第一个不在当前最小圆上的点Pk,
                    //当前圆为Pi , Pj , Pk 的外接圆。
                    cir=circle(p[i],p[j],p[k]);
                }

            }
        }
        return cir;
    }

};



int main()
{
    polygon pp;
    int n;
    scanf("%d",&n);
    pp.input(n);
    circle cir=pp.smallest_circle();
    printf("%.10f\n%.10f %.10f\n",cir.r,cir.p.x,cir.p.y);
    return 0;
}