【洛谷-3958】 奶酪
好久没更新博客了……正巧做了一次NOIP提高组的线下赛,胡乱写一篇?
◇ 题目
三维直角坐标系上有一个正方体奶酪,它的长和宽是无穷大的,它的底端在z=0的平面。老鼠杰瑞在奶酪的底端,它想要上到奶酪的顶部。
奶酪有n个半径相等的球形的洞,如果两个球形的洞相交或相接,老鼠杰瑞就可以从它们中间穿过去。老鼠杰瑞可以从与底面相交或相切的任意一个球形开始,从与顶面相交或相切的任意一个球形结束,求它能否穿过这些洞到达奶酪顶部。
◇ 解析
不难发现一些洞形成了连通块,如果某一个连通块中既包含连接底面(可以从该洞出发),又包含连接顶面(从该洞可以到达顶面)的洞,则杰瑞可以通过这个连通块到达顶部,反之则不能。
如何求出连通块?其实这就相当于一个无向图,我们可以通过并查集来维护这些连通块,当然……BFS之类的搜索算法应该也是可以的(毕竟n不算大)。枚举两个不同的洞,判断它们是否相交或相切(即两洞中心的距离小于等于两倍的半径),然后就把它们连接到一个连通块里面。定义一个数组 cnt[i][0/1] 表示第i个洞所属连通块有没有连接 顶部/底部 的连通块。如果都存在,就可以通到顶部了?。
(具体看一看代码吧,注释应该比较清晰了)
◇ 源代码
/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
int T;
int n,h,r;
double bal[1005][3];
int fa[1005],cnt[1005][2];
int Find(int x) { //并查集
if(x==fa[x]) return x;
return fa[x]=Find(fa[x]);
}
double Dis(int A,int B) { //计算三维直角坐标系两点的距离
return sqrt(1.0*(bal[A][0]-bal[B][0])*(bal[A][0]-bal[B][0])+1.0*(bal[A][1]-bal[B][1])*(bal[A][1]-bal[B][1])+1.0*(bal[A][2]-bal[B][2])*(bal[A][2]-bal[B][2]));
}
bool Solve() {
for(int i=1; i<=n; i++)
for(int j=i+1; j<=n; j++)
if(i!=j && Dis(i,j)<=2*r) { //如果 两洞中心的距离 小于等于 2倍半径
int prei=Find(i),prej=Find(j);
if(prei!=prej) //连接到一个连通块
fa[prei]=prej;
}
memset(cnt,0,sizeof cnt);
for(int i=1; i<=n; i++) {
if(bal[i][2]<=r) //如果连接到底部
cnt[Find(i)][0]=1;
if(h-bal[i][2]<=r) //如果连接到顶部
cnt[Find(i)][1]=1;
}
for(int i=1; i<=n; i++)
if(cnt[i][0] && cnt[i][1]) //该连通块顶部、底部都连接
return true;
return false;
}
int main() {
freopen("cheese.in","r",stdin);
freopen("cheese.out","w",stdout);
scanf("%d",&T);
while(T--) {
scanf("%d%d%d",&n,&h,&r);
for(int i=1; i<=n; i++) {
scanf("%lf%lf%lf",&bal[i][0],&bal[i][1],&bal[i][2]);
fa[i]=i;
}
printf("%s\n",Solve()? "Yes":"No");
}
return 0;
}
The End
Thanks for reading!
- Lucky_Glass
(Tab:如果我有没讲清楚的地方可以直接在邮箱lucky_glass@foxmail.com email我,在周末我会尽量解答并完善博客~?)