进击的骑士。一个坐标可以从 -infinity 延伸到 +infinity 的 无限大的 棋盘上,你的 骑士 驻扎在坐标为 [0, 0] 的方格里。骑士的走法和中国象棋中的马相似,走 “日” 字:即先向左(或右)走 1 格,再向上(或下)走 2 格;或先向左(或右)走 2 格,再向上(或下)走 1 格。每次移动,他都可以按图示八个方向之一前进。现在,骑士需要前去征服坐标为 [x, y] 的部落,请你为他规划路线。最后返回所需的最小移动次数即可。本题确保答案是一定存在的。例子,
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]
国际象棋的规则跟中国象棋类似,都是马走日。这个题基本上就是在问你,你现在有一个马在[0, 0],问你最少需要走几步能走到一个目标点[x, y]。题目规定了是一定能走得到的点。
这是在无向图上找最少的步数,所以思路是BFS,但是有一些问题需要处理,首先,单纯的BFS很可能在某些分支上会让你越走越远,因为题目说了棋盘的尺寸是无限大的,需要有一个办法来限制,否则一定会超时。至于限制的办法,是试图将棋盘的范围缩小。我这里引用了一个网友的思路,直接粘贴过来。
如果简单来为棋盘来划定一下界限,起点和终点两个坐标(x1,y1), (x2,y2)围成的四边形应该是相对合理的范围。但是考虑到如果两个点在一条直线上,或者两点间的距离过近而无法完成日字跳跃的情况,我们可以将划定的棋盘范围四周分别扩大2格即可。
此外,为了方便,我们可以将起点和终点平移到正数坐标范围内。举个例子,比如:
Code 1234int
x = -
5
;
// 终点x
int
y = -
3
;
// 终点y
int
startX =
0
;
// 起点x
int
starty =
0
;
// 起点y
先将终点移动到(0, 0),相应的起点会被移动到(5, 3)。此外,要考虑到上面提到的扩大2格的操作,因此,终点T应该是(2, 2),起点S则是(7, 5)。相应的棋盘范围则是(0, 0) 到(9, 7)之间的范围,即下图蓝色区域。
剩下的部分就是BFS的常规操作,另外需要用一个二维数组visited记录访问过的坐标。
时间O(mn)
空间O(mn)
Java实现
1 class Solution { 2 public int minKnightMoves(int x, int y) { 3 int[][] dirs = { { -1, -2 }, { -1, 2 }, { 1, -2 }, { 1, 2 }, { 2, -1 }, { 2, 1 }, { -2, 1 }, { -2, -1 } }; 4 // starting point is [0, 0] 5 int startX = 0; 6 int startY = 0; 7 if (x < 0) { 8 startX = -x; 9 x = 0; 10 } 11 if (y < 0) { 12 startY = -y; 13 y = 0; 14 } 15 16 // expand the [] with two more cells 17 startX += 2; 18 startY += 2; 19 x += 2; 20 y += 2; 21 22 // expand the chess board 23 int right = Math.max(startX, x) + 2; 24 int bottom = Math.max(startY, y) + 2; 25 26 // start to do BFS 27 Queue<int[]> queue = new LinkedList<>(); 28 queue.offer(new int[] { startX, startY }); 29 boolean[][] visited = new boolean[right + 1][bottom + 1]; 30 visited[startX][startY] = true; 31 32 int res = 0; 33 while (!queue.isEmpty()) { 34 int size = queue.size(); 35 while (size-- > 0) { 36 int[] cur = queue.poll(); 37 if (cur[0] == x && cur[1] == y) { 38 return res; 39 } 40 for (int[] dir : dirs) { 41 int nextX = cur[0] + dir[0]; 42 int nextY = cur[1] + dir[1]; 43 if (nextX <= right && nextX >= 0 && nextY <= bottom && nextY >= 0 && !visited[nextX][nextY]) { 44 visited[nextX][nextY] = true; 45 queue.offer(new int[] { nextX, nextY }); 46 } 47 } 48 } 49 res++; 50 } 51 return -1; 52 } 53 }