LeetCode_dijkstra算法_中等_1631. 最小体力消耗路径

x33g5p2x  于2022-05-05 转载在 其他  
字(2.8k)|赞(0)|评价(0)|浏览(225)

1.题目

你准备参加一场远足活动。给你一个二维 rows * columns 的地图 heights ,其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) ,且你希望去最右下角的格子 (rows-1, columns-1) (注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动,你想要找到耗费体力最小的一条路径。

一条路径耗费的体力值是路径上相邻格子之间高度差绝对值的最大值决定的。

请你返回从左上角走到右下角的最小体力消耗值 。

示例 1:

输入:heights = [[1,2,2],[3,8,2],[5,3,5]]
输出:2
解释:路径 [1,3,5,3,5] 连续格子的差值绝对值最大为 2 。
这条路径比路径 [1,2,2,2,5] 更优,因为另一条路径差值最大值为 3 。

示例 2:

输入:heights = [[1,2,3],[3,8,4],[5,3,5]]
输出:1
解释:路径 [1,2,3,4,5] 的相邻格子差值绝对值最大为 1 ,比路径 [1,3,5,3,5] 更优。

示例 3:

输入:heights = [[1,2,1,1,1],[1,2,1,2,1],[1,2,1,2,1],[1,2,1,2,1],[1,1,1,2,1]]
输出:0
解释:上图所示路径不需要消耗任何体力。

提示:
rows == heights.length
columns == heights[i].length
1 <= rows, columns <= 100
1 <= heights[i][j] <= 106

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-with-minimum-effort

2.思路

(1)dijkstra算法
思路参考我写了一个模板,把 DIJKSTRA 算法变成了默写题。本题与一般的最短路径题目有点不一样,这道题中评判一条路径的标准不是路径经过的权重总和,而是路径经过的所有节点中相邻节点的最大权重差值的绝对值

3.代码实现(Java)

//思路1————dijkstra算法
class Solution {
    public int minimumEffortPath(int[][] heights) {
        int m = heights.length;
        int n = heights[0].length;
        //efforts[i][j]表示从起点(0, 0)到(i, j)的最小体力消耗值
        int[][] efforts = new int[m][n];
        //初始化efforts,初始值均为Integer.MAX_VALUE
        for (int i = 0; i < m; i++) {
            Arrays.fill(efforts[i], Integer.MAX_VALUE);
        }
        //显然,从起点(0, 0)到自身的最小体力消耗值
        efforts[0][0] = 0;
        //定义优先级队列,distFromStart值较小的元素排在队首
        Queue<State> queue = new PriorityQueue<>((a, b) -> {
            //返回值为正数则交换 a 和 b
            return a.effortFromStart - b.effortFromStart;
        });
        //将起点(0, 0)加入到队列中
        queue.offer(new State(0, 0, 0));
        while (!queue.isEmpty()) {
            //取出队首元素
            State curState = queue.poll();
            int curX = curState.x;
            int curY = curState.y;
            int curEffortFromStart = curState.effortFromStart;
            //若到达终点,则直接结束,返回curEffortFromStart即可
            if (curX == m - 1 && curY == n - 1) {
                return curEffortFromStart;
            }
            //如果从起点到当前坐标的最小体力消耗值大于efforts[curX][curY],则说明当前节点不适合在路径中,直接跳过即可
            if (curEffortFromStart > efforts[curX][curY]) {
                continue;
            }
            //获取与坐标(curX, curY)相邻的坐标
            List<int[]> neighbors = adj(heights, curX, curY);
            // 将neighbors 加入到队列中
            for (int[] neighbor : neighbors) {
                int neiX = neighbor[0];
                int neiY = neighbor[1];
                //计算从起点(0, 0)达到(neiX, neiY)的体力消耗值
                int effortToNeiNode = Math.max(efforts[curX][curY], Math.abs(heights[curX][curY] - heights[neiX][neiY]));
                if(effortToNeiNode < efforts[neiX][neiY]) {
                    //更新efforts
                    efforts[neiX][neiY] = effortToNeiNode;
                    queue.offer(new State(neiX, neiY, effortToNeiNode));
                }
            }            
        }
        return -1;
    }

    //方向数组,上下左右的坐标偏移量
    int[][] dirs = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    
    //返回坐标(x, y)的上下左右相邻的坐标
    public List<int[]> adj(int[][] heights, int x, int y) {
        int m = heights.length;
        int n = heights[0].length;
        //存储相邻节点
        List<int[]> neighbors = new ArrayList<>();
        for (int[] dir : dirs) {
            int nx = x + dir[0];
            int ny = y + dir[1];
            //判断边界
            if (nx >= m || nx < 0 || ny >= n || ny < 0) {
                continue;
            }
            neighbors.add(new int[]{nx, ny});
        }
        return neighbors;
    }

    class State {
        //矩阵中的一个坐标位置(x, y)
        int x, y;
        //从起点(0, 0)到当前位置的最小体力消耗值
        int effortFromStart;

        //构造函数
        public State(int x, int y, int effortFromStart) {
            this.x = x;
            this.y = y;
            this.effortFromStart = effortFromStart;
        }
    }
}

相关文章

微信公众号

最新文章

更多