圆
基本计算
1.圆的定义
struct Circle{
Point c;
db r;
Circle(){}
Circle(Point _c,db _r):c(_c),r(_r){}
Circle(db x,db y,db _r){c=Point(x,y);r=_r;}
};
2.点和圆的关系
int Point_circle_relation(Point p,Circle C){
db dst = Dist(p,C.c);
if(sgn(dst-C.r)<0)return 0;//在圆内
if(sgn(dst-C.r)==0)return 1;//点在圆上
return 2;//点在圆外
}
3.直线和圆的关系
int line_circle_relation(line v,Circle C){
db dst = Dis_point_line(C.c,v);
if(sgn(dst-C.r)<0)return 0;//直线和圆相交
if(sgn(dst-C.r)==0)return 1;//相切
return 2;//相交
}
4.线段和圆的关系
int Seg_circle_relation(Segment v,Circle C){
db dst = Dis_point_seg(C.c,v);
if(sgn(dst-C.r)<0)return 0;//线段在圆外
if(sgn(dst-C.r)==0)return 1;//相切或有一个点在圆上
return 2; //圆外
}
5.直线和圆的交点
int line_cross_circle(line v,Circle C,Point &pa,Point &pb){
if(line_circle_relation(v,C)==2)return 0;//无交点
Point q=Point_line_proj(C.c,v); //投影点
db d=Dis_point_line(C.c,v);
db k=sqrt(C.r*C.r-d*d);
if(sgn(k)==0){
pa=q;pb=q;return 1;//只有一个交点
}
Point n=(v.p2-v.p1)/len(v.p2-v.p1);
pa=q+n*k;pb=q-n*k;
return 2;//两个交点
}
6.三角形外接圆圆心
即两条中垂线的交点
Point circle_center(const Point a,const Point b,const Point c){
Point center;
db a1=b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2;
db a2=c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2;
db d=a1*b2-a2*b1;
center.x=a.x+(c1*b2-c2*b1)/d;
center.y=a.y+(a1*c2-a2*c1)/d;
return center;
}
最小圆覆盖
最小圆覆盖问题:给定n个点的平面坐标,求一个半径最小的圆,把n个点全部包围,部分点在圆上。
预备知识
1.两点定圆或三点定圆
即两个点作为直径可确定一个圆,或三个点(及以上)组成三角形的外心可确定一个圆。那么我们只需要找到两个或三个点确定的那个满足题意的最小圆即可。
2.最小覆盖圆唯一
3.若\(O1\)为点集\(S\)的最小覆盖圆,增加一个在圆外的点\(p\)后,新点集的最小覆盖圆一定过点\(p\)。
几何算法
假设已经求得前i-1个点的\(C_{i-1}\)(最小覆盖圆),现在加入第i个点,有两种情况。
1)i在\(C_{i-1}\)的内部或圆上,忽略i。
2)i在\(C_{i-1}\)的外部,需要求新的\(C_i\)。首先,i肯定在\(C_i\)上,然后重新把前面的i-1个点依次加入,根据三点或两点定圆重新构造最小圆。
void min_cover_circle(Point *p,int n,Point &c,db &r){
random_shuffle(p,p+n);//打乱
c=p[0];r=0;
for(int i=1;i<n;++i){ //可以理解为已经找到i-1个点的最小覆盖圆
if(sgn(Dist(p[i],c)-r)<0){ //检查第i个点
c=p[i];r=0; //第i个点在圆外,那么新圆一定经过i点,所以以i为圆心然后在向其中加点扩充圆
for(int j=0;j<i;++j){//检查新圆能否包含之前的所有点
//同时可理解为已经找到覆盖j-1个点并且经过i点的最小圆
if(sgn(Dist(p[j],c)-r)>0){
c.x=(p[i].x+p[j].x)/2;
c.y=(p[i].y+p[j].y)/2;
r=Dist(p[j],c);//如果不能覆盖点j,更新这个新圆,且更新后的新圆定会经过点j,
//再加上之前的条件,我们要找的圆定会经过i,j这两个点
for(int k=0;k<j;++k){
//这个循环可理解为覆盖k-1个点并且经过i,j的圆
if(sgn(Dist(p[k],c)-r)>0){
//更新完之后如果仍不能包含之前所有点
//就使用三点定圆,由于一个圆最多被三个点确定(点多了没用),
//所以三点定圆一定能找到满足题意的圆
c=circle_center(p[i],p[j],p[k]);
r=Dist(p[i],c);
}
}
}
}
}
}
}