LeetCode 1631. 最小体力消耗路径 BFS-详细题解

题目描述

给定一个二维矩阵 heights,其中 heights[row][col] 表示格子 (row, col) 的高度。从左上角 (0, 0) 出发,移动到右下角 (rows-1, cols-1),每次可以向上、下、左、右四个方向移动。路径的体力消耗由路径中相邻格子的高度差绝对值的最大值决定。要求找到一条路径,使得体力消耗值最小。

示例

输入:heights = [[1,2,2],[3,8,2],[5,3,5]]
输出:2
解释:路径 [1→3→5→3→5] 的最大高度差为 2。

解题思路
问题分析

我们需要找到一条从起点到终点的路径,使得路径中所有相邻格子的高度差的最大值最小。这个问题可以转化为一个最短路径问题的变种,但路径的“权重”不再累加,而是取路径中的最大值。

Dijkstra算法变种

Dijkstra算法通常用于解决权重累加的最短路径问题,但本题的关键在于“路径的最大高度差”。我们可以对Dijkstra进行如下调整:

  1. 优先级队列:使用最小堆,按当前路径的最大高度差排序。

  2. 松弛操作:当移动到相邻格子时,计算新的高度差,如果新的最大值比之前记录的更小,则更新路径。

为什么选择Dijkstra?
  • 我们需要确保每次扩展的路径是当前已知的最优解(即最大高度差最小的路径)。

  • 优先队列保证每次取出的节点都是当前最优的,从而可以快速找到终点时的最小消耗。


代码实现(详细注释)

cpp

#include 
#include 
#include 
#include 
#include 
using namespace std;

class Solution {
public:
    // 定义节点结构,保存坐标(x,y)和当前路径的最大高度差dist
    struct Node {
        int x, y, dist;
        Node(int _x, int _y, int _dist) : x(_x), y(_y), dist(_dist) {}
        // 重载运算符<,使优先队列按dist从小到大排序(小顶堆)
        bool operator<(const Node& other) const {
            return dist > other.dist; 
        }
    };
    int minimumEffortPath(vector>& heights) {
        int rows = heights.size();
        int cols = heights[0].size();
        // 初始化距离矩阵,记录到达每个格子的最小最大高度差
        vector> dist(rows, vector(cols, INT_MAX));
        priority_queue pq; // 优先队列(小顶堆)
        // 起点初始化:起点到自身的体力消耗为0
        dist[0][0] = 0;
        pq.push(Node(0, 0, 0));
        // 四个方向的坐标偏移量:右、下、左、上
        vector dx = {0, 1, -1, 0};
        vector dy = {1, 0, 0, -1};

        while (!pq.empty()) {
            Node cur = pq.top();
            pq.pop();
            // 如果到达终点,直接返回当前路径的最大高度差
            if (cur.x == rows - 1 && cur.y == cols - 1) {
                return cur.dist;
            }
            // 如果当前路径的dist已经不是最优,跳过(可能已被更优路径更新过)
            if (cur.dist > dist[cur.x][cur.y]) {
                continue;
            }
            // 遍历四个方向
            for (int i = 0; i < 4; i++) {
                int nx = cur.x + dx[i];
                int ny = cur.y + dy[i];
                // 检查新坐标是否越界
                if (nx >= 0 && nx < rows && ny >= 0 && ny < cols) {
                    // 计算移动到(nx, ny)的新高度差,取当前路径的最大值
                    int newDist = max(cur.dist, abs(heights[nx][ny] - heights[cur.x][cur.y]));
                    // 如果新路径更优,则更新距离矩阵,并加入队列
                    if (newDist < dist[nx][ny]) {
                        dist[nx][ny] = newDist;
                        pq.push(Node(nx, ny, newDist));
                    }
                }
            }
        }
        // 若所有路径都探索过,返回终点的最小消耗
        return dist[rows - 1][cols - 1];
    }
};

关键细节解析
  1. 小顶堆的作用
    优先队列按 dist 从小到大排序,确保每次扩展的路径是当前已知最优的。这样当第一次到达终点时,路径的体力消耗一定是最小的。

  2. 距离矩阵 dist 的意义
    dist[i][j] 表示从起点到 (i, j) 的所有路径中,最大的高度差的最小值。例如,dist[1][1] = 3 表示到达 (1,1) 的最优路径的最大高度差为3。

  3. 为何需要判断 cur.dist > dist[cur.x][cur.y]
    可能有多条路径到达同一个坐标 (x, y)。如果当前路径的 dist 已经比之前记录的更大,说明这条路径不可能是最优解,直接跳过。

  4. 时间复杂度
    使用优先队列优化的Dijkstra算法,时间复杂度为 O(mn log(mn)),其中 m 和 n 是网格的行数和列数。


相关思考题
1. 水位上升的泳池中游泳(LeetCode 778)

问题:在一个 N x N 的网格中,每个格子表示水位高度。你从起点 (0, 0) 出发,每次可以游向相邻四个方向。当且仅当水位升至格子高度时才能游过。求从起点到终点所需的最小等待时间(即路径中格子高度的最大值)。
答案:同样使用Dijkstra算法,维护路径中的最大高度值。

2. 最大最小路径(LeetCode 1102)

问题:给定一个二维矩阵,找到一条从左上角到右下角的路径,使得路径中经过的格子的最小值最大。
答案:使用优先队列(最大堆),按路径中的最小值从大到小排序,每次扩展路径时更新最小值。


通过这种思路,可以解决许多类似的路径优化问题,关键在于灵活调整Dijkstra算法的权重定义和优先级规则。

欢迎指出不足之处!!!

你可能感兴趣的:(算法-BFS(C++实现),算法)