小u的最大连续移动次数问题

134 阅读5分钟

问题解析

题目要求在一个二维矩阵中,找到从任意位置出发的最大交替上坡/下坡路径。具体来说,你可以从一个位置开始,移动到相邻的格子(上下左右),要求路径上的每一步必须是交替的:上坡或者下坡。我们的目标是找到一条最长的路径,路径上的每个点都满足交替上坡和下坡的条件。

问题要点:

  1. 每个位置的高度给定为二维数组 a,其中 a[i][j] 表示位置 (i, j) 的高度。
  2. 从任意起点开始,移动到相邻的格子,要求是交替的上坡和下坡。
  3. 上坡定义为从较低的高度移动到较高的高度,反之为下坡。
  4. 我们需要返回可能的最大路径长度。

思路解析

  1. DFS(深度优先搜索)+ 记忆化

    • 我们可以通过DFS来探索每一个可能的路径,递归地探索每一个位置的上下左右相邻的格子。
    • 为了避免重复计算,我们使用记忆化,即每个位置 (x, y) 的结果(从此位置开始的最大路径长度)只计算一次,并存储在一个二维数组 dp 中。
    • 状态记录:我们通过 is_up(一个布尔值)来记录当前的坡度方向:True 表示上坡,False 表示下坡。
  2. 状态转移

    • 从一个位置出发,我们根据当前位置的高度和相邻格子的高度来决定是否可以移动。
    • 如果当前位置 a[x][y] 高于邻居格子的高度,且当前是在上坡状态,那么可以进行下坡is_up=False)。
    • 如果当前位置 a[x][y] 低于邻居格子的高度,且当前是在下坡状态,那么可以进行上坡is_up=True)。
  3. 递归过程

    • 从每个位置 (i, j) 开始进行递归,分别考虑从此位置出发进行上坡和下坡的两种情况。
    • 每次递归都会向相邻的四个方向(上、下、左、右)移动,满足上坡或下坡的条件。
  4. 边界检查:在DFS时要确保每次移动后的坐标 (nx, ny) 不会越界。

代码详解

pythonCopy Code
def solution(m: int, n: int, a: list) -> int:
    # 初始化记忆化数组,用于存储从每个位置出发的最大步数
    dp = [[-1] * n for _ in range(m)]

    def dfs(x: int, y: int, is_up: bool) -> int:
        # 如果当前位置的结果已计算过,直接返回
        if dp[x][y] != -1:
            return dp[x][y]
        
        # 初始化最大步数
        max_steps = 0
        
        # 遍历四个方向
        for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            nx, ny = x + dx, y + dy
            # 确保新的位置在边界内且尚未访问
            if 0 <= nx < m and 0 <= ny < n:
                # 根据is_up的状态,判断是否可以移动
                if is_up and a[nx][ny] < a[x][y]:
                    max_steps = max(max_steps, 1 + dfs(nx, ny, False))  # 下坡
                elif not is_up and a[nx][ny] > a[x][y]:
                    max_steps = max(max_steps, 1 + dfs(nx, ny, True))  # 上坡
        
        # 存储计算的结果
        dp[x][y] = max_steps
        return dp[x][y]

    max_moves = 0
    # 遍历所有位置,计算从每个点出发的最大步数
    for i in range(m):
        for j in range(n):
            max_moves = max(max_moves, dfs(i, j, True), dfs(i, j, False))
    
    return max_moves

if __name__ == '__main__':
    print(solution(2, 2, [[1, 2], [4, 3]]) == 3)
    print(solution(3, 3, [[10, 1, 6], [5, 9, 3], [7, 2, 4]]) == 8)
    print(solution(4, 4, [[8, 3, 2, 1], [4, 7, 6, 5], [12, 11, 10, 9], [16, 15, 14, 13]]) == 11)

代码详解:

  1. dp 数组

    • dp[x][y] 用来存储从位置 (x, y) 出发的最大路径长度。初始时所有位置的值都设置为 -1,表示未计算过。
  2. dfs(x, y, is_up)

    • 递归函数 dfs 用来计算从位置 (x, y) 出发的最大路径长度。
    • is_up 参数表示当前是否是在上坡状态,True 表示上坡,False 表示下坡。
    • 每次递归都计算当前点的最大步数,递归地遍历四个相邻的方向(上、下、左、右),并根据当前位置与相邻格子的高度差决定是否可以继续上坡或下坡。
  3. 状态转移

    • 如果当前是上坡(is_up=True),则可以向高度较低的邻居格子下坡。
    • 如果当前是下坡(is_up=False),则可以向高度较高的邻居格子上坡。
  4. max_steps 更新

    • 在每次递归中,我们都检查并更新从当前位置出发的最大步数。每次递归调用都会增加步数并返回。
  5. 最终结果

    • 在主函数 solution 中,我们遍历每个点,并分别计算从每个点出发的最大路径(上坡和下坡两种状态),最终返回所有计算路径中的最大值。

图解:

假设我们有一个简单的 3x3 矩阵:

Copy Code
a = [    [10, 1, 6],
    [5, 9, 3],
    [7, 2, 4]
]
  1. 初始状态:从每个点开始,我们递归地寻找路径。
  2. DFS遍历:假设从位置 (0, 0) 开始,首先考虑向下或向右移动,递归地探索每个方向。
  3. 记忆化:在每个递归过程中,存储每个位置的最大路径长度,避免重复计算。

复杂度分析:

  1. 时间复杂度

    • 每个位置 (x, y) 在递归时最多被访问一次,每次递归的深度是有限的。因此,时间复杂度为 O(m * n),其中 m 是行数,n 是列数。
  2. 空间复杂度

    • 除了输入矩阵 a,我们还使用了 dp 数组存储每个位置的最大路径,空间复杂度为 O(m * n)

测试用例:

  1. 示例1

    Copy Code
    a = [[1, 2], [4, 3]]
    输出:3
    

    路径:(0,0) -> (0,1) -> (1,1)

  2. 示例2

    Copy Code
    a = [[10, 1, 6], [5, 9, 3], [7, 2, 4]]
    输出:8
    
  3. 示例3

    Copy Code
    a = [[8, 3, 2, 1], [4, 7, 6, 5], [12, 11, 10, 9], [16, 15, 14, 13]]
    输出:11
    

通过这种DFS + 记忆化的方式,我们可以高效地解决这个问题,避免了重复计算,确保了较大的输入也能在合理的时间内得到解。