题目:机器人大冒险

  • 力扣团队买了一个可编程机器人,机器人初始位置在原点(0, 0)。小伙伴事先给机器人输入一串指令command,机器人就会无限循环这条指令的步骤进行移动。指令有两种:
  1. U: 向y轴正方向移动一格
  2. R: 向x轴正方向移动一格。
  • 不幸的是,在xy平面上还有一些障碍物,他们的坐标用obstacles表示。机器人一旦碰到障碍物就会被损毁。
  • 给定终点坐标(x,y),返回机器人能否完好地到达终点。如果能,返回true;否则返回false
示例 1:
输入:command = "URR", obstacles = [], x = 3, y = 2
输出:true
解释:U(0, 1) -> R(1, 1) -> R(2, 1) -> U(2, 2) -> R(3, 2)。
示例 2:
输入:command = "URR", obstacles = [[2, 2]], x = 3, y = 2
输出:false
解释:机器人在到达终点前会碰到(2, 2)的障碍物。
示例 3:
输入:command = "URR", obstacles = [[4, 2]], x = 3, y = 2
输出:true
解释:到达终点后,再碰到障碍物也不影响返回结果。
限制:
  • 2 <= command的长度<= 1000
  • command由U,R构成,且至少有一个U,至少有一个R
  • 0 <= x <= 1e9,0 <= y <= 1e9
  • 0 <= obstacles的长度<= 1000
  • obstacles[i]不为原点或者终点

来源:力扣(LeetCode)LCP.3

链接:https://leetcode-cn.com/problems/programmable-robot

分析:

  • 由于x和y最多10亿,一个一个遍历肯定不现实。
  • 经过思考发现,我们可以找每一个要找的点的前一行或前一列的最后一个。
  • 比如终点为(x, y),我们可以找到x-1或者y-1时的最后一个位置。
  • 然后再遍历一遍就能判断是否经过该点。因为command至少有一个U和R。

代码:

class Solution {
    public boolean robot(String command, int[][] obstacles, int x, int y) {
        int dx = 0, dy = 0;
        char[] cmd = command.toCharArray(); // 把String转为数组,方便遍历。
        for (char c : cmd) { // 算出up和right各有多少个。
            if (c == 'U') dy++;
            else dx++;
        }
        int ans = isPassed(cmd, x, y, dx, dy); // 拿到走到终点的次数。
        // 先看isPassed函数再往下看。
        /*
            为什么isPassed要拿到走的总次数而不直接返回true或false呢
            比如你发现有一个obstacle是经过的,那么最终答案并不一定是false,
            因为如果终点在这个点的前面,那么机器人根本不会走到那个点。答案是true。
        */
        if (ans == -1) return false; // 终点都没经过,肯定false
        for (int[] obstacle : obstacles) {
            int cnt = isPassed(cmd, obstacle[0], obstacle[1], dx, dy);
            if (cnt != -1 && cnt < ans) return false; 
            //不等于-1,说明经过了,然后再看这个点和终点哪个次数多。ans多,说明这个点在ans前面,返回false。
        }
        return true;
    }
    // 判断是否经过该点,经过返回走的次数,没经过返回-1。
    public int isPassed(char[] cmd, int x, int y, int dx, int dy) {
        int round = Math.min(x / dx, y / dy); // 计算走到第x-1或y-1层需要多少轮
        int cnt = cmd.length * round;  // 前几轮的总次数
        dx *= round; dy *= round; // 在第x-1或y-1层时的位置。
        if (dx == x && dy == y) return cnt; // 正好就是要找的点,直接返回。
        for (char c : cmd) { // 遍历第x层或y层,如果经过,那么答案一定会遍历到。
            if (c == 'U') dy++; // 要按command的顺序走
            else dx++;
            cnt++; // 不要忘了每遍历一次,次数都要加1
            if (dx == x && dy == y) return cnt; // 一旦找到,直接返回所需要的次数。
        }
        return -1;
    }
}