小U的最大连续移动次数问题刷题笔记 | 豆包MarsCode AI 刷题

127 阅读5分钟

做题笔记:小U的最大连续移动次数问题

问题描述:

小U决定在一个 mx n的地图上行走。地图中的每个位置都有一个高度,表示地形的高低。小U只能在满足以下条件的情况下移动:

  1. 只能上坡或者下坡,不能走到高度相同的点。
  2. 移动时必须交替进行:上坡后必须下坡,下坡后必须上坡,不能连续上坡或下坡
  3. 每个位置只能经过一次,不能重复行走。 任务是帮助小U找到他在地图上可以移动的最大次数,即在符合所有条件的前提下,小U能走过的最大连续位置数量。

题目内容分析

题目描述了一个类似深度优先搜索(DFS)的路径规划问题,涉及以下关键点:

地图特点:

  • 一个 𝑚 × 𝑛 m×n 的二维地图,每个格子有一个具体的高度值。
  • 高度值用于判断路径是否符合移动条件。

移动规则:

  • 高度限制:只能走到高度不同的格子(上坡或下坡),不能在高度相同的格子间移动。
  • 交替移动:必须严格按照上坡-下坡交替的顺序进行移动,不能连续上坡或连续下坡。
  • 单次通过:每个位置只能经过一次,不能重复走回之前的格子。

目标:

找到地图上可以行走的最大连续路径长度。

解题思路

路径搜索:

  • 采用DFS进行路径遍历。
  • 从每一个起点开始,尝试所有可能的移动方向。
  • 每次移动前检查是否满足规则:当前移动是“上坡”还是“下坡”,并根据当前状态调整下一步的允许条件。

状态保存:

  • 使用visited集合记录已访问的格子,防止重复走回。
  • 通过一个布尔变量is_uphill记录当前移动方向是否为上坡,便于在递归过程中切换上下坡状态。

结果计算:

  • 从地图的每一个格子开始DFS,记录从该点出发的最大路径长度,最终取所有起点的最大值。

复杂度分析:

  • 时间复杂度:理论上最差情况下,每个点都需要遍历所有剩余未访问的点,时间复杂度为 𝑂 ( 𝑚 ⋅ 𝑛 ⋅ 4 𝑚 ⋅ 𝑛 ) O(m⋅n⋅4 m⋅n )。
  • 空间复杂度:由于递归调用栈和visited集合的使用,空间复杂度为 𝑂 ( 𝑚 ⋅ 𝑛 ) O(m⋅n)。

代码思路分析

嵌套循环遍历所有起点:

max_result = 0
for i in range(m):
    for j in range(n):
        max_result = max(max_result, dfs(i, j, True, set([(i, j)])))
        max_result = max(max_result, dfs(i, j, False, set([(i, j)])))
  • 遍历每个格子:将每一个格子作为起点,尝试寻找从该点出发的最长路径。
  • 双向探索: dfs(i, j, True, ...):假设从该点开始以“上坡”作为第一步。 dfs(i, j, False, ...):假设从该点开始以“下坡”作为第一步。
  • max_result:记录从所有起点出发的最大路径长度。

结果返回:

return max_result
  • 最终返回 max_result,即符合条件的最大连续移动次数。

DFS函数

def dfs(x, y, is_uphill, visited):
    ...

输入参数:

  • x, y:当前格子的坐标。
  • is_uphill:当前是否是上坡移动。
    • True 表示上坡,下一步只能走到比当前点高度更高的位置。
    • False 表示下坡,下一步只能走到比当前点高度更低的位置。
  • visited:保存当前路径中已经访问过的格子,防止重复访问。

核心逻辑:

初始化最大移动次数:

max_moves = 0
  • 用于记录从当前格子出发的最大路径长度。

定义移动方向:

directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
  • 定义四个可能的移动方向:上、下、左、右。
  • 每个方向用相对坐标表示:
    • (-1, 0) 表示向上移动1格。
    • (1, 0) 表示向下移动1格。
    • (0, -1) 表示向左移动1格。
    • (0, 1) 表示向右移动1格。

尝试每个方向:

for dx, dy in directions:
    nx, ny = x + dx, y + dy
    if 0 <= nx < m and 0 <= ny < n and (nx, ny) not in visited:
        ...
  • 根据当前格子的坐标 x, y 和方向 dx, dy,计算新格子的坐标 nx, ny。
  • 边界检查:确保新坐标 nx, ny 在地图范围内。
  • 访问检查:确保新格子未被访问。

移动判断与递归:

if is_uphill and a[nx][ny] > a[x][y]:  # 上坡条件
    visited.add((nx, ny))
    max_moves = max(max_moves, 1 + dfs(nx, ny, not is_uphill, visited))
    visited.remove((nx, ny))  # 回溯
elif not is_uphill and a[nx][ny] < a[x][y]:  # 下坡条件
    visited.add((nx, ny))
    max_moves = max(max_moves, 1 + dfs(nx, ny, not is_uphill, visited))
    visited.remove((nx, ny))  # 回溯
  • 上坡条件:
    • 当 is_uphill == True 且新格子的高度 a[nx][ny] > a[x][y] 时,可以移动。
    • 调用递归函数 dfs(nx, ny, not is_uphill, visited):
      • nx, ny 是新格子的坐标。
      • not is_uphill 切换为下坡状态。
    • 回溯:在递归结束后移除 visited 中的当前格子,以便后续尝试其他路径。
  • 下坡条件:
    • 类似上坡,只是条件改为 a[nx][ny] < a[x][y]。
  • 更新最大值:
    • 通过 max_moves = max(max_moves, ...),记录当前路径的最大长度。

返回结果:

return max_moves
  • 返回从当前格子出发的最大移动次数。

总结与展望

总结:

  • 通过DFS搜索和回溯思想解决了路径规划问题。
  • 注意了路径的状态保存(visited和is_uphill)以及回溯逻辑,确保代码可以正确尝试所有可能的路径。

优化空间:

  • 剪枝:若某方向无法形成有效路径,则尽早结束递归,减少不必要的搜索。
  • 动态规划(DP)优化:将路径的中间结果保存下来,避免重复计算。例如,用一个三维DP数组dp[x][y][is_uphill]保存从某点出发的最大移动次数。
  • 并行计算:如果地图规模较大,可以通过多线程或分区计算提升性能。

扩展问题:

  • 如果地图变为动态生成的(如无穷大地图),如何设计算法?
  • 如果添加新的移动规则(如能在高度相同点间跳跃),如何调整现有代码逻辑?

通过这个问题,可以进一步熟悉DFS与状态记录的结合,也为动态规划的理解打下基础。