三种解法:bfs,dfs,并查集

方法一:bfs

两个点之间的距离如果小于等于2*r,那么这两个点可以相互到达。将所有可以互相到达的两个点之间连一条边。将所有与下表面相交的点入队,从这些点开始进行bfs搜索,对于每个搜索到的点进行判断:该点是否与上表面相交,若能找到与上表面相交的点,则输出yes,若无法找到这样的点则输出no。需要注意的是初始所有与下表面相交入队的点,这些点可能同时也与上表面相交,所以要确保判断了这些点是否与上表面相交。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+10;
int h[N],e[N*N],ne[N*N],idx,T,n,H,r;
bool st[N];//标记走过的点
queue<int>q;
struct Point
{
    int x,y,z;
}point[N];
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool bfs()
{
    while(q.size())
    {
        int t=q.front();
        q.pop();
        if(H-point[t].z<=r) return true;//第一批与下表面相交初始进入队列的点可能同时也与上表面相交
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(H-point[j].z<=r) return true;//该点纵坐标与上表面高度之差小于等于r,可以到达上表面
            if(st[j]==false)//该点之前没有走过则入队
            {
                st[j]=true;
                q.push(j);
            }
        }
    }
    return false;
}
signed main()
{    
    cin>>T;
    while(T--)
    {
        idx=0;
        q=queue<int>();//清空q队列
        memset(st,0,sizeof st);
        memset(h,-1,sizeof h);
        cin>>n>>H>>r;
        for(int i=0;i<n;i++)//输入n个点的坐标
        {
            cin>>point[i].x>>point[i].y>>point[i].z;
            if(point[i].z<=r)
            {
                q.push(i);
                st[i]=true;
            }
        }
        for(int i=0;i<n;i++)//建图,将可以互相到达的两个点之间连一条边
            for(int j=i+1;j<n;j++)
            {
                int x=point[i].x-point[j].x;
                int y=point[i].y-point[j].y;
                int z=point[i].z-point[j].z;
                int dist=x*x+y*y+z*z;
                if(dist<=4*r*r)//两点之间的距离小于等于2r则可以互相到达
                {
                    add(i,j);
                    add(j,i);
                }
            }
        if(bfs()) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
}

方法二:并查集

基本思路大体相同。所有可以互相到达的两个点之间连一条边,改成所有可以互相到达的两个点合并入一个集合。用vectorbottom,top保存与下表面相交的节点和与上表面相交的节点。判断是否存在top中的节点和bottom中的节点在同一个集合中,若存在则输出yes,若不存在输出no。对于一些与下表面相交同时也与上表面相交的节点x,那么x节点即会存入bottom也会存入top,所以一定存在top中的节点和bottom中的节点在同一个集合中。所以这里也可以不用特殊处理这类节点。以下代码我多此一举处理了这些节点。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+10;
int T,n,h,r;
int p[N];
struct Point
{
    int x,y,z;
}point[N];
signed find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
signed main()
{
    cin>>T;
    while(T--)
    {
        vector<int>bottom,top;//bottom保存与下表面相交或相切的节点,top保存与上表面相交或相切的节点
        bool flag=false;//标记能否从下表面到达上表面
        cin>>n>>h>>r;
        for(int i=1;i<=n;i++) p[i]=i;
        for(int i=1;i<=n;i++)//输入n个点的坐标
        {
            cin>>point[i].x>>point[i].y>>point[i].z;
            if(point[i].z<=r&&h-point[i].z<=r) flag=true;//存在即与上表面相交又与下表面相交的点
            else if(point[i].z<=r) bottom.push_back(i);//只与下表面相交的点
            else if(h-point[i].z<=r) top.push_back(i);//只与上表面相交的点
        }
        
        for(int i=1;i<=n;i++)//将可以互相到达的两个点合并
            for(int j=i+1;j<=n;j++)
            {
                int x=point[i].x-point[j].x;
                int y=point[i].y-point[j].y;
                int z=point[i].z-point[j].z;
                int dist=x*x+y*y+z*z;//dist是距离的平方
                if(dist<=4*r*r)//两点之间的距离小于等于2r则可以互相到达
                {
                    int pi=find(i),pj=find(j);//提取i,j的根节点
                    p[pi]=pj;//合并两个集合
                }
            }
        for(int i=0;i<bottom.size();i++)
            for(int j=0;j<top.size();j++)
            {
                if(find(bottom[i])==find(top[j])) flag=true;
            }
        if(flag==true) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
}

方法三:dfs

将所有可以互相到达的两个点之间连一条边。 用bottom存与下表面相交的点 。先判断这些点是否与上表面相交。从这些点开始dfs()搜索,对每个搜索到的点判断其是否与上表面相交。若能搜索到这样的点输出yes,若无法搜索到这样的点输出no

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+10;
int h[N],e[N*N],ne[N*N],idx,T,n,H,r;
bool flag;//标记能否从下表面到上表面
bool st[N];//标记走过的点
vector<int>bottom;//保存与下表面相交或相切的点,从bottom中的点开始dfs
struct Point
{
    int x,y,z;
}point[N];
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u)
{
    if(H-point[u].z<=r) flag=true;//u节点与下表面相交同时也与上表面相交
    st[u]=true;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(st[j]==false)//该点之前没有被搜索过
        {
            if(H-point[j].z<=r) flag=true;//判断该节点是否与上表面相交
            dfs(j);//继续搜索下一个节点
        }
    }
}
signed main()
{    
    cin>>T;
    while(T--)
    {
        flag=false;
        idx=0;
        bottom.clear();
        memset(st,0,sizeof st);
        memset(h,-1,sizeof h);
        cin>>n>>H>>r;
        for(int i=0;i<n;i++)//输入n个点的坐标
        {
            cin>>point[i].x>>point[i].y>>point[i].z;
            if(point[i].z<=r) bottom.push_back(i);//与下表面相交的点存入bottom
        }
        for(int i=0;i<n;i++)//建图,将可以互相到达的两个点之间连一条边
            for(int j=i+1;j<n;j++)
            {
                int x=point[i].x-point[j].x;
                int y=point[i].y-point[j].y;
                int z=point[i].z-point[j].z;
                double dist=sqrt(x*x+y*y+z*z);
                if(dist<=2.0*r)//两点之间的距离小于等于2r则可以互相到达
                {
                    add(i,j);
                    add(j,i);
                }
            }
        for(int i=0;i<bottom.size();i++) dfs(bottom[i]);
        if(flag==true) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
}