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 }