一、题目
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.
Input Specification:
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.
Output Specification:
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.
Sample Input 1:
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
Sample Output 1:
4
0 11
10 21
10 35
Sample Input 2:
4 13
-12 12
12 12
-12 -12
12 -12
Sample Output 2:
0
二、解答
import java.util.*;
import java.util.jar.JarEntry;
public class Main{
static ArrayList<Integer> queue = new ArrayList<>();
static int front = -1;
static int rear = -1;
public static void main(String[] args) {
//Get data and Transfer to map
Scanner sc = new Scanner(System.in);
int nodeNum = sc.nextInt() + 2;
int maxDis = sc.nextInt();
if (7.5 + maxDis >= 50 || 7.5 - maxDis <= -50 || 7.5 + maxDis >= 50 || 7.5 - maxDis <= -50){
System.out.println(1);
}else {
int[][] posMap = new int[nodeNum - 2][2];
int[][] judgeMap = new int[nodeNum][nodeNum];//算上岸边和出发点
for (int i = 0; i < nodeNum - 2; i++) {
posMap[i][0] = sc.nextInt();
posMap[i][1] = sc.nextInt();
}
sc.close();
judgeEdge(maxDis, posMap, judgeMap);
judgeDis(maxDis, posMap, judgeMap);
//单源最短路问题 无权图:BFS即可实现 通过记录上一个点的位置来记录路径以及距离
int[] path = new int[nodeNum];
for (int i = 0; i < nodeNum; i++) {
path[i] = -1;
}
boolean[] visited = new boolean[nodeNum]; // 初始化默认全为false
//要求步数相同的时候,第一跳需要最短,因此BFS过程第一次广度遍历时按照距离最短的顺序压入队列
int curNode = 0;
int[] minStart = getMinStart(judgeMap[curNode], posMap);
visited[curNode] = true;
for (int i = 0; i < minStart.length; i++) {
inQue(minStart[i]);
path[minStart[i]] = curNode;
visited[minStart[i]] = true;
}
curNode = outQue();
//开始后续BFS
while (curNode != -1) {
for (int i = 0; i < nodeNum; i++) {
if (judgeMap[curNode][i] != 0 && visited[i] == false) {
inQue(i);
path[i] = curNode;
visited[i] = true;
}
}
if (path[nodeNum - 1] != -1) break;
curNode = outQue();
}
//路径以及生成,输出结果
int index = nodeNum - 1;
if (path[index] == -1) System.out.println(0);
else {
int jumps = 0;
ArrayList<Integer> resPath = new ArrayList<>();
while (index != 0) {
index = path[index];
jumps++;
resPath.add(index);
}
System.out.println(jumps);
//for循环i最初的赋值也需要进行条件判断,满足才会执行循环体内容
for (int i = jumps - 2; i >= 0; i--) {
int temp = resPath.get(i) - 1;
System.out.println(posMap[temp][0] + " " + posMap[temp][1]);
}
}
}
}
public static int[] getMinStart(int[] connNode, int[][] posMap){
HashMap<Integer, Integer> starts = new HashMap<>();
for (int i = 1; i < connNode.length; i++) {
if (connNode[i] != 0){ starts.put(calDis(posMap[i-1][0],posMap[i-1][1]),i);}
}
ArrayList<Integer> keys = new ArrayList<Integer>(starts.keySet());
Collections.sort(keys);
int[] res = new int[keys.size()];
for (int i = 0; i < keys.size(); i++) {
res[i] = starts.get(keys.get(i));
}
return res;
}
public static int calDis(int x, int y){
return x*x+y*y;
}
//Queue
public static void inQue(int num){
queue.add(num);
front++;
}
public static int outQue(){
if (rear >= front) return -1;
return queue.get(++rear);
}
//judge position
public static void judgeEdge(int m, int[][] posMap, int[][] judgeMap){
int edgeIndex = posMap.length + 1; //将岸边放在数组最后一个,出发点放在数组第一位
int startIndex = 0;
for (int i = 0; i < posMap.length; i++) {
int x = posMap[i][0], y = posMap[i][1];
//判断与岸边距离
if (x + m >= 50 || x - m <= -50 || y + m >= 50 || y - m <= -50) {
judgeMap[i+1][edgeIndex] = 1;
judgeMap[edgeIndex][i+1] = 1;
}
//判断与出发点距离
int dis = m*m + 15*m + 15*15/4;
if (x*x + y*y <= dis) {
judgeMap[startIndex][i+1] = 1;
judgeMap[i+1][startIndex] = 1;
}
}
}
public static void judgeDis(int m, int[][] posMap, int[][] judgeMap){
//judge数组的第一个元素和数组最后一个元素已经进行判断
for (int i = 0; i < posMap.length; i++) {
int x1 = posMap[i][0], y1 = posMap[i][1];
for (int j = 0; j < posMap.length; j++) {
int x2 = posMap[j][0], y2 = posMap[j][1];
if ((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2) <= m * m) {
judgeMap[i+1][j+1] = 1;
judgeMap[j+1][i+1] = 1;
}
}
}
}
}
思路:
- 这是一道图的什么类型的题? 先把距离转化为普通的二维数组图,可见这是一题图的单源无权最短路问题,可以直接用BFS实现。
- 如何实现路径的记录?通过记录上一个点的位置,即可遍历输出路径
- 踩过的坑:①一步可以直接跨到岸边的情况 ②如果需要的步数相同,则选择第一步迈出距离最短的路径
思考:
集合排序问题:
ArrayList<Integer> keys = new ArrayList<Integer>(starts.keySet());
Collections.sort(keys);