题目描述
现有一块大奶酪,它的高度为 hh ,它的长度和宽度我们可以认为是无限大的,奶酪 中间有许多 半径相同 的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中, 奶酪的下表面为 z = 0z=0 ,奶酪的上表面为 z = hz=h 。
现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐 标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别 地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果 一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。
位于奶酪下表面的 Jerry 想知道,在 不破坏奶酪 的情况下,能否利用已有的空洞跑 到奶酪的上表面去?
空间内两点P1(x1,y1,z1) 、 P2(x2,y2,z2) 的距离公式如下:
dist(P1,P2)=sqrt( (x1-x2)^2 + (y1-y2)^2 + (z1-z2)^2) )
输入输出格式
输入格式:
每个输入文件包含多组数据。
第一行,包含一个正整数 T ,代表该输入文件中所含的数据组数。
接下来是 T 组数据,每组数据的格式如下: 第一行包含三个正整数 n,h 和 r ,两个数之间以一个空格分开,分别代表奶酪中空 洞的数量,奶酪的高度和空洞的半径。
接下来的 n行,每行包含三个整数 x,y,z,两个数之间以一个空格分开,表示空 洞球心坐标为 (x,y,z) 。
输出格式:
T 行,分别对应 T 组数据的答案,如果在第 i 组数据中,Jerry 能从下 表面跑到上表面,则输出Yes
,如果不能,则输出No。
输入输出样例
输入样例#1:
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
输出样例#1:
Yes
No
Yes
照抄题目,啦啦啦啦啦……
好,不扯了,开始讲解一下
(考场上我想到了正解,然而刚学了两周的我根本不知道啥叫并查集,然后我就…………)
其实就是很简单的并查集,把能互相联通的洞塞到一个并查集里,然后查找有没有与上边界相切的和与下边界相切的洞在一个并查集里,有就“Yes”,没有就“No”。
怎么判断呢?
bao li a !!!
不相信?
n<=1000 ,暴力判断每个洞是否相切才O(n^2 ),在乘个T,总复杂度才O( T*n^2 ),算算最大才 2*1e7,轻松AC。
(现在想想考试时我现场现编并查集就心疼)
上代码吧!
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#define N 1010
using namespace std;
int f[N];
int n,h,r,pd;
struct point {
double x,y,z;
} p[N];//结构体,代表每一个点
double jl(int a,int b){//算个距离(球心),距离嘛,jl……
if(b==0)return p[a].z;
if(b==n+1)return h-p[a].z;
return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y)+(p[a].z-p[b].z)*(p[a].z-p[b].z));
}
int ss(int x){//找查集号(一定要找最深的那个点的查集号!!!)
if(f[x]==0)return 0;
if(f[x]==n+1)return n+1;
if(f[x]==x)return x;
else return f[x]=ss(f[x]);//小技巧(缩个点,把寻找过程中所有的点直接塞进根点的查集里)
}
void xg(int x) {//判断一下是否与前面的洞相切
int fx,fi;
if(jl(x,n+1)<=r)f[x]=n+1;//如果与上边界相切,那就把它塞进n+1这个查集,因为普通的点查集号从1~n ,0 和 n+1 可以成为特殊的查集 ,0代表与下边界相切,n+1代表与上边界相切
if(jl(x,0)<=r) {
if(f[x]==n+1) {//如果这个洞即与上边界相切,又与下边界相切,那就YES,不然就塞进查集号为0的查集
pd=1;
return;
} else {
f[x]=0;
}
}
for(int i=1; i<x; i++) {
if(jl(i,x)<=2*r){//判断两个洞是否相切
fx=ss(x);
fi=ss(i);
if((fi==0&&fx==n+1)||(fi==n+1&&fx==0)){//这两个洞一个在与上边界相切的查集,一个在与下边界相切的查集,那就以为这可以,把判断值改为true
pd=1;
return;
}
else if(fx==0||f[i]==0){//以0 和 n+1 两个查集优先级高,然后再随便塞
f[x]=0;
f[i]=0;
}
else if(fx==n+1||fi==n+1){
f[x]=n+1;
f[i]=n+1;
}
else {
f[i]=fx;
}
}
}
}
int main() {
//freopen("add.in","r",stdin);
//freopen("add.out","w",stdout);
int T;
scanf("%d",&T);//多组数据
while(T--) {
pd=0;//清掉判断值
scanf("%d%d%d",&n,&h,&r);
memset(p,0,sizeof(p));
memset(f,0,sizeof(f));//多组数据时一定要清零!!!
for(int i=1; i<=n; i++)f[i]=i;//先挨个塞进一个并查集
for(int i=1; i<=n; i++) {
scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
xg(i);
}//因为输入数据行数有限制,不然第一次判断成功就就直接下组数据了
if(pd)printf("Yes\n"); //如果搜索过所有点都不成功(即 pd还是false )那就输出NO,否则YES
else printf("No\n");
}
return 0;
}
谢谢啦!!!