/* 这题的思路让我觉得很巧妙,所以...虽然小白书 P175 ,已经有详细到能看懂的解释了,但我还是用自己的话,把书上的解析复述一遍: 简化: 先判断解的存在性,有解再找解 类比: 可将敌人类比为圆,攻击范围则为一个个以敌人位置为圆心,攻击距离为半径的圆 可将战场看成湖,敌人的攻击范围组成的圆,看作一个个圆形踏脚石,看能否从湖的上边界走到下边界 分析: 题意所求,就是想知道,能否从左边界到右边界,并找到最北进入点和最北出边界点 可以先考虑一下,何时不可能从左边界走到右边界? 答:在敌人攻击范围的圆,能够组成一个从上边界到下边界的连通块时,因为这是,战场相当于被( 一条从上边界到下边界到分界线 )分成了东西两块,分界线上,都是敌人的攻击区域所覆盖的范围,那么无论如何也没有办法走到右边界了 至于最北位置,书上 P175 讲了判断方法,理解的层面,结合下集合和不等式的知识,想通不难,就不赘述了 这题最关键的是类比思想的运用,以把它转换为我们熟悉的连通块问题 值得一提的是: 1. 这题在浮点数判相等时,讲道理是该用 EPS 判等,考虑浮点误差的。但这题好像没有想在这里卡我们,因为直接用 <= 时,居然也是可以 AC 的...不过我 AC 完以后,还是觉得还是不太严谨,还是乖乖用精确的方法判了一次 2. 不可以定义 left 和 right 作为变量名 虽然它们看上去似乎不是关键字,但是...别忘了在 ios 的输入和输出的格式控制里,是有 std::left 和 std::right 的,这里引一个网址 ( http://www.cplusplus.com/reference/ios/left/ ) 所以在导入命名空间 std 的同时,left 和 right 被一并导入,意味着我们不能再定义这两个变量,否则必将引起二义性 < 这个坑以前倒是踩过,所以报错后立刻就发现了,哪里不对劲,但是,既然会再犯,说明它还是值得我重新整理一次的,因为再踩一次坑,无论我爬起来的有多快,其实还是说明,我对这个细节的掌握,其实还是没有到很熟悉的地步嘛~还是没到位啊! ╯﹏╰ > 3. C++的四舍五入 setprecision 博客: http://blog.csdn.net/mingzhentanwo/article/details/41082449 其中有句话特别重要,一定要无比牢记: 与setw()不同,setprecision(n)一直作用到下一个setprecisin(n)之前,所以,只需要写一个setprecision(n)即可。但setw()要每次都写 4. 这题到最后时,还因为少写了一个 endl 换行符,卡了许久的 WA,最后还是用 uva 自带的 udebug 里的测试数据,才发现我的 WA 是错在哪... T^T 唉...一定要注意细节,注意细节,这点再怎么强调也不为过...想想如果真的在正式场合,因为这个原因被卡 WA,出题方又没提供多的数据给我们检查的话,怕是一直发现不了这个bug,只能重敲一次了...揪心 */
/* 查阅过的其他链接的整理: 1. 浮点数精度问题,以及浮点数的“不可用符号比较的问题”: https://www.zhihu.com/question/36176935/answer/66516958 http://www.cnblogs.com/crazyacking/p/4668471.html http://blog.csdn.net/cbnotes/article/details/38920511 http://bbs.csdn.net/topics/90390941 http://blog.sina.com.cn/s/blog_48d4cf2d0100qzfc.html https://www.zhihu.com/question/29064056?sort=created 2. C++ 精度控制 http://blog.csdn.net/mingzhentanwo/article/details/41082449 */
#include <iostream> #include <cstring> #include <iomanip> #include <cmath> #include <cstdio> #define rep(i, n) for ( int i = 0; i < (n); i++ ) using namespace std; const int N = 1e3 +5; const double W = 1000.0; const double EPS = 1e-8; int n, vis[N]; double x[N], y[N], r[N], _left, _right; bool jud; // 标记变量,标记是否能从左边界某点进入,右边界某点出来 // 顾名思义,不大于,这是重写了浮点型的 <= ,因为浮点数由于精度问题,是不能用符号直接判定的 bool notMoreThan ( double a, double b ) { return ( a < b ) || ( fabs (a - b) < EPS); } // 几何中,相切和相交,就是不相离的情况,所以定义为 notApart bool notApart (int c1, int c2 ) { double a = sqrt ( pow( x[c1] - x[c2], 2 ) + pow ( y[c1] - y[c2], 2 ) ); double b = r[c1] + r[c2]; return notMoreThan(a, b); } void renewCircle ( int u ) { //与左边界相交或相切时,可能会更新 _left if ( notMoreThan ( x[u], r[u] ) ) _left = min ( _left, y[u] - sqrt( pow( r[u], 2 ) - pow( x[u], 2 ) ) ); //与右边界相交或相切时,可能会更新 _right if ( notMoreThan ( W - x[u], r[u] ) ) _right = min ( _right, y[u] - sqrt( pow( r[u], 2 ) - pow( W - x[u], 2) ) ); } bool dfs (int u) // 看能否到达底部 { if ( vis[u] ) return false; // 如果已经被访问,但是居然现在又被访问,说明在上下方向上,不会有,覆盖了 u,且能完整连接上下边界的连通快了 (毕竟如果有,u 之前被某个其他的圆通过 bfs 访问时,当时应该就已经能够确认 jud 为 false了,就不会再有机会,再来访问 u 这个圆形攻击范围了) vis[u] = 1; // 先来标记访问了这个区域 if ( notMoreThan ( y[u], r[u] ) ) return true; //单凭这个圆,就已经能形成,从上边界到下边界的完整连通块的情况 rep ( v, n ) //与该圆相切或相交的圆形中,有圆能形成,从上边界到下边界的完整连通块的情况 if ( notApart(u, v) && dfs(v) ) return true; renewCircle(u); // 返回 false 前,先更新 _left 和 _right 的值 return false; } void solve () { while ( cin >> n ) { jud = true; _left = _right = W; memset( vis, 0, sizeof(vis) ); rep(i, n) cin >> x[i] >> y[i] >> r[i]; rep(i, n) if ( y[i] + r[i] >= W && dfs(i) ) { jud = false; break; } if (jud) cout << "0.00 " << fixed << setprecision(2) << _left << " " << W << " " << _right << endl; else cout << "IMPOSSIBLE" << endl; } } int main() { solve(); return 0; }