http://www.lydsy.com/JudgeOnline/problem.php?id=3564
此题很裸,对每个点的坐标稍作处理即可直接套最小圆覆盖算法。最小圆覆盖的细节不需多说,我只想讲一下怎么求三角形外接圆的圆心
以下斜体高亮内容来自网上,作者不知,在此表示歉意。
已知三角形三个点p0、p1、p2的坐标,求外接圆圆心O的坐标
列出方程
sqr(O.x-p0.x)+sqr(O.y-p0.y) = sqr(r); (1)
sqr(O.x-p0.x)+sqr(O.y-p0.y) = sqr(r); (2)
sqr(O.x-p0.x)+sqr(O.y-p0.y) = sqr(r); (3)
联立(1)(2)得
(p1.x-p0.x)*O.x+(p1.y-p0.y)*O.y = (sqr(p1.x)+sqr(p1.y)-sqr(p0.x)-sqr(p0.y))/2; (4)
联立(1)(3)的
(p2.x-p0.x)*O.x+(p2.y-p0.y)*O.y = (sqr(p2.x)+sqr(p2.y)-sqr(p0.x)-sqr(p0.y))/2; (5)
令O.x = p0.x+x;
O.y = p0.y+y;
带入(4)(5)后化简得到
(p1.x-p0.x)*x+(p1.y-p0.y)*y = (sqr(p1.x-p0.x)+sqr(p1.y-p0.y))/2;
(p2.x-p0.x)*x+(p2.y-p0.y)*y = (sqr(p2.x-p0.x)+sqr(p2.y-p0.y))/2;
分别解得(x,y)
p0+(x,y) = O;
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include <cmath> #define MAXN 51000 #define PI (3.1415926535897384626) #define EPS (1e-10) using namespace std; struct Point //保存坐标系上的点 { double x,y; Point(double xx=0,double yy=0) { x=xx; y=yy; } Point operator-(Point b) { return Point(x-b.x,y-b.y); } Point operator+(Point b) { return Point(x+b.x,y+b.y); } double operator*(Point b) //点积 { return x*b.y-y*b.x; } Point operator*(double t) //数乘 { return Point(x*t,y*t); } Point operator/(double t) //数除 { return Point(x/t,y/t); } Point rotate(double ang) //绕原点顺时针旋转ang度 { return Point(x*cos(ang)+y*sin(ang),-x*sin(ang)+y*cos(ang)); } Point verl() //逆时针旋转90度后的坐标 { return Point(-y,x); } }dots[MAXN]; int n; int dcmp(double x) //三态函数 { if(fabs(x)<EPS) return 0; if(x>EPS) return 1; return -1; } double dist(Point a,Point b) //求点a到点b距离 { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } Point Circumcenter(Point a,Point b,Point c) // { double a1=b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2; double a2=c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2; double d=a1*b2-a2*b1; return Point(a.x+(c1*b2-c2*b1)/d,a.y+(a1*c2-a2*c1)/d); } double X,Y,R; //最小覆盖圆的圆心是(X,Y),圆的半径是R void MinCoverCir() //随机增量算法求最小圆覆盖 { random_shuffle(dots+1,dots+n+1); Point center=dots[1]; R=0; for(int i=2;i<=n;i++) if(dist(center,dots[i])+EPS>R) //点i在当前的最小覆盖圆外面 { center=dots[i]; R=0; for(int j=1;j<i;j++) if(dist(center,dots[j])+EPS>R) { center.x=(dots[i].x+dots[j].x)/2; center.y=(dots[i].y+dots[j].y)/2; R=dist(center,dots[j]); //半径是圆心到第j个点之间的距离 for(int k=1;k<j;k++) if(dist(center,dots[k])+EPS>R) //k在当前的最小覆盖圆外面 { center=Circumcenter(dots[i],dots[j],dots[k]); //i、j、k三点确定最小覆盖圆 R=dist(center,dots[k]); } } } } int main() { double ang,scale; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lf%lf",&dots[i].x,&dots[i].y); scanf("%lf%lf",&ang,&scale); ang=ang/180*PI; //转为弧度制 for(int i=1;i<=n;i++) { dots[i]=dots[i].rotate(ang); dots[i].x/=scale; } MinCoverCir(); printf("%.3lf\n",R); return 0; }