ZOJ 1450 Minimal Circle 点集的最小圆覆盖

给定一个点集,求出能覆盖点集内所有点的半径最小的圆。包含点在圆上的情况。个人感觉算是比较麻烦的计算几何模板了。

在网上看了很多解题,大多数都摘抄自《求一个包含点集所有点的最小圆的算法》这篇论文。

论文中提出的算法一共分一下四步:

第 1 步. 在点集中任取 3 点 A , B , C .

第 2 步. 作一个包 含 A , B , C 三点的最小 圆. 圆周可 能通过这 3 点( 如图 1 所示) , 也 可能只通过
其中两点, 但包含第 3 点. 后一种情况圆周上的两点一定是 位于圆的一条直径的两端.

第 3 步. 在点集中找 出距离第 2 步所建圆圆心最远的点 D . 若 D 点已在圆内或圆周上, 则该
圆即为所求的圆, 算法结束. 否则, 执行第 4 步.

第 4 步. 在 A , B , C , D 中 选 3 个点, 使 由它们生成的一个 包含这 4 点的圆为最 小. 这 3 点成
为新的 A , B 和 C , 返回执 行第 2 步 .

若在第 4 步生成的圆的圆周只通过 A , B , C , D 中的两点, 则圆周上的两点取成新的 A 和 B ,
从另两点中任取一点作为新的 C .


但是我读的时候感觉其中最关键的第四步说的最为模糊了。

下面是我自己的一些关于求四边形即四个点的最小圆覆盖的一些想法。

1. 若此四边形中存在大于等于180度的内角时,则可看作求三角形的最小覆盖圆(钝角三角形为以长边做直径的圆,否则为该三角形外接圆)。

2. 若存在两个锐角,两个非锐角,则必为两个锐角顶点连线为直径的圆。

3. 若有三个锐角,一个非锐角,则必为三个锐角顶点组成的三角形的外接圆。

4. 若有四个直角,则任选其中三点组成三角形求其外接圆即可。

解决了第四步,剩下的就是按照上述算法求解了。


下面是AC代码,里面还有一些其他的模板,一直想攒一套完整的模板,所以就没删,求最小点集覆盖圆的函数为 Cal_Min_Circle(P *p,int n)。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define LL long long
#define PI (acos(-1.0))
#define EPS (1e-10)

using namespace std;

struct P
{
    double x,y,a;
}p[1100],cp[1100];

struct L
{
    P p1,p2;
} line[50];

double X_Mul(P a1,P a2,P b1,P b2)
{
    P v1 = {a2.x-a1.x,a2.y-a1.y},v2 = {b2.x-b1.x,b2.y-b1.y};
    return v1.x*v2.y - v1.y*v2.x;
}

double Cal_Point_Dis(P p1,P p2)
{
    return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}

double Cal_Point_Dis_Pow_2(P p1,P p2)
{
    return (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y);
}

P Cal_Segment_Cross_Point(P a1,P a2,P b1,P b2)
{
    double t = X_Mul(b1,b2,b1,a1) / X_Mul(a1,a2,b1,b2);
    P cp = {a1.x+(a2.x-a1.x)*t,a1.y+(a2.y-a1.y)*t};
    return cp;
}

//规范相交
bool Is_Seg_Cross_Without_Point(P a1,P a2,P b1,P b2)
{
    double xm1 = X_Mul(a1,a2,a1,b1);
    double xm2 = X_Mul(a1,a2,a1,b2);
    double xm3 = X_Mul(b1,b2,b1,a1);
    double xm4 = X_Mul(b1,b2,b1,a2);

    return (xm1*xm2 < (-EPS) && xm3*xm4 < (-EPS));
}

//向量ab与X轴正方向的夹角
double Cal_Angle(P t,P p)
{
    return ((t.x-p.x)*(t.x-p.x) + 1 - (t.x-p.x-1)*(t.x-p.x-1))/(2*sqrt((t.x-p.x)*(t.x-p.x) + (t.y-p.y)*(t.y-p.y)));
}

