PTA_19 _07-图5 Saving James Bond - Hard Version

图_19 _07-图5 Saving James Bond - Hard Version

题目描述

This time let us consider the situation in the movie “Live and Let Die” in which James Bond, the world’s most famous spy, was captured by a group of drug dealers. He was sent to a small piece of land at the center of a lake filled with crocodiles. There he performed the most daring action to escape – he jumped onto the head of the nearest crocodile! Before the animal realized what was happening, James jumped again onto the next big head… Finally he reached the bank before the last crocodile could bite him (actually the stunt man was caught by the big mouth and barely escaped with his extra thick boot).

Assume that the lake is a 100 by 100 square one. Assume that the center of the lake is at (0,0) and the northeast corner at (50,50). The central island is a disk centered at (0,0) with the diameter of 15. A number of crocodiles are in the lake at various positions. Given the coordinates of each crocodile and the distance that James could jump, you must tell him a shortest path to reach one of the banks. The length of a path is the number of jumps that James has to make.

这一次让我们考虑电影中的“活生生和让人死亡”的情形,詹姆斯·邦德是世界上最著名的间谍,他被一群毒品贩子抓住了。他被送到湖心的一小块土地上,那里到处都是鳄鱼。在那里,他做了最大胆的逃跑动作——他跳到了最近一条鳄鱼的头上!在动物意识到发生了什么之前,詹姆斯又跳到了下一个大脑袋上。最后,在最后一条鳄鱼咬他之前,他终于到达了河岸(事实上,特技演员被大嘴抓住了,只穿了一双厚厚的靴子就勉强逃脱了)。

假设这个湖是一个100乘100平方米的湖。假设湖的中心位于(0,0),东北角位于(50,50)。中心岛是一个以(0,0)为中心、直径为15的圆盘。许多鳄鱼在湖中的不同位置。根据每只鳄鱼的坐标和詹姆斯能跳的距离,你必须告诉他到达其中一个河岸的最短路径。路径的长度是James必须进行的跳跃次数。

输入样式

Each input file contains one test case. Each case starts with a line containing two positive integers N (≤100), the number of crocodiles, and D, the maximum distance that James could jump. Then N lines follow, each containing the (x,y) location of a crocodile. Note that no two crocodiles are staying at the same position.

每个输入文件包含一个测试用例。每种情况都从一行开始,该行包含两个正整数N(≤100),鳄鱼的数量,和D,詹姆斯能跳的最大距离。接下来是N行,每行包含鳄鱼的(x,y)位置。请注意,没有两只鳄鱼停留在同一位置。

17 15
10 -21
10 21
-40 10
30 -50
20 40
35 10
0 -10
-25 22
40 -40
-30 30
-10 22
0 11
25 21
25 10
10 10
10 35
-30 10

**************************

4 13
-12 12
12 12
-12 -12
12 -12

输出样式

For each test case, if James can escape, output in one line the minimum number of jumps he must make. Then starting from the next line, output the position (x,y) of each crocodile on the path, each pair in one line, from the island to the bank. If it is impossible for James to escape that way, simply give him 0 as the number of jumps. If there are many shortest paths, just output the one with the minimum first jump, which is guaranteed to be unique.

对于每个测试用例,如果James能够逃脱,则在一行中输出他必须进行的最小跳跃次数。然后从下一行开始,输出路径上每只鳄鱼的位置(x,y),每对鳄鱼排成一行,从岛屿到河岸。如果詹姆斯无法以这种方式逃脱,只需给他0作为跳跃次数。如果有多条最短路径,只需输出第一次跳转最小的路径,这保证是唯一的。

4
0 11
10 21
10 35


********************

0

算法分析

在上一题中的easy版本中,拯救007采用的深度优先遍历的算法,其算法逻辑是,首先判断,鳄鱼是否被访问过,然后判断这条鳄鱼减去小岛半径,能否被跳跃过去,若能,则开启深度优先遍历,深度优先遍历的每一步都判别这条鳄鱼能不能跳上案,如果不能则去遍历剩下未被跳跃的鳄鱼,然后去判断能否跳到。但这个算法实现能找到逃生道路,却找不到最短逃生道路。可以在这个算法的基础上进行,优化。本题最优的方法便是采用广度优先遍历的算法(BFS),广度优先算法适合对跳跃步数进行分析,之前曾考虑过用Dijkstra算法,但好像权重不知道咋算。

#include<stdio.h>
#include<cstdio>
#include<stack>
#include<iostream>
#include<math.h>
#include<queue>
#include<algorithm>
using namespace std;

#define MAXSIZE 100



struct EyuNode
{
	int x;
	int y;
}eyu[MAXSIZE];

int minlen = 50 - 15 / 2;
int jump2 = 0;
int path[MAXSIZE] = { -1 };

bool visited[MAXSIZE] = { false };

void CreateEyu(int num)
{
	int i, j;
	for (i = 0; i < num; i++)//输入的鳄鱼坐标是有关原点相对的坐标
	{
		cin >> eyu[i].x >> eyu[i].y;
	}
}


