【并查集 | Python】1631. 最小体力消耗路径

1631. 最小体力消耗路径


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

题目


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

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

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

示例 1:

图示 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

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

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

以上图源均来自力扣(LeetCode)

解题思路


思路:并查集

先审题,题目给定一个 r o w s × c o l u m n s rows \times columns rows×columns 的地图 h e i g h t s heights heights,其中 h e i g h t s [ r o w ] [ c o l ] heights[row][col] heights[row][col] 表示的是 ( r o w , c o l ) (row, col) (row,col) 这个格子的高度。

现在题的背景的进行远足,出发的地点在左上角 ( 0 , 0 ) (0, 0) (0,0) 处,目的地在图的右下角 ( r o w s − 1 , c o l u m n s − 1 ) (rows-1, columns-1) (rows1,columns1) 处。允许每次往 上,下,左,右 四个方向移动,要求找出耗费体力最小的路径。

其中题目定义的耗费体力值,由相邻格子之间 高度差绝对值最大值 决定。

即是,我们要找出一条路径,而这条路径格子之间相邻的 高度差绝对值 要尽可能的小。

其实本题可看是一个图模型:

  • 将地图的每个格子看成是图的每个顶点;
  • 将相邻格子用边进行相连,而边的权值为相邻两个格子之间 高度差绝对值
  • 要求找出一条从左上角到右下角的最短路径,而路径的长度取决于所有边的权值的最大值。

这里我们用并查集来解决这个问题,具体思路如下:

  • 实现并查集数据结构;

注意,由于给定的地图是二维形式的,这里将每个格子映射在一维表格中,对应唯一的编号。

例如将第 i i i 行,第 j j j 列的格子映射在一维表格中,那么对应的索引应该是 i × n + j i \times n + j i×n+j,其中 n n n 表示列的宽度。

  • 遍历,从图的左上角开始出发,向右向下开始添加边,由变量 e d g e s edges edges 存储连接相邻格子的边。这里边包含的元素 ( a , b , w i ) (a, b, wi) (a,b,wi),其中 a , b a, b a,b 表示边连接的两个顶点,而 w i wi wi 表示边的权值,也就是相邻格子的高度差绝对值;
  • e d g e s edges edges 按照 w i wi wi(边的权值)大小进行升序排序;
  • 开始遍历 e d g e s edges edges 中所有边,对两个顶点进行合并,遍历的同时判断左上角与右下角是否连通:
    • 若连通,那么此时 w i wi wi(边的权值)即是要求的答案(步骤 3 已经将边进行升序排序)
    • 否则,继续遍历合并边的顶点。

具体代码实现如下。

class UnionFind:
    def __init__(self, n):
        self.parent = [i for i in range(n)]
    
    def find(self, x):
        if x != self.parent[x]:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, x, y):
        x_root = self.find(x)
        y_root = self.find(y)
        if x_root != y_root:
            self.parent[x_root] = y_root
    
    def connected(self, x, y):
        return self.find(x) == self.find(y)
        

class Solution:
    def minimumEffortPath(self, heights: List[List[int]]) -> int:
        rows = len(heights)
        cols = len(heights[0])

        edges = []

        for i in range(rows):
            for j in range(cols):
                # 格子映射在一维表格中
                idx = i * cols + j
                # 向右向下添加边
                # 边的元素 (a, b, wi),a、b 表示边连接的顶点,wi 表示边的权值(相邻格子的高度差绝对值)
                if i < rows - 1:
                    edges.append((idx, idx + cols, abs(heights[i+1][j] - heights[i][j])))
                if j < cols - 1:
                    edges.append((idx + 1, idx, abs(heights[i][j+1] - heights[i][j])))
        # 按照边的权值升序排序
        edges.sort(key=lambda x:x[2])

        uf = UnionFind(rows * cols)
        # 遍历,合并顶点
        for x, y, wi in edges:
            uf.union(x, y)
            # 判断左下角与右下角是否连通
            # 连通,返回当前的边的权值
            if uf.connected(0, rows * cols - 1):
                return wi

        return 0                

欢迎关注


公众号 【书所集录


如有错误,烦请指出,欢迎指点交流。若觉得写得还不错,麻烦点个赞👍,谢谢。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值