Medium
题目描述
有一条长度为 n 的三车道道路,由 n + 1 个点标记,从 0 到 n。一只青蛙从第 2 车道的点 0 开始,想要跳到点 n。但是,路上可能会有障碍物。
给你一个长度为 n + 1 的数组 obstacles,其中 obstacles[i](取值范围从 0 到 3)表示在点 i 处位于车道 obstacles[i] 上的障碍物。如果 obstacles[i] == 0,则表示在点 i 处没有障碍物。每个点最多只有一个障碍物在 3 条车道中的一条上。
- 例如,如果 obstacles[2] == 1,那么在点 2 处的车道 1 上有一个障碍物。
青蛙只能在同一车道上从点 i 移动到点 i + 1,前提是在点 i + 1 处的车道上没有障碍物。为了避开障碍物,青蛙也可以在同一点执行侧跳,跳到另一个车道(即使它们不相邻),前提是新车道上没有障碍物。
- 例如,青蛙可以在点 3 处从车道 3 跳到车道 1。
返回青蛙从点 0 的车道 2 开始到达点 n 的任意车道所需的最少侧跳次数。
注意:点 0 和点 n 处不会有障碍物。
示例 1:
输入:obstacles = [0,1,2,3,0]
输出:2
解释:最优解如上图箭头所示。总共有 2 次侧跳(红色箭头)。
注意青蛙只有在侧跳时才能跳过障碍物(如点 2 处所示)。
示例 2:
输入:obstacles = [0,1,1,3,3,0]
输出:0
解释:车道 2 上没有障碍物。不需要侧跳。
示例 3:
输入:obstacles = [0,2,1,0,3,0]
输出:2
解释:最优解如上图箭头所示。总共有 2 次侧跳。
提示:
- obstacles.length == n + 1
- 1 <= n <= 5 * 10^5
- 0 <= obstacles[i] <= 3
- obstacles[0] == obstacles[n] == 0
解题思路
这是一个典型的动态规划问题。我们需要追踪青蛙在每个位置的每个车道上所需的最少侧跳次数。
核心思路:
- 使用
dp[i][j]表示青蛙到达位置 i 的车道 j 所需的最少侧跳次数 - 青蛙从位置 0 的车道 2 开始,初始化
dp[0][2] = 0,其他车道需要一次侧跳 - 对于每个位置 i,青蛙可以:
- 从前一个位置的同一车道直接前进(如果当前车道没有障碍物)
- 在当前位置侧跳到其他车道(如果目标车道没有障碍物)
状态转移:
- 如果车道 j 在位置 i 没有障碍物:
dp[i][j] = dp[i-1][j](从前一位置的同一车道直接前进)dp[i][j] = min(dp[i][j], min(dp[i][other_lanes]) + 1)(从其他车道侧跳过来)
优化空间: 由于只需要前一个位置的状态,可以使用一维数组优化空间复杂度,每次处理当前位置时更新数组。
为了避免在同一轮更新中使用已更新的值,我们先计算能直接前进的情况,再计算侧跳的情况。
代码实现
class Solution {
public:
int minSideJumps(vector<int>& obstacles) {
vector<int> dp = {1, 0, 1}; // 车道1、2、3的最少侧跳次数
for (int i = 1; i < obstacles.size(); i++) {
int obstacle = obstacles[i];
// 如果当前位置有障碍物,将对应车道设为无穷大
if (obstacle != 0) {
dp[obstacle - 1] = 1e6;
}
// 计算侧跳的情况
for (int j = 0; j < 3; j++) {
if (j + 1 != obstacle) {
// 从其他车道侧跳过来
for (int k = 0; k < 3; k++) {
if (k != j) {
dp[j] = min(dp[j], dp[k] + 1);
}
}
}
}
}
return min({dp[0], dp[1], dp[2]});
}
};
class Solution:
def minSideJumps(self, obstacles: List[int]) -> int:
dp = [1, 0, 1] # 车道1、2、3的最少侧跳次数
for i in range(1, len(obstacles)):
obstacle = obstacles[i]
# 如果当前位置有障碍物,将对应车道设为无穷大
if obstacle != 0:
dp[obstacle - 1] = float('inf')
# 计算侧跳的情况
for j in range(3):
if j + 1 != obstacle:
# 从其他车道侧跳过来
for k in range(3):
if k != j:
dp[j] = min(dp[j], dp[k] + 1)
return min(dp)
public class Solution {
public int MinSideJumps(int[] obstacles) {
int[] dp = {1, 0, 1}; // 车道1、2、3的最少侧跳次数
for (int i = 1; i < obstacles.Length; i++) {
int obstacle = obstacles[i];
// 如果当前位置有障碍物,将对应车道设为无穷大
if (obstacle != 0) {
dp[obstacle - 1] = int.MaxValue;
}
// 计算侧跳的情况
for (int j = 0; j < 3; j++) {
if (j + 1 != obstacle) {
// 从其他车道侧跳过来
for (int k = 0; k < 3; k++) {
if (k != j) {
dp[j] = Math.Min(dp[j], dp[k] + 1);
}
}
}
}
}
return Math.Min(Math.Min(dp[0], dp[1]), dp[2]);
}
}
var minSideJumps = function(obstacles) {
let dp = [1, 0, 1]; // 车道1、2、3的最少侧跳次数
for (let i = 1; i < obstacles.length; i++) {
const obstacle = obstacles[i];
// 如果当前位置有障碍物,将对应车道设为无穷大
if (obstacle !== 0) {
dp[obstacle - 1] = Infinity;
}
// 计算侧跳的情况
for (let j = 0; j < 3; j++) {
if (j + 1 !== obstacle) {
// 从其他车道侧跳过来
for (let k = 0; k < 3; k++) {
if (k !== j) {
dp[j] = Math.min(dp[j], dp[k] + 1);
}
}
}
}
}
return Math.min(...dp);
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(n) - 需要遍历所有位置,每个位置的状态转移是常数时间 |
| 空间复杂度 | O(1) - 只使用了固定大小的数组存储3个车道的状态 |
相关题目
- . Frog Jump (Hard)