跳至主要內容

最小体力消耗路径

yczha大约 2 分钟Algorithmleetcode回溯PythonGolang

题目表述

你准备参加一场远足活动。给你一个二维 rows x 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

示例2
示例2

输入:heights = [[1,2,3],[3,8,4],[5,3,5]]

输出:1

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

示例 3

示例3
示例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

解法一:并查集

可以把二维表看成是一张无环图,边的节点差值的绝对值是无环图边的权重。

将无环图的边按权重排序,依次加入并查集直到左上角跟右下角连通即可。

时间复杂度:O(mnlog(mn)) 空间复杂度:O(mn)

代码实现

from typing import List


class UnionFind:
    """并查集"""
    def __init__(self, n:int):
        self.parent = list(range(n))
        self.size = [1] * n
    
    def findSet(self, x: int)->int:
        """查找元素的根元素,同时找到后将元素连接到根上"""
        if self.parent[x] == x:
            return x
        self.parent[x] = self.findSet(self.parent[x])
        return self.parent[x]
    
    def unite(self, x, y)->bool:
        """将两个集合合并"""
        # 已经在同一个集合中,无需合并
        if self.parent[x] == self.parent[y]:
            return False
        # 查找根元素
        x = self.findSet(x)
        y = self.findSet(y)
        # 将较小的树加入较大者(保证查找效率)
        if self.size[x] < self.size[y]:
            x, y = y, x
        self.parent[y] = x
        self.size[x] += self.size[y]
        return True

    def connected(self, x, y)->bool:
        """两个节点是否连通"""
        return self.findSet(x) == self.findSet(y)


class Solution:
    def minimumEffortPath(self, heights: List[List[int]]) -> int:
        m, n = len(heights), len(heights[0])
        edges = []
        for i in range(m):
            for j in range(n):
                idx = i * n + j
                if i > 0: # 垂直边
                    edges.append([idx-n, idx, abs(heights[i][j]-heights[i-1][j])])
                if j > 0: # 水平边
                    edges.append([idx-1, idx, abs(heights[i][j]-heights[i][j-1])])
        # 将边按照权重排序
        edges.sort(key=lambda x:x[2])
        # 并查集搜索,只需要找到连通的路径即可
        uf = UnionFind(m*n)
        res = 0
        for x, y, v in edges:
            uf.unite(x, y)
            if uf.connected(0, m*n-1):
                res = v
                break
        return res