LeetCode 874. 模拟行走机器人
题目描述



题意分析
机器人从原点面向北,逐条执行左转、右转和前进指令。前进途中若下一格有障碍,就停在当前格并放弃本条指令的剩余步数,之后继续执行下一条指令。答案是整个行走过程中到原点的最大距离平方,不只看最终位置。
解法:哈希集合 + 模拟
核心思路
[!blue]
保存当前坐标
(x, y)和方向下标dir,方向按北、东、南、西排列,对应位移(0,1)、(1,0)、(0,-1)、(-1,0)。右转沿顺序前进一格,使用(dir + 1) % 4;左转退一格,写成(dir + 3) % 4可避免负下标。将障碍坐标放入哈希集合,前进时先根据方向计算下一格
(nx, ny),确认没有障碍才更新当前位置。单条指令最多走 9 步,直接逐格检查即可,也能发现途中障碍;若只检查整条指令的终点,就可能穿过障碍。代码把坐标对编码为一个 64 位整数:
x的 32 位放在高半部分,y的 32 位放在低半部分。对y做掩码或转为uint32,是为了保留它的低 32 位且避免负数符号扩展污染高位。两部分互不重叠,能够唯一还原两个坐标,负坐标也不会发生键冲突。每次成功移动后,用
x*x + y*y更新最大值;初始原点的距离为 0,转向和遇障碍都不改变位置。只检查即将进入的格子,也自然满足原点有障碍的规则:机器人仍能从原点离开,但之后不能再次走回原点。
解题步骤
- 编码全部障碍并放入集合,初始化坐标、朝北方向和最大距离平方。
- 遇到
-2左转,遇到-1右转,两者都只更新方向。- 正数指令逐步计算下一格;若该格在障碍集合中,跳出本次前进循环,否则更新位置与最大距离平方。
- 全部指令处理完后返回最大值。题目保证答案小于 $2^{31}$,距离平方可用现有整数类型保存。
代码实现
class Solution {
public int robotSim(int[] commands, int[][] obstacles) {
Set<Long> block = new HashSet<>();
for (int[] o : obstacles) {
long key = (((long) o[0]) << 32) ^ (o[1] & 0xffffffffL);
block.add(key);
}
int[][] dirs = new int[4][2];
dirs[0] = new int[] {
0,
1
};
dirs[1] = new int[] {
1,
0
};
dirs[2] = new int[] {
0,
-1
};
dirs[3] = new int[] {
-1,
0
};
int dir = 0;
int x = 0;
int y = 0;
int best = 0;
for (int cmd : commands) {
if (cmd == -2) {
dir = (dir + 3) % 4;
} else if (cmd == -1) {
dir = (dir + 1) % 4;
} else {
for (int i = 0; i < cmd; i++) {
int nx = x + dirs[dir][0];
int ny = y + dirs[dir][1];
long key = (((long) nx) << 32) ^ (ny & 0xffffffffL);
// 检查即将进入的格子,障碍只中止当前前进指令
if (block.contains(key)) {
break;
}
x = nx;
y = ny;
best = Math.max(best, x * x + y * y);
}
}
}
return best;
}
}
func robotSim(commands []int, obstacles [][]int) int {
block := make(map[int64]bool)
for _, o := range obstacles {
key := (int64(o[0]) << 32) ^ int64(uint32(o[1]))
block[key] = true
}
dirs := make([][2]int, 0, 4)
dirs = append(dirs,
[2]int{
0,
1,
},
[2]int{
1,
0,
},
[2]int{
0,
-1,
},
[2]int{
-1,
0,
},
)
dir := 0
x, y := 0, 0
best := 0
for _, cmd := range commands {
if cmd == -2 {
dir = (dir + 3) % 4
} else if cmd == -1 {
dir = (dir + 1) % 4
} else {
for step := 0; step < cmd; step++ {
nx := x + dirs[dir][0]
ny := y + dirs[dir][1]
key := (int64(nx) << 32) ^ int64(uint32(ny))
// 检查即将进入的格子,障碍只中止当前前进指令
if block[key] {
break
}
x, y = nx, ny
dist := x*x + y*y
if dist > best {
best = dist
}
}
}
}
return best
}
复杂度分析
- 时间复杂度:期望 $O(M+C)$,其中 $M$ 为障碍数、$C$ 为指令数。建集合耗时 $O(M)$,每条指令最多尝试 9 次移动,查询障碍的期望时间为常数。
- 空间复杂度:$O(M)$,用于障碍集合,坐标和方向只占常数空间。
关键点总结
[!green]
- 障碍判断针对候选下一格。
- 坐标编码需要区分两个分量并支持负数。
易错点总结
[!yellow]
- 命中障碍后结束全部指令,会漏掉后面的转向和移动。
- 只返回最终位置距离,可能漏掉早先走到的更远点。
- 把当前格当作障碍检查对象,会先走进障碍。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 1496. 判断路径是否相交 | 简单 | 同样按方向在整数网格中移动,原题检测访问重复,本题用固定障碍集合阻止前进。 |
| 2069. 模拟行走机器人 II | 中等 | 原题只沿矩形外边行走,可按周长压缩步数,本题有任意障碍,需要判断路径是否受阻。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!