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 }