[LeetCode] 1197. Minimum Knight Moves

进击的骑士。一个坐标可以从 -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
1
2
3
4
int 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 }

猜你喜欢

转载自www.cnblogs.com/aaronliu1991/p/12820573.html
今日推荐