【NOIP2017】【洛谷3958】奶酪cheese(并查集)(dfs)

问题描述

现有一块大奶酪,它的高度为h,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为z=0,奶酪的上表面为z=h

现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。

位于奶酪下表面的 Jerry 想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑到奶酪的上表面去?

空间内两点P1(x1,y1,z1),P2(x2,y2,z2)的距离公式如下:

dist(P1,P2)=sqrt((x1x2)2+(y1y2)2+(z1z2)2)

输入格式

每个输入文件包含多组数据。

的第一行,包含一个正整数T,代表该输入文件中所含的数据组数。

接下来是T组数据,每组数据的格式如下: 第一行包含三个正整数n,h和r,两个数之间以一个空格分开,分别代表奶酪中空洞的数量,奶酪的高度和空洞的半径。

接下来的n行,每行包含三个整数x,y,z两个数之间以一个空格分开,表示空洞球心坐标为(x,y,z)。

输出格式

T行,分别对应T组数据的答案,如果在第i组数据中,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

数据规模与约定

对于20%的数据,n=1,1h,r10,000,坐标的绝对值不超过10,000。

对于40%的数据,1n8,1h,r10,000,坐标的绝对值不超过10,000。

对于80%的数据,1n1,000,1h,r10,000,坐标的绝对值不超过10,000。

对于100%的数据,1n1,000,1h,r1,000,000,000,T20,坐标的绝对值不超过1,000,000,000。

题解

第一次看到这题很多人的第一想法都是bfs或dfs吧。

听说可以用并查集我也是一脸懵逼。。。

但是想一下并查集好像挺好写的。。。

如果两个洞相切或相交,把这两个洞放进同一个集合,最后只要判断下表面是否有一个洞和上表面的一个洞在同一个集合就行了。

怎么判断呢?

我们构造一个源点0,向所有和下表面相切或相交的洞连边,那么初始时这些洞在同一个集合里。再构造一个汇点n+1,把所有和上表面相切或相交的洞向这个汇点连边。最后只要O(1)判断源点和汇点是否在同一个集合就行了。

这样应该是可行的。然而代码是我一个月前打的,当时不知道怎么想的觉得这样不行,就打了个比较麻烦的。╮(╯▽╰)╭

 1 #include <cstdio>
 2 #define ll long long
 3 int T,n,f[1005],num,a[1005];
 4 ll h,r,rr,x[1005],y[1005],z[1005];
 5 int find(int x)
 6 {
 7     if (f[x]==x) return x;
 8     return f[x]=find(f[x]);
 9 }
10 int main()
11 {
12     int i,j,fi,fj;
13     scanf("%d",&T);
14     bool can;
15     while (T--)
16     {
17         scanf("%d%lld%lld",&n,&h,&r);
18         rr=4*r*r;   num=0;
19         for (i=1;i<=n;i++) f[i]=i;
20         for (i=1;i<=n;i++)
21         {
22             scanf("%lld%lld%lld",&x[i],&y[i],&z[i]);
23             if (z[i]<=r && z[i]>=-r) f[i]=0;
24             if (z[i]<=h+r && z[i]>=h-r) a[++num]=i;
25         }  
26         for (i=1;i<=n;i++)
27           for (j=i+1;j<=n;j++)
28             if (rr>=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])+(z[i]-z[j])*(z[i]-z[j]))
29             {
30                 fi=find(i);  fj=find(j);
31                 if (fi!=fj) 
32                   if (fi<fj) f[fj]=fi;
33                   else f[fi]=fj;
34             }
35         for (i=1,can=0;i<=num && !can;i++)
36           if (find(a[i])==0)
37             can=1;
38         if (can) printf("Yes
");
39         else printf("No
");       
40     }
41     return 0;
42 }

再贴个很早以前打的dfs

 1 #include <cstdio>
 2 int head[1005],T,n,num,h,x[1005],y[1005],z[1005];
 3 long long r;
 4 struct node{
 5     int to,next;
 6 }e[2000005];
 7 bool vis[1005];
 8 bool dfs(int u)
 9 {
10     vis[u]=1;if(u==n+1)return 1;
11     for(int i=head[u];i;i=e[i].next)
12      if(!vis[e[i].to])
13       if(dfs(e[i].to))return 1;
14     return 0;
15 }
16 void add(int u,int v)
17 {
18     e[++num].next=head[u];head[u]=num;e[num].to=v;
19     return;
20 }
21 int main()
22 {
23     
24     scanf("%d",&T);
25     while(T--)
26     {
27         scanf("%d%d%lld",&n,&h,&r);num=0;
28         for(int i=0;i<=n+1;i++)head[i]=vis[i]=0;
29         for(int i=1;i<=n;i++)
30         {
31             scanf("%d%d%d",&x[i],&y[i],&z[i]);
32             if(z[i]-r<=0 && z[i]+r>=0)add(0,i);
33             if(z[i]+r>=h && z[i]-r<=h)add(i,n+1);
34             for(int j=1;j<i;j++)
35             {
36                 long long a=x[i]-x[j],b=y[i]-y[j],c=z[i]-z[j];
37                 if((a*a+b*b)<=(4*r*r-c*c))add(i,j),add(j,i);
38             }
39         }
40         if(dfs(0)) printf("Yes
");
41         else printf("No
");
42     }
43     return 0;
44 }
原文地址:https://www.cnblogs.com/rabbit1103/p/9550373.html