[LeetCode] 1197. Minimum Knight Moves

In an infinite chess board with coordinates from -infinity to +infinity, you have a knight at square [0, 0].

A knight has 8 possible moves it can make, as illustrated below. Each move is two squares in a cardinal direction, then one square in an orthogonal direction.

Return the minimum number of steps needed to move the knight to the square [x, y].  It is guaranteed the answer exists.

Example 1:

Input: x = 2, y = 1
Output: 1
Explanation: [0, 0] → [2, 1]

Example 2:

Input: x = 5, y = 5
Output: 4
Explanation: [0, 0] → [2, 1] → [4, 2] → [3, 4] → [5, 5] 

Constraints:

  • |x| + |y| <= 300

进击的骑士。一个坐标可以从 -infinity 延伸到 +infinity 的 无限大的 棋盘上,你的 骑士 驻扎在坐标为 [0, 0] 的方格里。骑士的走法和中国象棋中的马相似,走 “日” 字:即先向左(或右)走 1 格,再向上(或下)走 2 格;或先向左(或右)走 2 格,再向上(或下)走 1 格。每次移动,他都可以按图示八个方向之一前进。现在,骑士需要前去征服坐标为 [x, y] 的部落,请你为他规划路线。最后返回所需的最小移动次数即可。本题确保答案是一定存在的。

国际象棋的规则跟中国象棋类似,都是马走日。这个题基本上就是在问你,你现在有一个马在[0, 0],问你最少需要走几步能走到一个目标点[x, y]。题目规定了是一定能走得到的点。我给出如下这个图,显示骑士可以跳的八个空格各自的坐标。

 

这是在无向图上找最少的步数,所以思路是BFS。你需要意识到无论[x, y]在哪个象限,只要他与(0,0)的距离相等,实际在哪个象限都是等价的。所以一开始我们为了计算方便,就可以把input的这个[x, y]坐标取绝对值。这里我们为了避免走到重复的点,我们需要一个hashset记录遍历过的点。其他部分都是BFS的模板了。有一个细节需要注意的是虽然我们对[x, y]取了绝对值,但是并不意味着路径中不会经过坐标值为负的点。例子就是(0, 0) -> (2, -1) -> (1, 1)。这也就是为什么代码中26行把遍历过的点加入hashset的时候额外判断横纵坐标是否都 >= -1的原因了。

另外一个优化就是为什么26行我可以限制i和j的范围,那是因为如果你再往外跳,实际你的步数只有可能是更多。例子,比如还是从(0, 0) -> (1, 1),如果你一开始跳到(2,1),你再往比如(4,2)或者(4,0)跳,实际是没有帮助的。

时间O(mn) 

空间O(mn)

Java实现

 1 class Solution {
 2     public int minKnightMoves(int x, int y) {
 3         int[][] dirs = new int[][] { { -1, -2 }, { -1, 2 }, { 1, -2 }, { 1, 2 }, { -2, -1 }, { -2, 1 }, { 2, -1 },
 4                 { 2, 1 } };
 5         x = Math.abs(x);
 6         y = Math.abs(y);
 7         HashSet<String> visited = new HashSet<>();
 8         Queue<int[]> queue = new LinkedList<>();
 9         queue.offer(new int[] { 0, 0 });
10         visited.add("0,0");
11 
12         int step = 0;
13         while (!queue.isEmpty()) {
14             int size = queue.size();
15             while (size-- > 0) {
16                 int[] cur = queue.poll();
17                 if (cur[0] == x && cur[1] == y) {
18                     return step;
19                 }
20 
21                 for (int[] dir : dirs) {
22                     int i = cur[0] + dir[0];
23                     int j = cur[1] + dir[1];
24                     // (0, 0) -> (2, -1) -> (1, 1)
25                     // +2的意思是多给两个格子的空间以便于骑士跳出去再跳回来的操作
26                     if (!visited.contains(i + "," + j) && i >= -1 && j >= -1 && i <= x + 2 && j <= y + 2) {
27                         queue.offer(new int[] { i, j });
28                         visited.add(i + "," + j);
29                     }
30                 }
31             }
32             step++;
33         }
34         return -1;
35     }
36 }

LeetCode 题目总结

原文地址:https://www.cnblogs.com/cnoodle/p/12820573.html