1824. 最少侧跳次数

【算法题】最少侧跳次数 —— 青蛙过三跑道障碍问题详解


题目描述

给你一条长度为 n 的道路,道路有 3 条跑道,总共包含 n + 1 个点,编号从 0 到 n。青蛙从起点 0 处的 第 2 条跑道 出发,目标是跳到第 n 号点的任意一条跑道。

道路上可能存在障碍。用一个长度为 n + 1 的数组 obstacles 表示:

  • obstacles[i] 的取值范围是 0 到 3。
  • 如果 obstacles[i] = k (k != 0),则表示第 i 个点的第 k 条跑道上有一个障碍。
  • 如果 obstacles[i] = 0,则该点所有跑道无障碍。

题目要求:

  • 青蛙可以从点 i 的某条跑道跳到点 i+1 同跑道(如果没有障碍)。
  • 青蛙也可以在同一点 i 处,侧跳(换跑道),条件是换过去的跑道在该点没有障碍。
  • 青蛙从点 0 的第 2 跑道出发,想跳到点 n 任意跑道,求 最少侧跳次数

注意:点 0 和点 n 的任意跑道均无障碍。


题目示例

假设 obstacles = [0,1,2,3,0] 表示:

  • 点0无障碍
  • 点1第1条跑道有障碍
  • 点2第2条跑道有障碍
  • 点3第3条跑道有障碍
  • 点4无障碍

青蛙从点0跑道2出发,要跳到点4,避免障碍,最少侧跳次数是多少?


解题分析

这道题的关键在于:

  • 青蛙想尽量少侧跳;
  • 只能向前跳到下一点同跑道上无障碍的位置;
  • 如果前方受阻,可以在当前点换跑道(侧跳),但不能跳到有障碍的跑道。

思考方式

  • 起点在点0跑道2,侧跳次数为0。
  • 如果下一点同跑道无障碍,可以直接跳,不用侧跳。
  • 如果下一点有障碍,就必须在当前点换跑道侧跳,再继续跳。
  • 目标是求最少侧跳次数。

解题方法

使用动态规划(DP)或贪心策略模拟每个点每条跑道的最少侧跳次数。


状态定义

  • dp[i][j] 表示到达第 i 个点,处于第 j 条跑道时的最少侧跳次数。
  • 跑道编号 j 范围是 1~3。
  • 青蛙从 (0,2) 出发,因此:
    • dp[0][2] = 0
    • 其他跑道初始为 (或者可以设为1,表示必须侧跳一次)

状态转移

对于点 i (从 0 到 n-1):

  • 先检查点 i+1 各跑道是否有障碍:
    • 如果 obstacles[i+1] == j,跑道 j 不可用,dp[i+1][j] = ∞
    • 否则,可以尝试:
      • 如果从跑道 j 的 dp[i][j] 可达,则 dp[i+1][j] = dp[i][j](直接跳,无需侧跳)。
  • 对于当前点 i ,考虑侧跳:
    • 对于跑道 j,尝试从其他两条跑道侧跳过来,侧跳次数 +1。
    • 更新 dp[i][j] = min(dp[i][j], dp[i][k] + 1),其中 k != j。

状态压缩优化

因为我们只用到前一个点的信息,可以用一维数组 dp[j] 表示当前点处第 j 条跑道的最少侧跳次数。

  • 初始:
    • dp = [∞, 1, 0, 1] (下标0不用)
  • 每处理一个点 i:
    • 设置 dp[obstacles[i]] = ∞ 表示该跑道被障碍阻断不可达
    • 找出当前 dp 中的最小值 min_dp
    • 对未阻断的跑道 j 更新:dp[j] = min(dp[j], min_dp + 1) 表示可以侧跳过来

代码实现

from typing import List

class Solution:
    def minSideJumps(self, obstacles: List[int]) -> int:
        n = len(obstacles) - 1
        dp = [float('inf')] * 4
        # 起点0跑道2侧跳次数0,跑道1和3各需要1次侧跳跳过来
        dp[1], dp[2], dp[3] = 1, 0, 1

        for i in range(1, n + 1):
            # 把有障碍的跑道设为不可达
            dp[obstacles[i]] = float('inf')
            # 找当前点所有跑道的最小侧跳次数
            min_dp = min(dp[1], dp[2], dp[3])
            # 更新其他跑道侧跳次数(可能通过侧跳变得更小)
            for j in range(1, 4):
                if obstacles[i] != j:
                    dp[j] = min(dp[j], min_dp + 1)
        return min(dp[1], dp[2], dp[3])

复杂度分析

  • 时间复杂度:O(n),遍历一次 obstacles 数组,每个点处理3条跑道的状态更新。
  • 空间复杂度:O(1),使用长度为4的一维 dp 数组,空间使用固定。

示例讲解

obstacles = [0,1,2,3,0] 为例:

  • 起点:dp = [∞,1,0,1]
  • 点1障碍在跑道1,dp[1] = ∞
    • 计算当前最小侧跳次数 = min(dp) = 0
    • 其他跑道更新为 min(dp[j], 0 + 1) 即 dp[2] = 0, dp[3] = 1
  • 点2障碍跑道2,dp[2] = ∞
    • 最小侧跳次数是 min(∞, ∞, 1) = 1
    • dp[1] = min(∞, 1+1=2) = 2, dp[3] = 1 (保持不变)
  • 点3障碍跑道3,dp[3] = ∞
    • 最小侧跳次数是 min(2, ∞, ∞) = 2
    • dp[1] = 2, dp[2] = min(∞, 2+1=3) = 3
  • 点4无障碍
    • 最小侧跳次数是 min(2, 3, ∞) = 2
    • 更新dp[1], dp[2], dp[3] 最小分别为 2, 3, 3

最终最小值是2,表示青蛙至少要侧跳2次才能到达点4。


总结

本题核心考察对状态转移的理解与优化。通过维护每个点上三条跑道的最少侧跳次数,动态规划可以高效求解问题。状态压缩使得空间复杂度降为常数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值