//计算 ∠b2.a.b1 的大小
double Cal_Angle_Three_Point(P a,P b1,P b2)
{
    double l1 = Cal_Point_Dis_Pow_2(b1,a);
    double l2 = Cal_Point_Dis_Pow_2(b2,a);
    double l3 = Cal_Point_Dis_Pow_2(b1,b2);

    return acos( (l1+l2-l3)/(2*sqrt(l1*l2)) );
}

bool cmp_angle(P p1,P p2)
{
    return (p1.a < p2.a || (fabs(p1.a-p2.a) < EPS && p1.y < p2.y) ||(fabs(p1.a-p2.a) < EPS && fabs(p1.y-p2.y) < EPS && p1.x < p2.x)   );
}

//p为点集  应该保证至少有三点不共线
//n 为点集大小
//返回值为凸包上点集上的大小
//凸包上的点存储在cp中
int Creat_Convex_Hull(P *p,int n)
{
    int i,top;
    P re; //re 为建立极角排序时的参考点  下左点

    for(re = p[0],i = 1; i < n; ++i)
    {
        if(re.y > p[i].y || (fabs(re.y-p[i].y) < EPS && re.x > p[i].x))
            re = p[i];
    }

    for(i = 0; i < n; ++i)
    {
        if(p[i].x == re.x && p[i].y == re.y)
        {
            p[i].a = -2;
        }
        else p[i].a = Cal_Angle(re,p[i]);
    }

    sort(p,p+n,cmp_angle);

    top =0 ;
    cp[top++] = p[0];
    cp[top++] = p[1];

    for(i = 2 ; i < n;)
    {
        if(top < 2 || X_Mul(cp[top-2],cp[top-1],cp[top-2],p[i]) > EPS)
        {
            cp[top++] = p[i++];
        }
                    else
        {
            --top;
        }
    }

    return top;
}

bool Is_Seg_Parallel(P a1,P a2,P b1,P b2)
{
    double xm1 = X_Mul(a1,a2,a1,b1);
    double xm2 = X_Mul(a1,a2,a1,b2);

    return (fabs(xm1) < EPS && fabs(xm2) < EPS);
}

bool Point_In_Seg(P m,P a1,P a2)
{
    double l1 = Cal_Point_Dis(m,a1);
    double l2 = Cal_Point_Dis(m,a2);
    double l3 = Cal_Point_Dis(a1,a2);

    return (fabs(l1+l2-l3) < EPS);
}

//计算三角形外接圆圆心
P Cal_Triangle_Circumcircle_Center(P a,P b,P c)
{

    P mp1 = {(b.x+a.x)/2,(b.y+a.y)/2},mp2 = {(b.x+c.x)/2,(b.y+c.y)/2};
    P v1 = {a.y-b.y,b.x-a.x},v2 = {c.y-b.y,b.x-c.x};

    P p1 = {mp1.x+v1.x,mp1.y+v1.y},p2 = {mp2.x+v2.x,mp2.y+v2.y};

    return Cal_Segment_Cross_Point(mp1,p1,mp2,p2);
}

bool Is_Acute_Triangle(P p1,P p2,P p3)
{
    //三点共线
    if(fabs(X_Mul(p1,p2,p1,p3)) < EPS)
    {
        return false;
    }

    double a = Cal_Angle_Three_Point(p1,p2,p3);
    if(a > PI || fabs(a-PI) < EPS)
    {
        return false;
    }
    a = Cal_Angle_Three_Point(p2,p1,p3);
    if(a > PI || fabs(a-PI) < EPS)
    {
        return false;
    }
    a = Cal_Angle_Three_Point(p3,p1,p2);
    if(a > PI || fabs(a-PI) < EPS)
    {
        return false;
    }
    return true;
}

bool Is_In_Circle(P center,double rad,P p)
{
    double dis = Cal_Point_Dis(center,p);
    return (dis < rad || fabs(dis-rad) < EPS);
}

P Cal_Seg_Mid_Point(P p2,P p1)
{
    P mp = {(p2.x+p1.x)/2,(p1.y+p2.y)/2};
    return mp;
}

