进阶实验6-3.4 拯救007(升级版) (30 分)

这题很多人可能会用Dijkstra算法或者Floyd算法去算最短路径,其实没有必要,仔细想想这个题其实是一个无权图的单源最短路径问题,针对这种题我们只要用一下BFS就可以轻松愉快解决了,但是要处理好一些细节。

⚠️:1、原点是小岛的圆心(0,0),但是岛是有半径的,所以第一跳只能跳不在岛上且距离圆心<=D+7.5的小鳄鱼。具体在程序中实现就是一进入BFS,让James Bond第一跳能到达的且不在岛上的小鳄鱼入队。

2、四条岸边看作一个顶点。

3、因为有可能有多条最短路,所以题目要求输出第一跳跳跃距离最短的(题目保证这种结果是唯一的)。想法是把合法的第一跳从小到大排序,然后把第一跳从小到大依次压入队列中,从而在BFS的过程中,永远都是第一跳距离较短的子孙后代先被访问,一旦有从个子孙结点可以直接跳到岸上,BFS提前结束,得到的就是从小岛到岸边的最短路径。因为即使后面还有相等的最短路径的话,那也一定是第一跳距离比较大的最短路径。这个问题其实不是那么直白,可以画一颗?树利用树的层序遍历去理解,实际上树的层序遍历和图的广度优先搜索本质是一样的,下面是我当时?推导时画的图,你们可以看一下。

4、在岸上的小鳄鱼并不会让我们的算法挂掉,我们是通过岸上的小鳄鱼跳上岸的,那么其实我们会从跳到岸上小鳄鱼的前一只小鳄鱼直接上岸。

5、还有一个小细节就是,有可能James Bond跳得很远,不需要通过小鳄鱼就直接上岸的话,要在main函数里面设置一个判断 语句解决掉这个情况,BFS中我是默认至少要通过一只合法小鳄鱼才上岸的。

6、记录路径只要设置一个path[]数组即可,每次访问一个结点V就设置path[V] = V的父结点,最后利用一下堆栈反序输出即可。

7、还有一个小细节是,我是利用dist[v]是否等于无穷大来判断在BFS过程中这个顶点是否被访问过。陈姥姥教的小技巧,这样就可以省下不少空间,否则再开一个visited[]数组有点多余。

上面的图是第3点的配图

总的来说,这个题想要一次性拿?真的不容易,纸上得来终觉浅,绝知此事要躬行。

下面是代码:

#include<cstdio>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#define MAXV 105
#define INF 1000000000
using namespace std;
int n,vis[MAXV] = { false },dist[MAXV],path[MAXV];
int d;
struct GNode
{
	int x,y;
	double firstJump;
}G[MAXV];
bool cmp(GNode a,GNode b)
{
	return a.firstJump<b.firstJump;
}
void BFS()
{
	int u,v,w; queue<int> q;
	double jumpEdge,gap;
	for(v=0; v<n; v++)
	{
		if(G[v].firstJump>7.5&&G[v].firstJump<=d+7.5)
		{
			q.push(v); dist[v] = 1; path[v] = -1; //printf("q.push(%d) %d %d \n",v,G[v].x,G[v].y);
		}
	}
	while(!q.empty())
	{
		u = q.front(); q.pop();
		if(G[u].x>=0&&G[u].y>=0)
			jumpEdge = min(50-G[u].x,50-G[u].y);
		else if(G[u].x>=0&&G[u].y<=0)
			jumpEdge = min(50-G[u].x,50+G[u].y);
		else if(G[u].x<=0&&G[u].y>=0)
			jumpEdge = min(50+G[u].x,50-G[u].y);
		else if(G[u].x<=0&&G[u].y<=0)
			jumpEdge = min(50+G[u].x,50+G[u].y);
		if(jumpEdge<=d)
		{
            if(dist[n]==INF)
            {
				dist[n] = dist[u]+1;// printf("dist[%d] = dist[%d]+1\n",n,u);
				path[n] = u; break;
            }
		}
		for(w=0; w<n; w++)
		{
			if(dist[w]==INF)
			{
				gap = sqrt((G[u].x-G[w].x)*(G[u].x-G[w].x)+(G[u].y-G[w].y)*(G[u].y-G[w].y));
				if(gap <= d)
				{
					dist[w] = dist[u]+1; q.push(w);
					path[w] = u; //printf("dist[%d] = dist[%d]+1;path[%d] = %d;\n",w,u,w,u);
				}
			}
		}
	}
}
int main()
{	
	fill(dist,dist+MAXV,INF);
	fill(path,path+MAXV,-1);
	int i,u;
	stack<int> s;
	scanf("%d%d",&n,&d);
	if(d>=42.5)
	{
		printf("1\n"); return 0;
	}
	for(i=0; i<n; i++)
	{
		scanf("%d%d",&G[i].x,&G[i].y);
		G[i].firstJump = sqrt(G[i].x*G[i].x+G[i].y*G[i].y);
	}
	sort(G,G+n,cmp);
	BFS();
	if(dist[n]==INF)
	{
		printf("0\n"); return 0;
	}
	printf("%d\n",dist[n]);
	u = path[n];
	int num = 0;
	while(u!=-1)
	{
		s.push(u);
		u = path[u];num++;
	}
	for(i=0; i<num;i++)
	{
		printf("%d %d\n",G[s.top()].x,G[s.top()].y);
		s.pop();
	}
	return 0;
}

 

  • 11
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值