P3958 奶酪 (DFS&BFS双解)

链接: 原题链接


输入输出样例

输入 #1

3 
2 4 1 
0 0 1 
0 0 3 
2 5 1 
0 0 1 
0 0 4 
2 5 2 
0 0 2 
2 0 4

输出 #1

Yes
No
Yes


思路讲解

本題是一个简单的DFS/BFS搜索问题,也可以用并查集来做。
下面讲解一下DFS思路。
首先,奶酪上的洞个数是不确定的,并且输入的洞的位置(主要是高低)不是有序的,因此要先根据高度对洞进行排序。
DFS中要判断两个洞是否相连,即两洞之间距离是否小于等于球直径(注意是直径)。难点在于如何判断是否可以穿过奶酪。只有从下表面通过一个个相连的洞然后穿过与上表面相连的洞才能穿过奶酪。从最下面的一个洞开始走,走过的洞标记已经走过,凡是走过的洞都不再走,但如果前方不能再走了还返回这个洞,寻找下一个能连接到此洞的洞,这样的话就可以将每一个能和底边连接到的洞全部判断一遍,只要其中有洞连接到顶端,就用flag记录为1。再进行适当的剪枝!
注意:与底边相连的洞可能有很多,应该从每一个与底边相连的洞开始DFS一遍,直到有穿过奶酪的路径为止。因此在主函数中我们用循环把能连接到底边的所有洞DFS一遍即可。

代码实现

DFS:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1005;
int t;
long long n,h,r; //注意变量类型要开 long long 
struct node{
	long long x,y,z;
}yuan[MAXN];
int v[MAXN];
bool cmp(node a,node b)
{
	return a.z<b.z;
}
int flag;
void DFS(int a); //当前要判断的洞 
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		flag=0;
		memset(v,0,sizeof(v));// 因为有多组数据,所以每次都要初始化 
		scanf("%ld%ld%ld",&n,&h,&r);
		for(int i=1;i<=n;i++)
			scanf("%ld%ld%ld",&yuan[i].x,&yuan[i].y,&yuan[i].z);
		sort(yuan+1,yuan+1+n,cmp);
		for(int i=1;i<=n;i++)
		{
			if(flag==1)break;  // 剪枝 
			if(v[i]==0&&yuan[i].z<=r)
			{
				v[i]=1;
				DFS(i);
			}		
		}
		if(flag)printf("Yes\n");
		else printf("No\n");
	}
}
void DFS(int a)
{
	if(yuan[a].z+r>=h)
	{
		flag=1;
		return; 
	}
	for(int i=a+1;i<=n;i++)
	{
		if(flag==1)return; //剪枝 
		long long tx,ty,tz,as;
		tx=(yuan[i].x-yuan[a].x)*(yuan[i].x-yuan[a].x);
		ty=(yuan[i].y-yuan[a].y)*(yuan[i].y-yuan[a].y);
		tz=(yuan[i].z-yuan[a].z)*(yuan[i].z-yuan[a].z);
		as=tx+ty+tz; //距离的平方 
		long long dis=2*r;
		if(v[i]==0&&as<=dis*dis)//
		{
			v[i]=1;
			DFS(i);
			v[i]=0;
		}
	}
}
 

BFS:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1005;
int t;
long long n,h,r;
int flag;
int v[MAXN]; 
struct node{
	long long x,y,z;
}yuan[MAXN],start,temp;
bool cmp(node a,node b)
{
	return a.z<b.z;
}
int dis(node a,node b)
{
	long long tyx,tyy,tyz,as;
	tyx=a.x-b.x;
	tyy=a.y-b.y;
	tyz=a.z-b.z;
	as=tyx*tyx+tyy*tyy+tyz*tyz;
	if(as>2*r*2*r) return 0;
	else return 1;
}
queue<node>d;
void BFS(int a);//从第几号开始判断 
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		flag=0;
		memset(v,0,sizeof(v));
		scanf("%ld%ld%ld",&n,&h,&r);
		for(int i=1;i<=n;i++)
			scanf("%ld%ld%ld",&yuan[i].x,&yuan[i].y,&yuan[i].z);
		sort(yuan+1,yuan+1+n,cmp);
		for(int i=1;i<=n;i++)
			if(v[i]==0&&yuan[i].z<=r)
			{
				if(flag==1)break;
				v[i]=1;
				BFS(i);	
			}
		if(flag)printf("Yes\n");
		else printf("No\n");		
	}
}
void BFS(int a)//从第几号开始判断 
{
	d=queue<node>();//清空队列 
	start=yuan[a];
	d.push(start);
	while(!d.empty())
	{
		temp=d.front();
		if(temp.z+r>=h)
		{
			flag=1;  //只要有一种路径穿过奶酪就标记 
			break;  //退出 
		}
		for(int k=a+1;k<=n;k++)
		{
			if(v[k]==0&&dis(yuan[k],temp))
			{
				v[k]=1;
				d.push(yuan[k]);
			}
		}
		d.pop();
	}
}

继续加油!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值