JZOJ 3956. 【GDOI2015模拟12.20】鸡腿の梦境(计算几何)

≯℡__Kan透↙ 提交于 2020-01-22 22:11:33

JZOJ 3956. 【GDOI2015模拟12.20】鸡腿の梦境

题目

Description

【故事の背景】

鸡腿是CZYZ的著名DS,他为了树立高富帅的伟大形象决定暑假去张江大学学习(游玩)。去的第一天晚上因为蚊子很多,鸡腿不堪其扰怒而打了一夜游戏。 第二天鸡腿吸取教训,弄好了蚊帐,自然是睡了一个好觉。鸡腿似乎还记得做的那个梦……

【问题の描述】

鸡腿做了一个好梦呢!他化身钢铁侠大战全宇宙的各种怪物,并且取得了胜利。在梦的结尾,鸡腿开的飞船困在了一片废弃飞船群中,同时由于引擎故障,他只能在平面内移动。废弃飞船群可以描述为许多圆形的飞船,当然因为各种奇怪的原因,飞船可能叠加在一切。鸡腿也驾驶着飞船在这个平面中。一开始的时候鸡腿的飞船是不会和废弃的飞船有叠加部分的,鸡腿驶出这片区域时,可以蹭过某一个废弃飞船,但不能撞上更不能挤开它(质量问题),更不能和废弃飞船叠起来。鸡腿要立刻离开这片区域进行补给,你能告诉他现有条件下可以离开这片废弃飞船群吗?

Input

有多组数据,文件以EOF结束。
每组数据第一行一个整数N,表示有N个废弃飞船组成废弃飞船群。
接下来N行每行三个实数,Xi、Yi、Ri表示废弃飞船圆心坐标和半径。
最后一行三个实数表示鸡腿驾驶飞船的圆心位置和半径。
数据保证一开始位置合法。

Output

对于每一组数据,如果可行请输出“YES”,否则输出“NO”,输出后换行。

Sample Input

输入1:
7
2 2 1.1
-2 2 1.1
2 -2 1.0
-2 -2 1.0
2 -5 1.0
0 -8 1.0
-2 -6 1.0
0 0 1
输入2:
5
2 2 1.1
-2 2 1.1
2 -2 1.0
-2 -2 1.0
0 -3 0.01
0 0 1

Sample Output

输出1:
YES
输出2:
NO

Data Constraint

对于30%的数据N ≤ 15;
对于40%的数据 N ≤ 150;
对于 100%的数据 N ≤ 300,数据组数 ≤ 5。

题解

  • 乍一看这道题目,一个圆在一对圆中穿梭,只能相离或相切,虽然看懂了题目,但完全无从下手,就这么直接放弃了——
  • 若想做出这道题,首先不得不做的是转换题目的条件,很容易(假的)可以想到(我怎么就没想到),把每个废弃的飞船半径加上鸡腿飞船的半径,将鸡腿飞船变成一个点,现在只用看这个点能不能“离开”这片区域。
  • 只有一圈的圆把这个点包围起来了,这个点才出不去,否则都是可以的,问题就是怎么找到这“一圈”的圆。
  • 先转换坐标,以鸡腿飞船为原点,
  • 枚举两个废弃飞船,满足两个条件:在x轴同侧(在x轴上可算作在上方)且两个圆相交,那么就用并查集将这两个圆连边,
  • 这样就会出现x轴上方的一些联通块和x轴下方的一些联通块,现在需要看他们中能不能有两块把原点包围。
  • 再枚举两个圆,满足两个条件:在x轴异侧且两个圆相交,找到它们连线与x轴的交点,判断是在正半轴还是负半轴,将这两个圆所在的联通块编号之间打上标记,表示在正半轴或负半轴相交了一次,
  • 最后枚举两个联通块,如果它们之间能有连线既在正半轴相交且在负半轴相交,那么它们一定能把原点包围,则输出NO,否则输出YES。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define ld long double
#define N 310
struct
{
	ld x,y,r;
}a[N];
int f[N],p[N][N][2];
ld sqr(ld x)
{
	return x*x;
}
ld dis(int x,int y)
{
	return sqrt(sqr(a[x].x-a[y].x)+sqr(a[x].y-a[y].y));
}
int get(int x)
{
	if(f[x]==x) return x;
	return f[x]=get(f[x]);
}
int main()
{
	int n,i,j;
	scanf("%d",&n);
	while(n>0)
	{
		for(i=1;i<=n+1;i++) scanf("%Lf%Lf%Lf",&a[i].x,&a[i].y,&a[i].r);
		for(i=1;i<=n;i++) a[i].x-=a[n+1].x,a[i].y-=a[n+1].y,a[i].r+=a[n+1].r;
		for(i=1;i<=n;i++) f[i]=i;
		for(i=1;i<n;i++)
			for(j=i+1;j<=n;j++)
			{
				if((a[i].y>=0)!=(a[j].y>=0)) continue;
				if(a[i].r+a[j].r>dis(i,j))
				{
					int x=get(f[i]),y=get(f[j]);
					f[x]=y;
				}
			}
		memset(p,0,sizeof(p));
		for(i=1;i<n;i++)
			for(j=i+1;j<=n;j++)
			{
				if((a[i].y>=0)==(a[j].y>=0)) continue;
				if(a[i].r+a[j].r>dis(i,j))
				{
					int x=get(f[i]),y=get(f[j]);
					ld m=a[i].x;
					if(a[i].x!=a[j].x)
					{
						ld k=(a[i].y-a[j].y)/(a[i].x-a[j].x),b=a[i].y-k*a[i].x;
						m=-b/k;	
					}
					if(m<=0) p[x][y][1]=p[y][x][1]=1; else p[x][y][0]=p[y][x][0]=1;
				}
			}
		int ok=1;
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++) if(p[i][j][0]&&p[i][j][1]) ok=0;
		ok?printf("YES\n"):printf("NO\n");
		n=0;
		scanf("%d",&n);
	}
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!