这篇总结可能很长 - - 需要一点点的分析。
首先,这题最大的收获:
关于eps的宏定义,并不一定总是一个固定值,1e-10之类。。这个浮点常量也不一定是越小越好。。
对于这个题来讲,const double eps = 1E-6; const double eps = 1E-5; const double eps = 1E-4; 甚至 const double eps = 1E-1; 都可以AC。
再小一点就不行了~
接着,说一下这个:
struct Line { Point p; Vector v; Line(Point p, Vector v):p(p),v(v) { } Point point(double t) { return p + v*t; } Line move(double d) { return Line(p + Normal(v)*d, v); } };
Line可以用一点p和这条直线的方向向量v来唯一确定;
其中,move()函数的功能是,对直线进行平移d个单位,函数返回值也是直线;
具体来讲,对于直线L1,L1是由点p和方向向量v确定,向量v的法线向量为Normal(v),
那么通过点p且与L1垂直直线方程可以表示为:p+Normal(v)*t,当t=d时,就是p1点的坐标p+Normal(v)*d。
此时,L2的就可以用点p1和方向向量v来表示了
接下来程序的六个主要模块:
给定三角形三个顶点坐标,求三角形外接圆的圆心坐标与半径
/******************* Problem 1 **********************/ Circle CircumscribedCircle(Point p1, Point p2, Point p3) { double Bx = p2.x-p1.x, By = p2.y-p1.y; double Cx = p3.x-p1.x, Cy = p3.y-p1.y; double D = 2*(Bx*Cy-By*Cx); double cx = (Cy*(Bx*Bx+By*By) - By*(Cx*Cx+Cy*Cy))/D + p1.x; double cy = (Bx*(Cx*Cx+Cy*Cy) - Cx*(Bx*Bx+By*By))/D + p1.y; Point p = Point(cx, cy); return Circle(p, Length(p1-p)); }
这个是真没推出来,推出来的是这个:
记三角形的定点为(Ax, Ay), (Bx, By), (Cx, Cy),
外接圆的圆心为(Rx,Ry),
由于圆心到3个定点的距离相等. 因此有:
(Ax-Rx)^2 + (Ay-Ry)^2 = (Bx-Rx)^2 + (By-Ry)^2 (1)
(Ax-Rx)^2 + (Ay-Ry)^2 = (Cx-Rx)^2 + (Cy-Ry)^2 (2)
由(1)式得:Ax^2 + Ay^2 - 2*Ax*Rx - 2*Ay*Ry = Bx^2 + By^2 - 2*Bx*Rx - 2*By*Ry (3)
由(2)式得:Ax^2 + Ay^2 - 2*Ax*Rx - 2*Ay*Ry = Cx^2 + Cy^2 - 2*Cx*Rx - 2*Cy*Ry (4)
由(3)式得:Rx = (Ax^2 + Ay^2 - Bx^2 - By^2 + 2*(By-Ay) * y)/(2*(Ax-Bx))
代入(4)得:
Rx = 此处省略30字
Ry = 此处省略30字
总之是个简单的2元一次方程.
给定三角形三个顶点坐标,求三角形内切圆的圆心坐标与半径
/******************* Problem 2 **********************/ Circle InscribedCircle(Point p1, Point p2, Point p3) { double a = Length(p2-p3); double b = Length(p3-p1); double c = Length(p1-p2); Point p = (p1*a+p2*b+p3*c)/(a+b+c); return Circle(p, DistanceToLine(p, p1, p2)); }
这个是公式,推导过程:
三角形ABC的内切圆圆心为 三个角平分线交点 到三边距离相等
既然知道坐标 便可知三边所在直线方程 设该圆心坐标为(x,y) 用点到直线距离公式
⊿ABC中,A(x1,y1),B(x2,y2),C(x3,y3),那么⊿ABC内心I的坐标是:
(a·x1/(a+b+c)+b·x2(a+b+c)+c·x3(a+b+c),a·y1/(a+b+c)+b·y2/(a+b+c)+c·y3(a+b+c)).
其中:a b c是角A角B角C对应的边
给定圆心坐标和半径,求过定点p并且和这个圆相切的所有切线
/******************* Problem 3 **********************/ // 过点p到圆C的切线。v[i]是第i条切线的向量。返回切线条数 int getTangents(Point p, Circle C, Vector* v) { Vector u = C.c - p; double dist = Length(u); if(dist < C.r) return 0; else if(dcmp(dist - C.r) == 0) { // p在圆上,只有一条切线 v[0] = Rotate(u, PI/2); return 1; } else { double ang = asin(C.r / dist); v[0] = Rotate(u, -ang); v[1] = Rotate(u, +ang); return 2; } }
求出所有过定点p并且和直线相切的半径为r的圆
/******************* Problem 4 **********************/ vector<Point> CircleThroughPointTangentToLineGivenRadius(Point p, Line L, double r) { vector<Point> ans; double t1, t2; getLineCircleIntersection(L.move(-r), Circle(p, r), t1, t2, ans); getLineCircleIntersection(L.move(r), Circle(p, r), t1, t2, ans); return ans; }
给两条不平行的直线,求所有半径为r并且同时和这两条直线相切的圆
/******************* Problem 5 **********************/ vector<Point> CircleTangentToLinesGivenRadius(Line a, Line b, double r) { vector<Point> ans; Line L1 = a.move(-r), L2 = a.move(r); Line L3 = b.move(-r), L4 = b.move(r); ans.push_back(GetLineIntersection(L1, L3)); ans.push_back(GetLineIntersection(L1, L4)); ans.push_back(GetLineIntersection(L2, L3)); ans.push_back(GetLineIntersection(L2, L4)); return ans; }
给定两个相离的圆,求出所有和这两个圆外切的,半径为r的圆
/******************* Problem 6 **********************/ vector<Point> CircleTangentToTwoDisjointCirclesWithRadius(Circle c1, Circle c2, double r) { vector<Point> ans; /* Vector v = c2.c - c1.c; double dist = Length(v); int d = dcmp(dist - c1.r -c2.r - r*2); if(d > 0) return ans; */ getCircleCircleIntersection(Circle(c1.c, c1.r+r), Circle(c2.c, c2.r+r), ans); return ans; }