題目描述
現有一塊大奶酪,它的高度爲 hh ,它的長度和寬度我們可以認爲是無限大的,奶酪 中間有許多 半徑相同 的球形空洞。我們可以在這塊奶酪中建立空間座標系,在座標系中, 奶酪的下表面爲 z = 0z=0 ,奶酪的上表面爲 z = hz=h 。
現在,奶酪的下表面有一隻小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐 標。如果兩個空洞相切或是相交,則 Jerry 可以從其中一個空洞跑到另一個空洞,特別 地,如果一個空洞與下表面相切或是相交,Jerry 則可以從奶酪下表面跑進空洞;如果 一個空洞與上表面相切或是相交,Jerry 則可以從空洞跑到奶酪上表面。
位於奶酪下表面的 Jerry 想知道,在 不破壞奶酪 的情況下,能否利用已有的空洞跑 到奶酪的上表面去?
空間內兩點 P_1(x_1,y_1,z_1)P1(x1,y1,z1) 、 P2(x_2,y_2,z_2)P2(x2,y2,z2) 的距離公式如下:
\mathrm{dist}(P_1,P_2)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}dist(P1,P2)=(x1−x2)2+(y1−y2)2+(z1−z2)2
輸入輸出格式
輸入格式:每個輸入文件包含多組數據。
輸入文件的第一行,包含一個正整數 TT ,代表該輸入文件中所含的數據組數。
接下來是 TT 組數據,每組數據的格式如下: 第一行包含三個正整數 n,hn,h 和 rr ,兩個數之間以一個空格分開,分別代表奶酪中空 洞的數量,奶酪的高度和空洞的半徑。
接下來的 nn 行,每行包含三個整數 x,y,zx,y,z ,兩個數之間以一個空格分開,表示空 洞球心座標爲 (x,y,z)(x,y,z) 。
輸出格式:輸出文件包含 TT 行,分別對應 TT 組數據的答案,如果在第 ii 組數據中,Jerry 能從下 表面跑到上表面,則輸出Yes
,如果不能,則輸出No
(均不包含引號)。
輸入輸出樣例
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
說明
【輸入輸出樣例 1 說明】
第一組數據,由奶酪的剖面圖可見:
第一個空洞在(0,0,0)與下表面相切
第二個空洞在(0,0,4)與上表面相切 兩個空洞在(0,0,2)相切
輸出 Yes
第二組數據,由奶酪的剖面圖可見:
兩個空洞既不相交也不相切
輸出 No
第三組數據,由奶酪的剖面圖可見:
兩個空洞相交 且與上下表面相切或相交
輸出 Yes
【數據規模與約定】
對於 20%的數據, n = 1n=1 , 1 \le h1≤h , r \le 10,000r≤10,000 ,座標的絕對值不超過 10,000。
對於 40%的數據, 1 \le n \le 81≤n≤8 , 1 \le h1≤h , r \le 10,000r≤10,000 ,座標的絕對值不超過 10,000。
對於80%的數據, 1 \le n \le 1,0001≤n≤1,000 , 1 \le h , r \le 10,0001≤h,r≤10,000 ,座標的絕對值不超過10,000。
對於 100%的數據, 1 \le n \le 1,0001≤n≤1,000 , 1 \le h , r \le 1,000,000,0001≤h,r≤1,000,000,000 , T \le 20
T≤20 ,座標的 絕對值不超過 1,000,000,000。
思路:
思路:並查集,先找出連接頂面的球和連接底面的球,再把所有的球能連接的全部合併,最後枚舉所有連接頂面和底面的球,如果有聯通的,就說明Jerry能從底走到頂
ps:試了一下在合併的過程中記錄能往上爬的最大高度,可是有點問題,希望有大佬能解釋一下這種思路能否行得通
代碼:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int f[100001];
int find( int x ) { //並查集常規操作
if( x != f[x] ) f[x] = find( f[x] );
return f[x];
}
double dis( long long x, long long y, long long z, long long x1, long long y1, long long z1 ) { //計算空間內兩點的距離
return sqrt( ( x - x1 ) * ( x - x1 ) + ( y - y1 ) * ( y - y1 ) + ( z - z1 ) * ( z - z1 ) );
}
long long x[100001], y[100001], z[100001]; //三個數組分別用來存球心的橫縱豎座標
long long r;
int n, h, t, top, bot, s; //top是連接着頂面的球的數量,bot是連接底面的球的數量
int f1[100001], f2[100001]; //分別是連接着頂面和底面的球的父親
int main() {
freopen( "cheese.in", "r", stdin );
freopen( "cheese.out", "w", stdout );
scanf( "%d", &t );
while( t-- ) {
s = 0; //每一次都初始化
top = 0;
bot = 0;
scanf( "%d%d%ld", &n, &h, &r );
for( int i = 1; i <= n; i++ )
f[i]=i;
for( int i = 1; i <= n; i++ ) {
scanf( "%ld%ld%ld", &x[i], &y[i], &z[i] );
if( z[i] + r >= h ) //找出所有連接頂面的球
f1[++top] = i;
if( z[i] - r <= 0) //找出所有連接底面的球
f2[++bot] = i;
for ( int j = 1; j <= i; j++ )
if( dis( x[i], y[i], z[i], x[j], y[j], z[j] ) <= 2 * r ) { //如果兩球相連就合併
int a1 = find( i );
int a2 = find( j );
if ( a1 != a2 )
f[a1] = a2;
}
}
for( int i = 1; i <= top; i++ )
for( int j = 1; j <= bot; j++ )
if( find( f1[i] ) == find( f2[j] ) ) { //枚舉所有連接上下面的球,如果能找到兩個聯通的,說明能爬到頂
s = 1;
break;
}
if ( s == 1 ) printf( "Yes\n" );
else printf( "No\n" );
}
return 0;
}