UVA - 11853 Paintball

/*
  这题的思路让我觉得很巧妙,所以...虽然小白书  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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值