原题(English)
When I was a high school student, I learned that given a triangle ABC, denote D, E, F as the midpoints of AB, BC and CA, then three segments CD, AE, BF intersects at one point: the centroid. Then I thought about the following question: if we change “midpoint” by “perimeter midpoint”, can CD, AE, BF still intersect at one point? To be precise, if CA+AD = DB+BC, we say D is the “perimeter midpoint” on AB. It’s not difficult to see that there is exactly one such point lying strictly inside the segment AB. Point E and F are defined similarly and also have unique positions. Help (the younger) me to find out the answer!
Input
The first line contains the number of test cases T (T ≤ 100). Each test case contains 6 integers x1, y1, x2, y2, x3, y3, whose absolute values do not exceed 100. These integers represent three non-collinear points A(x1,y1), B(x2,y2), C(x3,y3).
Output
For each test case, if CD, AE, BF intersect at one point, print the position of the intersection to 6 decimal places. Otherwise print ‘ERROR’ (without quotes). Sample Input
2-1 0 1 0 0 1
0 0 5 0 3 3
Sample Output
Case 1: 0.000000 0.171573
Case 2: 2.362911 0.665041
翻译
在 △ABC 里,若有 D,E,F 为 AB,BC,AC 的中点,那么 CD,AE,BF 交于一点,即 △ABC 的重心。
如果我们定义 D 为 △ABC 的“周长中点”,则点 D 满足 AC+AD=BD+BC(点 D 在 AB 上),并且对于其他的两个点也这么定义。
问 CD,AE,BF 能否交于一点,如果能,交于哪一点。
输入
T 组数据,每组数据 6 个整数 x1,y1,x2,y2,x3,y3 分别代表 A(x1,y1),B(x2,y2),C(x3,y3) 并且满足这些整数的绝对值不大于 100。
输出
如果三条线段交于一点,则输出交点坐标(保留 6 位小数);否则输出 ERROR
。因为这是 UVa 的题目,你照例需要输出数据组数标号。
样例输入输出
输入
2
-1 0 1 0 0 1
0 0 5 0 3 3
输出
Case 1: 0.000000 0.171573
Case 2: 2.362911 0.665041
思路
计算三角形的周长中点(perimeter midpoint)并找到这些中点的交点
STEP 1:声明处理浮点数精度问题的函数。如果输入的浮点数n
的绝对值小于一个很小的值eps
(即1e-6
),则返回0.0
,否则返回n
本身,避免浮点数计算中的精度误差。(注意:如果这个不加的话会导致连样例都通过不了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!)
STEP 2:声明结构体以表示一个二维平面上的点,包含两个成员变量x
和y
,分别表示点的横坐标和纵坐标。再写一个构造函数,用于初始化点的坐标,默认值为(0, 0)
。
STEP 3:声明计算欧几里得距离的distance
函数,公式为:sqrt((x2 - x1)^2 + (y2 - y1)^2)
。
STEP 4:定义函数,计算三角形的周长中点,具体操作如下:
-
计算三角形的三条边的长度
AC
、BC
、AB
。 -
计算
AD = (AB + BC - AC) / 2
。 -
计算
ratio = AD / AB
。 -
根据比例
ratio
计算点D
的坐标(x, y)
。 -
返回点
D
。
STEP 5:定义计算两条直线的交点的函数。具体操作如下:
-
计算分母
denominator
,如果分母接近于0
,则说明两条直线平行或重合,返回(0, 0)
。 -
计算参数
t
,然后根据t
计算交点的坐标(x, y)
。 -
返回交点。
STEP 6:输入t组测试数据
STEP 7:读取三个点的坐标(x1, y1)
、(x2, y2)
、(x3, y3)
,并构造三个Point
对象A
、B
、C
。
STEP 8:计算三个周长中点D
、E
、F
和两条直线的交点intersect1
和intersect2
。
STEP 9:输出,若两个交点的坐标差小于1e-9
,则输出交点的坐标,否则输出ERROR
。
代码
#include<bits/stdc++.h> using namespace std; double handleZero(double n) { const double eps=1e-6; if (abs(n)<eps) { return 0.0; } return n; } struct Point { double x,y; Point(double x=0,double y=0):x(x),y(y) { } }; double distance(const Point&p1,const Point&p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } Point perimeterMidpoint(const Point&A,const Point&B,const Point&C) { double AC=distance(A, C),BC=distance(B, C),AB=distance(A, B),AD=(AB+BC-AC)/2,ratio=AD/AB,x=A.x+ratio*(B.x-A.x),y=A.y+ratio*(B.y-A.y); return Point(x,y); } Point intersection(const Point&p1,const Point& p2, const Point& p3, const Point& p4) { double denominator=(p1.x-p2.x)*(p3.y-p4.y)-(p1.y-p2.y)*(p3.x-p4.x); if(abs(denominator)<1e-9) { return Point(0,0); } double t=((p1.x-p3.x)*(p3.y-p4.y)-(p1.y-p3.y)*(p3.x-p4.x))/denominator,x=p1.x+t*(p2.x-p1.x),y=p1.y+t*(p2.y-p1.y); return Point(x,y); } int main() { int T; cin>>T; for(int caseNum=1;caseNum<=T;caseNum++) { double x1,y1,x2,y2,x3,y3; cin>>x1>>y1>>x2>>y2>>x3>>y3; Point A(x1,y1),B(x2,y2),C(x3, y3),D=perimeterMidpoint(A,B,C),E=perimeterMidpoint(B,C,A),F=perimeterMidpoint(C,A,B),intersect1=intersection(C,D,A,E),intersect2=intersection(C,D,B,F); if(abs(intersect1.x-intersect2.x)<1e-9&&abs(intersect1.y-intersect2.y)<1e-9) { cout<<"Case "<<caseNum<<": "<<fixed<<setprecision(6)<<handleZero(intersect1.x)<<" "<<handleZero(intersect1.y)<<'\n'; } else { cout<<"Case "<<caseNum<<": ERROR"<<'\n'; } } return 0; }
运行结果