void Cal_Min_Circle(P *p,int n)
{
    if(n == 0)
        return ;
    if(n == 1)
    {
        printf("%.2lf %.2lf %.2lf\n",p[0].x,p[0].y,0.00);
        return ;
    }
    if(n == 2)
    {
        printf("%.2lf %.2lf %.2lf\n",(p[1].x+p[0].x)/2,(p[0].y+p[1].y)/2,Cal_Point_Dis(p[0],p[1])/2);
        return ;
    }

    P center = Cal_Seg_Mid_Point(p[0],p[1]),tc1;

    double dis,temp,rad = Cal_Point_Dis(p[0],center),tra1;
    int i,site,a = 0,b = 1,c = 2,d,s1,s2,tp;

    for(dis = -1,site = 0,i = 0; i < n; ++i)
    {
        temp = Cal_Point_Dis(center,p[i]);
        if(temp > dis)
        {
            dis = temp;
            site = i;
        }
    }

    while( Is_In_Circle(center,rad,p[site]) == false )
    {
        d = site;
       // printf("a = %d b = %d c = %d d = %d\n",a,b,c,d);

        //printf("x = %.2lf  y = %.2lf\n",center.x,center.y);

       // printf("rad = %.2lf\n\n",rad);
       // getchar();

        double l1 = Cal_Point_Dis(p[a],p[d]);
        double l2 = Cal_Point_Dis(p[b],p[d]);
        double l3 = Cal_Point_Dis(p[c],p[d]);

        if((l1 > l2 || fabs(l1-l2) < EPS) && (l1 > l3 || fabs(l1-l3) < EPS))
        {
            s1 = a,s2 = d,tp = b;
        }
        else if((l2 > l1 || fabs(l1-l2) < EPS) && (l2 > l3 || fabs(l2-l3) < EPS))
        {
            s1 = b,s2 = d,tp = c;
        }
        else
        {
            s1 = c,s2 = d,tp = a;
        }

        center = Cal_Seg_Mid_Point(p[s1],p[s2]);
        rad = Cal_Point_Dis(center,p[s1]);

        if( Is_In_Circle(center,rad,p[a]) == false || Is_In_Circle(center,rad,p[b]) == false || Is_In_Circle(center,rad,p[c]) == false )
        {
            center = Cal_Triangle_Circumcircle_Center(p[a],p[c],p[d]);
            rad = Cal_Point_Dis(p[d],center);
            s1 = a,s2 = c,tp = d;

            if(Is_In_Circle(center,rad,p[b]) == false)
            {
                center = Cal_Triangle_Circumcircle_Center(p[a],p[b],p[d]);
                rad = Cal_Point_Dis(p[d],center);
                s1 = a,s2 = b,tp = d;
            }
            else
            {
                tc1 = Cal_Triangle_Circumcircle_Center(p[a],p[b],p[d]);
                tra1 = Cal_Point_Dis(p[d],tc1);

                if(tra1 < rad && Is_In_Circle(tc1,tra1,p[c]))
                {
                    rad = tra1,center = tc1;
                    s1 = a,s2 = b,tp = d;
                }
            }

            if(Is_In_Circle(center,rad,p[c]) == false)
            {
                center = Cal_Triangle_Circumcircle_Center(p[c],p[b],p[d]);
                rad = Cal_Point_Dis(center,p[d]);
                s1 = c,s2 = b,tp = d;
            }
            else
            {
                tc1 = Cal_Triangle_Circumcircle_Center(p[c],p[b],p[d]);
                tra1 = Cal_Point_Dis(p[d],tc1);
                if(tra1 < rad && Is_In_Circle(tc1,tra1,p[a]))
                {
                    rad = tra1,center = tc1;
                    s1 = b,s2 = c,tp = d;
                }
            }
        }

        a = s1,b = s2,c = tp;

        for(dis = -1,site = 0,i = 0;i < n; ++i)
        {
            temp = Cal_Point_Dis(center,p[i]);
            if(temp > dis)
            {
                dis = temp;
                site = i;
            }
        }
    }
    printf("%.2f %.2f %.2f\n",center.x,center.y,rad);
}

int main()
{
    int i,n;
    while(scanf("%d",&n) && n)
    {
        for(i = 0;i < n; ++i)
        {
            scanf("%lf %lf",&p[i].x,&p[i].y);
        }
        Cal_Min_Circle(p,n);
    }
}

你可能感兴趣的:(计算几何)