// Rope Crisis in Ropeland! (绳子王国的危机!) // PC/UVa IDs: 111302/10180, Popularity: B, Success rate: average Level: 2 // Verdict: Accepted // Submission Date: 2011-11-02 // UVa Run Time: 0.696s // // 版权所有(C)2011,邱秋。metaphysis # yeah dot net // // [解题方法] // 根据情况判断绳子是否需要绕过柱子,若需绕过柱子则根据边角关系计算即可。 #include <iostream> #include <iomanip> #include <cmath> using namespace std; // 计算需要绕过柱子时的绳子最小长度。 double linePlusArc(double x1, double y1, double x2, double y2, double r) { // 设坐标(x1,y1)为点 M,坐标(x2,y2)为点 N,圆心为 O,将两队 // 所在点和圆心连接成一个三角形 OMN,边长分别为 a(MO),b(NO), // c(MN),则边 c 对应的角 C 可以由余弦定理求出。 double a = sqrt(x1 * x1 + y1 * y1); double b = sqrt(x2 * x2 + y2 * y2); double c = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2)); double C = acos((a * a + b * b - c * c) / (2.0 * a * b)); // 设经过点 M 与圆相切的直线 L 与圆的切点为 P,则 MPO 构成一个直角 // 三角形,边 MP 对应的角可以很容易求出。 double angleMP = acos(r / a); // 同样的,将经过点 N 与圆相切的直线 L‘ 与圆的切点记为 Q,则 NQO // 构成一个三角形,边 NQ 对应的角也可以很容易求出。 double angleNQ = acos(r / b); // 则在柱子上的绳子部分长度可以容易的求出。 double length = r * (C - angleMP - angleNQ); // 再加上线段 MP,NQ 的长度即为所需绳子的最小长度。 length += (sqrt(a * a - r * r) + sqrt(b * b - r * r)); return length; } // 计算需要的绳子最小长度。 double ropeLength(double x1, double y1, double x2, double y2, double r) { // 两点相同。 if (x1 == x2 && y1 == y2) return 0.0; // 两点横坐标相同,在同一竖线上。 if (x1 == x2) { // 若竖线与原点距离小于 r 则绳子需要绕过柱子。 if ((x1 > 0 ? x1 : (-x1)) < r) { if (y1 * y2 > 0) return fabs(y1 - y2); else return linePlusArc(x1, y1, x2, y2, r); } else return fabs(y1 - y2); } // 根据对称性计算。 if (y1 == y2) return ropeLength(y1, x1, y2, x2, r); // 点 M 和 N 构成的直线斜率不为无穷大,可以使用斜截式来表示。则原点到直线的距离可以方 // 便的求出。 double slope = (double)(y1 - y2) / (double)(x1 - x2); double intercept = y1 - slope * x1; double distance = fabs(intercept) * cos(atan(fabs(slope))); // 若原点与直线的距离小于 r 且两队在柱子的两侧,则绳子需要绕过柱子。 // 否则,不需绕过柱子。 if (distance < r) { // 设过两队的直线为 l,求过原点与直线 l 垂直的直线与 l 的交点,若两队的位置 // 纵坐标均大于交点的纵坐标或者均小于交点的纵坐标,则两队在柱子的一侧。 double yIntersection = intercept / (slope * slope + 1.0); if ((y1 - yIntersection) * (y2 - yIntersection) >= 0.0) return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2)); else return linePlusArc(x1, y1, x2, y2, r); } else return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2)); } int main(int ac, char *av[]) { int cases; double x1, y1, x2, y2, r; cout.precision(3); cout.setf(ios::fixed | ios::showpoint); cin >> cases; while (cases--) { cin >> x1 >> y1 >> x2 >> y2 >> r; cout << ropeLength(x1, y1, x2, y2, r) << endl; } return 0; }