• [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 题目总结

  • 相关阅读:
    Android学习笔记一:项目目录结构
    ReactNative踩坑日志——OnPress随着render()执行被自动调用?
    ReactNative踩坑日志——使用async/await语法解决网络请求的异步导致的指令执行顺序错乱问题
    ReactNative踩坑日志——代码执行方式(面向对象)
    ReactNative踩坑日志——fetch如何向服务器传递参数
    ReactNative踩坑日志——函数绑定this
    ReactNative踩坑日志——如何实现删除scrollview中的视图
    ReactNative踩坑日志——页面跳转之——Undefined is not an Object(evaluating this2.props.navigation.navigate)
    Git使用笔记
    CSS拾遗
  • 原文地址:https://www.cnblogs.com/cnoodle/p/12820573.html
Copyright © 2020-2023  润新知