题目描述
现有一块大奶酪,它的高度为 h,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为 z=0,奶酪的上表面为 z=h。
现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐标。
如果两个空洞相切或是相交,则Jerry可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry则可以从空洞跑到奶酪上表面。
位于奶酪下表面的 Jerry 想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑到奶酪的上表面去?
空间内两点 P1(x1,y1,z1)、P2(x2,y2,z2)的距离公式如下:
dist(P1,P2)=((x1−x2)^2+(y1−y2)^2+(z1−z2)^2)^1/2
输入格式
每个输入文件包含多组数据.
输入文件的第一行,包含一个正整数 T,代表该输入文件中所含的数据组数。接下来是 T
组数据,每组数据的格式如下:
第一行包含三个正整数 n,h和r,两个数之间以一个空格分开,分别代表奶酪中空洞的数量,奶酪的高度和空洞的半径。
接下来的 n行,每行包含三个整数 x、y、z
,两个数之间以一个空格分开,表示空洞球心坐标为 (x,y,z)。
输出格式
输出文件包含 T行,分别对应 T组数据的答案,如果在第 i组数据中,Jerry 能从下表面跑到上表面,则输出 Yes,如果不能,则输出 No。
数据范围
1≤n≤1000
1≤h,r≤109
T≤20
坐标的绝对值不超过10^9
样例
3
2 4 1
0 0 1
0 0 3
2 5 1
0 0 1
0 0 4
2 5 2
0 0 2
2 0 4
输出
Yes
No
Yes
(并查集)
通过寻找是否有相同的根节点,来确定是否两点是否属于一个集合
并查集代码模版
int find(int x)//并查集+状态压缩
{
if(x == p[x]) return x;//若该结点等于根节点 则返回
else return p[x] = find(p[x]);
}
由题意可知,若一点与下底面距离小于等于半径,则Jerry可以穿行,同理上底面类似,则合并该点与底面的集合。
奶酪中的点若两点之间的距离的平方小于4倍的半径的平方,则Jerry可以通过,合并该点的两个集合。
最终判断上下底面是否在同一个集合中,如果在同一个集合中,则Jerry能从下底面到达上底面。反之不能到达。
时间复杂度
$O(n^2)$
C++ 代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 1010;
int n, h, r;
struct Sphere//定义三个坐标的结构体
{
int x, y, z;
}q[N];
int p[N];
int find(int x)//并查集+状态压缩
{
if(x == p[x]) return x;//若该结点等于根节点 则返回
else return p[x] = find(p[x]);
}
int main()
{
int T;
scanf("%d", &T);
while(T --)
{
scanf("%d%d%d", &n, &h, &r);
for(int i = 0;i <= n + 1;i ++) p[i] = i;
for(int i = 1;i <= n; i ++)
{
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
q[i] = {x, y, z};
if(abs(z) <= r) p[find(i)] = find(0);//下底面与集合合并
if(abs(z - h) <= r) p[find(i)] = find(n + 1);//上底面与集合合并
}
for(int i = 1;i <= n; i ++)
for(int j = 1;j < i;j ++)
{
LL dx = q[i].x - q[j].x;
LL dy = q[i].y - q[j].y;
LL dz = q[i].z - q[j].z;
if(dx * dx + dy * dy + dz * dz <= 4 * (LL)r * r)//若两点之间距离小于两倍的半径则合并集合
p[find(i)] = find(j);
}
if(find(0) == find(n + 1)) printf("Yes\n");//判断上底面与下底面是否在同一个集合中
else printf("No\n");
}
return 0;
}