Bootstrap

UVA12556:“Center“ of perimeter midpoints(周边中点的“中心”)

原题(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:声明结构体以表示一个二维平面上的点,包含两个成员变量xy,分别表示点的横坐标和纵坐标。再写一个构造函数,用于初始化点的坐标,默认值为(0, 0)

STEP 3:声明计算欧几里得距离的distance 函数,公式为:sqrt((x2 - x1)^2 + (y2 - y1)^2)

STEP 4:定义函数,计算三角形的周长中点,具体操作如下:

  1. 计算三角形的三条边的长度ACBCAB

  2. 计算AD = (AB + BC - AC) / 2

  3. 计算ratio = AD / AB

  4. 根据比例ratio计算点D的坐标(x, y)

  5. 返回点D

STEP 5:定义计算两条直线的交点的函数。具体操作如下:

  1. 计算分母denominator,如果分母接近于0,则说明两条直线平行或重合,返回(0, 0)

  2. 计算参数t,然后根据t计算交点的坐标(x, y)

  3. 返回交点。

STEP 6:输入t组测试数据

STEP 7:读取三个点的坐标(x1, y1)(x2, y2)(x3, y3),并构造三个Point对象ABC

STEP 8:计算三个周长中点DEF和两条直线的交点intersect1intersect2

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;
}

运行结果

 

;