青训营X豆包MarsCode 技术训练营第一课 | 豆包MarsCode AI 刷题

88 阅读4分钟

问题描述 小U决定在一个 m × n m×n 的地图上行走。地图中的每个位置都有一个高度,表示地形的高低。小U只能在满足以下条件的情况下移动: 只能上坡或者下坡,不能走到高度相同的点。 移动时必须交替进行:上坡后必须下坡,下坡后必须上坡,不能连续上坡或下坡。 每个位置只能经过一次,不能重复行走。 任务是帮助小U找到他在地图上可以移动的最大次数,即在符合所有条件的前提下,小U能走过的最大连续位置数量。 例如在以下2x2的地图中: 1 2 4 3 中庸行者可以选择移动顺序 3 -> 4 -> 1 -> 2,最大移动次数为3

问题分析

这是一个关于地图路径搜索的典型问题。我们需要在给定的地图中找到小U可以移动的最大次数,遵循以下规则:

  1. 只能上下坡:当前点到下一个点的高度必须不同。
  2. 交替移动:必须交替上坡和下坡,不能连续上坡或连续下坡。
  3. 不重复经过点:每个点只能经过一次。

本质上,这是一个 有约束的深度优先搜索(DFS) 问题,需要在地图上遍历所有可能的路径并记录最大移动次数。


解题思路

  1. 表示地图与方向
    用二维数组 grid[m][n] 表示地图,其中每个元素为地形高度。同时定义上下左右四个方向的偏移量 directions,用于控制搜索方向。

  2. 状态存储

    • 当前点的坐标 (x, y)
    • 上一次的移动状态:上坡还是下坡,用一个布尔值 is_ascend 来表示。如果当前移动是上坡,则下一次必须是下坡。
  3. DFS 搜索

    • 从地图上的任意一个点 (i, j) 出发。

    • 判断从当前位置是否可以移动到某一方向的下一个点 (nx, ny)

      • (nx, ny) 在地图范围内。
      • (nx, ny) 没有被访问过。
      • 高度满足上坡或下坡的条件。
    • 更新状态并继续递归搜索,记录路径长度。

    • 搜索完成后回溯,恢复状态,探索其他方向。

  4. 优化
    使用一个二维数组 dp[x][y] 来存储从某点开始的最长路径长度,避免重复计算。

  5. 输出结果
    遍历所有起点,找到最大路径长度。


实现代码

以下是基于上述思路的完整实现代码:

def longest_path(grid):
    m, n = len(grid), len(grid[0])
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    max_steps = 0
    dp = [[[-1, -1] for _ in range(n)] for _ in range(m)]  # dp[x][y][0] 表示上坡后最长路径,dp[x][y][1] 表示下坡后最长路径

    def dfs(x, y, is_ascend, visited):
        if dp[x][y][int(is_ascend)] != -1:
            return dp[x][y][int(is_ascend)]
        
        visited[x][y] = True
        max_len = 0
        for dx, dy in directions:
            nx, ny = x + dx, y + dy
            if 0 <= nx < m and 0 <= ny < n and not visited[nx][ny]:
                if is_ascend and grid[nx][ny] < grid[x][y]:  # 下坡
                    max_len = max(max_len, 1 + dfs(nx, ny, False, visited))
                elif not is_ascend and grid[nx][ny] > grid[x][y]:  # 上坡
                    max_len = max(max_len, 1 + dfs(nx, ny, True, visited))
        
        visited[x][y] = False
        dp[x][y][int(is_ascend)] = max_len
        return max_len

    for i in range(m):
        for j in range(n):
            visited = [[False] * n for _ in range(m)]
            max_steps = max(max_steps, dfs(i, j, True, visited))  # 从 (i, j) 开始,上坡
            max_steps = max(max_steps, dfs(i, j, False, visited))  # 从 (i, j) 开始,下坡

    return max_steps

测试用例

输入示例 1:

grid = [    [1, 2],
    [4, 3]
]
print(longest_path(grid))  # 输出: 3

输入示例 2:

grid = [    [1, 3, 2],
    [6, 5, 4],
    [7, 8, 9]
]
print(longest_path(grid))  # 输出: 7

输入示例 3:

grid = [    [10, 20, 30],
    [15, 25, 35],
    [5, 50, 40]
]
print(longest_path(grid))  # 输出: 5

复杂度分析

  1. 时间复杂度

    • 最差情况下,每个点都需要尝试上下坡两种状态,并且对每个状态进行深度优先搜索。
    • 时间复杂度为 O(m×n×4k)O(m \times n \times 4^k),其中 kk 是路径的最大长度。
  2. 空间复杂度

    • 使用了一个 dp 数组存储结果,大小为 O(m×n)O(m \times n)。
    • 递归栈的最大深度为 O(k)O(k),所以空间复杂度为 O(m×n+k)O(m \times n + k)。

总结

通过动态规划结合深度优先搜索,我们可以有效地求解满足题目约束条件的最长路径问题。dp 的使用显著优化了重复计算,适合解决复杂约束的路径搜索问题。