// Radar Tracking (雷达追踪) // PC/UVa IDs: 111406/849, Popularity: C, Success rate: low Level: 2 // Verdict: Wrong Answer // Submission Date: 2011-11-14 // UVa Run Time: N/A // // 版权所有(C)2011,邱秋。metaphysis # yeah dot net // // [解题方法] // 雷达的角速度为 PI 弧度每秒。所有物体均是沿直线运动(不考虑圆周运动或其他曲线运动的情况)。方程 // 是超越方程,需要使用近似解法来解方程。或者直接使用枚举的办法,因为角度要求是小数点后两位数。 // // 不知道是 UVa 上的测试数据的问题还是怎样,我无法 AC,看了 UVa 的帖子才知道,确实有问题,可能我 // 使用的算法和 AC 代码使用的不一样,不能得到相同 “错误” 的答案,就算帖子中作者认为正确的数据, // 我核实了一下,也是有问题的,本题暂且放一下,期望高手能给予指点,UVa 上面才只有 15 个人通过了 // 本题,通过率是少见的低,应该是测试数据的问题。 #include <iostream> #include <algorithm> #include <iomanip> #include <cmath> using namespace std; #define MAXN 3 #define PI 3.141592653589793 #define EPSILON (1E-12) #define COUNTER_CLOCKWISE 0 #define CLOCKWISE_SAME_SAME_CYCLE 1 #define CLOCKWISE_SAME_NEXT_CYCLE 2 #define CLOCKWISE_NEXT_NEXT_CYCLE 3 struct point { double angle; double distance; }; bool cmp(point a, point b) { if (a.angle != b.angle) return a.angle < b.angle; return a.distance > b.distance; } double radians(double degree) { return (degree / 180.0) * PI; } double degrees(double radian) { return (radian / PI) * 180.0; } double value(double b, double c, double B, double C, double x, int type) { switch (type) { case COUNTER_CLOCKWISE: return b * sin(x) / sin(B + C + x) - c * (2.0 * PI - x) / (2.0 * PI - C); break; case CLOCKWISE_SAME_SAME_CYCLE: return b * sin(x) / sin(B + C + x) - c * x / C; break; case CLOCKWISE_SAME_NEXT_CYCLE: return b * sin(x) / sin(B + C + x) - c * (2.0 * PI + x) / C; break; case CLOCKWISE_NEXT_NEXT_CYCLE: return b * sin(x) / sin(B + C + x) - c * (2.0 * PI + x) / (2.0 * PI + C); break; } } double bisection(double b, double c, double B, double C, int type) { // 设第三个观察位置与第二个观察位置的角度差为 x 弧度,则由三角关系和 // 雷达的扫描角速度以及物体的运动速度,可以得到如下方程。 // // b * sin(x) / sin(B + C + x) - c * (2 * PI - x) / (2 * PI - C) = 0。 // // 由二分法得到近似解。x 的区间为 (0,PI - B - C)。首先找到大于 // 0 时的 x 值,已经明确,当 x = 0 时,函数值小于 0,则需要找到一 // 个值,使得函数值大于 0。 double up = PI - B - C, down = 0.0; double x = (up + down) / 2.0; while (value(b, c, B, C, x, type) < 0) x = (up + x) / 2.0; // 此时函数值大于 0。 up = x; // 目前 f(up) 和 f(down) 异号,则区间 (down,up) 有解。 while (fabs(up - down) > EPSILON) { x = (up + down) / 2.0; if (value(b, c, B, C, x, type) * value(b, c, B, C, up, type) > 0) up = x; else down = x; } return x; } void possibleSolutions(point first, point second) { point possible[MAXN]; int solutions = 0; // 当前后两次的角度相同时,说明物体的运动轨迹是过极点的直线,且前两次观察结果方向相同。 if (first.angle == second.angle) { // 远离极点方向。 if (second.distance >= first.distance) possible[solutions++] = (point){first.angle, 2 * second.distance - first.distance}; // 靠近极点方向,可能会越过极点到相对的另一侧。 else { if (2 * second.distance > first.distance) possible[solutions++] = (point) {first.angle, 2 * second.distance - first.distance}; else possible[solutions++] = (point){ ((first.angle >= 180.0) ? (first.angle - 180.0) : (first.angle + 180.0)), fabs(2 * second.distance - first.distance)}; } } // 当前后两次观察到的位置极角相差 180 度时,说明物体的运动轨迹是过极点的直线。 else if (fabs(first.angle - second.angle) == 180.0) { // 注意在第二次观察时,时间过去了 1 s,但是再下一次观察时,将是 3s。故移动距 // 离要增加一倍。 possible[solutions++] = (point){second.angle, 3 * second.distance + 2 * first.distance}; } // 当第一次和第二次的极角不同,且相差不是 180 度时,物体的运动轨迹是一条不过极点的直线。 else { // 当第二次观察位置出现在第一次的逆时针方向,则表明雷达是在一个周期的时间内观察 // 到两个位置。则第三次观察时只有一种可能的位置。 double C = 0.0, finalAngle, finalDistance, x; double angleDiff = fabs(first.angle - second.angle); if ((first.angle > second.angle && angleDiff < 180.0) || (first.angle < second.angle && angleDiff > 180.0)) { // 二分数值法求近似解。 // 先求出前后两次观察位置之间的角度差,设为角 C,对应边为 c。 if (first.angle < second.angle && angleDiff > 180.0) C = radians(first.angle + (360.0 - second.angle)); else C = radians(first.angle - second.angle); // 不失一般性,设第一次观察的位置距离为 a,第二次观察的距离为 b,可 // 由正弦和余弦定理求出边 b 对应的角 B。先求边 c。 double a = first.distance, b = second.distance; double c = sqrt(a * a + b * b - 2.0 * a * b * cos(C)); double B = asin(sin(C) * b / c); x = bisection(b, c, B, C, COUNTER_CLOCKWISE); finalAngle = second.angle - degrees(x); if (finalAngle < 0.0) finalAngle += 360.0; finalDistance = b * sin(B + C) / sin(B + C + x); possible[solutions++] = (point){finalAngle, finalDistance}; } // 第二个位置在第一个位置的顺时针方向,有两种可能,一种是在观察到第一个位置后的 // 同一个扫描周期内观察到第二个位置,另外一种情况是在下一个扫描周期内观察到第二 // 个位置。故有两种可能解。 else { if (first.angle > second.angle && angleDiff > 180.0) C = radians(second.angle + (360.0 - first.angle)); else C = radians(second.angle - first.angle); double a = first.distance, b = second.distance; double c = sqrt(a * a + b * b - 2.0 * a * b * cos(C)); double B = asin(sin(C) * b / c); // 计算所有可能性。 for (int i = CLOCKWISE_SAME_SAME_CYCLE; i <= CLOCKWISE_NEXT_NEXT_CYCLE; i++) { x = bisection(b, c, B, C, i); if (x == 0.0) continue; finalAngle = second.angle + degrees(x); if (finalAngle >= 360.0) finalAngle -= 360.0; finalDistance = b * sin(B + C) / sin(B + C + x); possible[solutions++] = (point){finalAngle, finalDistance}; } // 判断那些解是有效解。 // 检查计算结果是否满足物体的速度限制要求。若存在则有矛盾,该组结果丢弃。 // 假设物体速度为 c / (C / PI),即在转过 C 角度的时间,物体运动距离 // 为 c,计算第二次观察结果,若结果符合,则第一组结果有效,第二组结果 // 无效,否则相反。 // 物体速度为 c / (C / PI + 2.0),计算第二次观察结果,若符合则 // 第三组结果有效,否则无效。 } } // 按极角和距离排序。 bool blank = false; sort(possible, possible + solutions, cmp); for (int i = 0; i < solutions; i++) { if (possible[i].distance == 0) continue; cout << (blank ? " " : ""); blank = true; cout << possible[i].angle << " " << possible[i].distance; } cout << endl; } int main (int argc, char const* argv[]) { cout.precision(2); cout.setf(ios::fixed | ios::showpoint); point first, second; while (cin >> first.angle >> first.distance >> second.angle >> second.distance) possibleSolutions(first, second); return 0; }