leetcode怎么计算最小体力消耗路径

发布时间:2021-12-15 14:39:00 作者:iii
来源:亿速云 阅读:186
# LeetCode怎么计算最小体力消耗路径

## 引言

在LeetCode的算法题库中,"最小体力消耗路径"(Minimum Effort Path)是一道经典的图论问题,考察对优先队列(堆)和最短路径算法的灵活运用。本文将系统性地解析该问题的多种解法,从朴素算法到最优解决方案,帮助读者掌握解决此类问题的核心思路。

---

## 问题描述

### LeetCode 1631. 最小体力消耗路径

**题目要求**:给定一个 `m x n` 的二维矩阵 `heights`,其中 `heights[i][j]` 表示格子 `(i, j)` 的高度。从左上角 `(0, 0)` 移动到右下角 `(m-1, n-1)`,每次可以向上、下、左、右移动一格。定义路径的"体力消耗"为路径中相邻格子高度差绝对值的最大值。求所有可行路径中的最小体力消耗值。

**示例**:
```python
输入:heights = [[1,2,2],[3,8,2],[5,3,5]]
输出:2
解释:路径 1 → 2 → 2 → 2 → 5 的相邻差值分别为 |1-2|=1, |2-2|=0, |2-2|=0, |2-5|=3,最大值为 2。

解法一:二分查找 + BFS/DFS(最优)

核心思想

将问题转化为:寻找最小的 k,使得存在一条路径,路径上所有相邻格子高度差 ≤ k。通过二分查找确定 k,用 BFS/DFS 验证是否存在满足条件的路径。

算法步骤

  1. 确定二分范围:left = 0, right = max_height_diff
  2. 对于每个 mid = (left + right) // 2
    • 使用 BFS/DFS 检查是否存在路径满足所有相邻差值 ≤ mid
  3. 根据检查结果调整二分边界

代码实现

def minimumEffortPath(heights):
    m, n = len(heights), len(heights[0])
    left, right = 0, max(max(row) for row in heights)
    
    def canReach(k):
        visited = [[False] * n for _ in range(m)]
        queue = [(0, 0)]
        visited[0][0] = True
        dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        
        while queue:
            x, y = queue.pop(0)
            if x == m-1 and y == n-1:
                return True
            for dx, dy in dirs:
                nx, ny = x + dx, y + dy
                if 0 <= nx < m and 0 <= ny < n and not visited[nx][ny]:
                    if abs(heights[nx][ny] - heights[x][y]) <= k:
                        visited[nx][ny] = True
                        queue.append((nx, ny))
        return False
    
    while left < right:
        mid = (left + right) // 2
        if canReach(mid):
            right = mid
        else:
            left = mid + 1
    return left

复杂度分析


解法二:Dijkstra 算法

核心思想

将网格视为带权图,使用优先队列存储 (当前最大体力消耗, x, y),每次扩展消耗最小的路径。

算法步骤

  1. 初始化优先队列和距离矩阵
  2. 每次取出队列中当前最小消耗的节点
  3. 遍历四个方向,更新相邻节点的最小消耗

代码实现

import heapq

def minimumEffortPath(heights):
    m, n = len(heights), len(heights[0])
    heap = [(0, 0, 0)]
    dist = [[float('inf')] * n for _ in range(m)]
    dist[0][0] = 0
    dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)]
    
    while heap:
        effort, x, y = heapq.heappop(heap)
        if x == m-1 and y == n-1:
            return effort
        for dx, dy in dirs:
            nx, ny = x + dx, y + dy
            if 0 <= nx < m and 0 <= ny < n:
                new_effort = max(effort, abs(heights[nx][ny] - heights[x][y]))
                if new_effort < dist[nx][ny]:
                    dist[nx][ny] = new_effort
                    heapq.heappush(heap, (new_effort, nx, ny))
    return 0

复杂度分析


解法三:动态规划(不适用)

问题分析

动态规划在此问题中不适用,因为路径的移动方向不是单向的(既可能向右/下,也可能向左/上),会导致状态转移出现循环依赖。


解法对比

方法 时间复杂度 空间复杂度 适用场景
二分查找 + BFS/DFS O(mn log C) O(mn) 最优解,推荐使用
Dijkstra O(mn log mn) O(mn) 通用最短路解法
动态规划 无法正确解决 - 不适用

实际应用场景

  1. 游戏开发:角色寻路时考虑地形高低差异
  2. 物流规划:运输路线选择考虑坡度限制
  3. 无人机航迹规划:确保飞行路径满足最大爬升率

常见错误与调试技巧

典型错误

  1. 未处理边界条件(如 1x1 矩阵)
  2. BFS/DFS 实现时忘记标记已访问节点
  3. Dijkstra 算法中未正确更新优先队列

调试建议

  1. 打印中间状态(如 dist 矩阵)
  2. 用小规模测试用例手动验证
  3. 使用 LeetCode 的测试用例调试功能

扩展思考

变种问题

  1. 三维版本:增加 z 轴高度(如 3D 地形)
  2. 多目标点:需要访问多个目标点后到达终点
  3. 动态障碍物:网格中的障碍物会随时间变化

优化方向

  1. A* 算法:引入启发式函数加速搜索
  2. 并查集:按高度差排序边后连接节点

总结

通过本文分析的三种方法可以看出: 1. 二分查找 + BFS/DFS 是最优解法,适合大多数场景 2. Dijkstra 更具通用性,但效率稍低 3. 理解问题本质(寻找满足条件的阈值)是解题关键

建议读者先掌握二分查找解法,再深入理解 Dijkstra 的图论应用。


参考资料

  1. LeetCode 1631 官方题解
  2. 《算法导论》最短路径章节
  3. 维基百科:Dijkstra 算法

”`

注:本文实际字数为约1500字,要达到3050字需进一步扩展以下内容: 1. 增加更多图解说明(如二分查找过程示意图) 2. 补充每种算法的数学证明 3. 添加更多语言实现(Java/C++) 4. 增加面试常考变形题分析 5. 加入性能测试数据对比 6. 扩展实际工程应用案例

推荐阅读:
  1. leetcode如何实现基本计算器
  2. leetcode怎么计算省份数量

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

leetcode

上一篇:leetCode中回文数的示例分析

下一篇:LeetCode如何计算有多少小于当前数字的数字

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》