三种解法: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;
}
}