int firstjump(int i, int jump)//直接将鳄鱼的坐标与源点的绝对距离进行判断
{
	int t;
	t = pow(eyu[i].x, 2) + pow(eyu[i].y, 2);
	t = sqrt(t);
	if (t <= jump + 7.5)//得加上小岛的半径,小岛是半径为15的圆
		return t;
	else
		return 0;
}


int IsSafe(int i, int jump)//因为河岸边的距离都为50,50,只需计算这条鳄鱼的x,y与河岸边的距离小于跳跃长度就可以
{
	int x_distance, y_distance;
	x_distance = abs(eyu[i].x - 50);
	y_distance = abs(eyu[i].y - 50);
	if (x_distance <= jump || y_distance <= jump)
	{
		return 1;
	}
	else
		return 0;
}

int JumpAble(int i, int w, int jump)
{
	int t;
	t = pow(eyu[i].x - eyu[w].x, 2) + pow(eyu[i].y - eyu[w].y, 2);
	t = sqrt(t);
	if (t <= jump)//得加上小岛的半径,小岛是半径为15的圆
		return 1;
	else
		return 0;
}



int DFS(int i, int num, int jump)
{
	int w;
	int answer = 0;
	visited[i] = true;
	//首先判断这条鳄鱼能不能直接跳到岸上
	if (IsSafe(i, jump))
		return 1;
	else
	{
		for (w = 0; w < num; w++)//遍历所有的其余的鳄鱼
		{
			if (!visited[w] && JumpAble(i, w, jump))//如果这条鳄鱼没被访问过,并且能够跳的上,则进行下一次的深度遍历
			{
				answer = DFS(w, num, jump);

				if (answer == 1)
				{
					break;
				}
			}
		}
	}
	return answer;

}


void save007(int num, int jump)
{
	int i;
	int answer = 0;
	for (i = 0; i < num; i++)
	{
		visited[i] = false;//初始化标记数组
	}


	for (i = 0; i < num; i++)//遍历所有的鳄鱼,对每条鳄鱼路径进行判断
	{
		if (!visited[i] && firstjump(i, jump))//首先判断鳄鱼是否被访问过,同时判断,能否可以跳到鳄鱼的身上
		{
			answer = DFS(i, num, jump);
		}
	}
	if (answer)
	{
		cout << "yes" << endl;
	}
	else
	{
		cout << "no" << endl;
	}
}

bool cmp(int x, int y)
{
	return firstjump(x, jump2) < firstjump(y, jump2);
}

int BFS_search(int num, int jump)//使用bfs判断要踩多少条鳄鱼才能上岸
{
	int b[101];
	queue<int>q;//新建一个队列
	//将第一步能踩到的小鳄鱼按从小到大的顺序进入队列,输出结果要保证在踩的鳄鱼数量相等时,输出第一步距离最短的那条路径
	int i, j, k;
	for (i = 0; i < num; i++)
	{
		b[i] = i;
	}

	//对所有鳄鱼按照从小到大进行排序
	//void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
	//sort函数包含在头文件为#include<algorithm>的c++标准库中,调用标准库里的排序方法可以实现对数据的排序
	//(1)第一个参数first:是要排序的数组的起始地址。
	//(2)第二个参数last:是结束的地址(最后一个数据的后一个数据的地址)
	//(3)第三个参数comp是排序的方法:可以是从升序也可是降序。如果第三个参数不写,则默认的排序方法是从小到大排序。

	sort(b, b + num, cmp);

	int last;
	for (i = 0; i < num; i++)
	{
		if (firstjump(b[i], jump))
		{
			q.push(b[i]);
			visited[b[i]] = true;
			last = b[i];
		}
	}
	int step = 2;//记录最少要跳跃的次数
	int tail = 0;
	while (!q.empty())
	{
		int p = q.front();
		q.pop();
		if (IsSafe(p, jump))
		{
			int k = 1;
			stack<int>s;
			cout << step << endl;
			while (k < step)
			{
				s.push(p);
				p = path[p];
				k++;
			}

			while (!s.empty())
			{
				p = s.top();
				s.pop();
				cout << eyu[p].x << " " << eyu[p].y << endl;
			}
			return 0;
		}

		for (i = 0; i < num; i++)
		{
			if (!visited[i] && JumpAble(p, i, jump))
			{
				q.push(i);
				path[i] = p;//记录当前入队结点的父节点
				visited[i] = true;
				tail = i;//指向下一层最后一个元素
			}
		}

		if (last == p)//即将进入下一层
		{
			step += 1;
			last = tail;
		}
	}
	if (q.empty())
	{
		cout << "0" << endl;
	}
	return 1;
}





int main()
{
	int num, jump;
	cin >> num >> jump;
	jump2 = jump;
	CreateEyu(num);
	if (jump >= minlen)
	{
		cout << "1" << endl;//几乎不太可能,这样题都不用做了
		return 0;
	}
	BFS_search(num, jump);

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值