【杂题总汇】洛谷-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我,在周末我会尽量解答并完善博客~📃)
欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~