874. 模拟行走机器人
审题
以示例2为例,先把题目意思搞明白。
输入: commands = [4, -1, 4, -2, 4], obstacles = [[2,4]]
输出: 机器人所经过的每个坐标点 (x, y)
到原点的欧式距离的平方的最大值。
欧式距离:
x
2
+
y
2
\sqrt{x^2 + y^2}
x2+y2
欧式距离的平方:$ x2+y2 $
如上图所示:
机器人初始位置坐标点(0,0)
初始方向为 向北。
- 读取第一个指令为4,沿着当前当前方向“北”,向前走4个单位,停在坐标点9
(0,4)
- 读取第二个指令为-1,该指令表示“向右转90度”,那么机器人就由原来的“北”右转90度之后方向变为“东”
- 读取第三个指令为4,沿着当前方向“东”,向前走4个单位,但是发现坐标点
(2,4)
是一个障碍物,不能跨越,只能停留在障碍物前面一个单位,即坐标点(1,4)
- 读取第四个指令为-2,该指令表示“向左转90度”,那么机器人就由原来的“东”左转90度之后方向变为“北”
- 读取第五个指令为4,沿着当前方向“北”,向前走4个单位,停留在坐标点
(1,8)
65怎么得来的? 机器人所经过的这些点中,坐标点
(1,8)
计算出的欧式距离的平方最大,为 1 2 + 8 2 = 65 1^2 + 8^2 = 65 12+82=65。
解题思路
总体思想: 模拟机器人行走过程,计算每一步坐标点到原点的欧式距离的平方,于保存的最大值比较,实时更小最大值。具体如下:
1.分解机器人行走
走 k
步,就是朝着一个方向走 k
个1步
怎么朝着某个方向走出一步
- 方向向北,机器人坐标向上走一步
- 方向向东,机器人坐标向右走一步
- 方向向南,机器人坐标向下走一步
- 方向向西,机器人坐标向上走一步
int direx[] = {0, 1, 0, -1}
int direy[] = {1, 0, -1, 0}
direx[],direy[] 要竖着对齐看
- 向北,坐标轴上x不动,y+1,即(0,1)
- 向东,坐标轴上x+1,y不动,即(1,0)
- 向南,坐标轴上x不动,y-1,即(0,-1)
- 向西,坐标轴上x-1,y不动,即(-1,0)
走(direx[i], direy[i])
,加上当前坐标后为 (curx,cury)+(direx[i], direy[i])
2.机器人如何调整方向
direx[]direy[]
的下标 i
代表了当前机器人的方向 \
- i = 0, 向北
- i = 1, 向东
- i = 2, 向南
- i = 3, 向西
当读取到调整方向的指令时,如
- “-1”:“向右转90度”,只要当前方向
curdire + 1
就可以得到右转方向 - “-2”:“向左转90度”,只要当前方向
curdire + 3
就可以得到左转方向(curdire + 3) % 4
,因为不管curdire
当前是哪个方向,左转都在其左边,在direx
数组的定义中顺时针数3个就是其左边,所以就是加3。
3.怎么判断是否遇到了障碍物
障碍物有多个,所以需要有一个障碍物坐标点集合
机器人每试图走一个位置,就用此位置于障碍物集合列表李的坐标进行比较,判断是否刚好是障碍物坐标点。
- 不是,则真正走到这个点,更小机器人坐标点
(curx, cury)
- 是障碍物,那么不走下一步,停留在当前,执行下一条命令
4.实现方法
- -1、-2 表示左右移动可需要工具指定东来西北,判断左右移动方向从而取得当前的朝向;
- 每移动一步都需要判断障碍物,所以需要快速寻找,这里可以考虑使用
hashmap
或者hashset
; - 每一个朝向的移动就是一个循环,只要遇到障碍物就结束当前循环,每循环一次都比较一次最远距离;
- 返回最远距离。
参考代码
class Solution {
public int robotSim(int[] commands, int[][] obstacles) {
// direction 表示当前朝向,0 1 2 3 表示 北东南西
int ans = 0, direction = 0, x = 0, y = 0;
// 每个朝向上的数据变化,比如朝北时取 Direction[0] -> {0, 1}
// 那么x轴的变化为x+0, y轴变化为y+1
int[][] Direction = {{0,1}, {1,0}, {0,-1}, {-1,0}};
HashSet<String> set = new HashSet<>();
// 将所有障碍物坐标组合成字符串存入set中方便查询
for (int[] arr : obstacles) set.add(arr[0] + "," + arr[1]);
for (int com : commands) {
// 定义下一步的坐标
int next_x = 0, next_y = 0;
// 当命令为前进,开始移动
if (com >= 0) {
for(int i = 0; i < com; i++) {
// 取得下一步的坐标
next_x = x + Direction[direction][0];
next_y = y + Direction[direction][1];
// 若下一步有障碍物,结束当前命令,跳至下一命令
if(set.contains(next_x + "," + next_y)) break;
// 否则更小坐标于最远距离
x = next_x;
y = next_y;
ans = Math.max(ans, x*x + y*y);
}
} else {
// 改变朝向,-1:向右转90度,-2:向左转90度
direction = com == -1 ? (direction + 1) % 4 : (direction + 3) % 4;
}
}
return ans;
}
}
部分图片来源于网络,版权归原作者,侵删。