【杂题总汇】洛谷-3958 奶酪

【洛谷-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我,在周末我会尽量解答并完善博客~📃)
posted @ 2018-09-15 11:52  Lucky_Glass  阅读(147)  评论(0编辑  收藏  举报
TOP BOTTOM