noip2017提高組 奶酪

點擊打開鏈接

題目描述

現有一塊大奶酪,它的高度爲 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)=(x1x2)2+(y1y2)2+(z1z2)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 (均不包含引號)。

輸入輸出樣例

輸入樣例#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







說明

【輸入輸出樣例 1 說明】

第一組數據,由奶酪的剖面圖可見:

第一個空洞在(0,0,0)與下表面相切

第二個空洞在(0,0,4)與上表面相切 兩個空洞在(0,0,2)相切

輸出 Yes

第二組數據,由奶酪的剖面圖可見:

兩個空洞既不相交也不相切

輸出 No

第三組數據,由奶酪的剖面圖可見:

兩個空洞相交 且與上下表面相切或相交

輸出 Yes

【數據規模與約定】

對於 20%的數據, n = 1n=1 , 1 \le h1h , r \le 10,000r10,000 ,座標的絕對值不超過 10,000。

對於 40%的數據, 1 \le n \le 81n8 , 1 \le h1h , r \le 10,000r10,000 ,座標的絕對值不超過 10,000。

對於80%的數據, 1 \le n \le 1,0001n1,000 , 1 \le h , r \le 10,0001h,r10,000 ,座標的絕對值不超過10,000。

對於 100%的數據, 1 \le n \le 1,0001n1,000 , 1 \le h , r \le 1,000,000,0001h,r1,000,000,000 , T \le 20

T20 ,座標的 絕對值不超過 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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章