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



输入：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
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
"""

from typing import *

class Solution:
    def union(self, i: int, j: int, father: List[int]):
        p_i = self.find(i, father)
        p_j = self.find(j, father)
        father[p_i] = p_j

    def find(self, p, father: List[int]):
        r = p
        while father[p] != p:
            p = father[p]
        while father[r] != p:
            father[r], r = p, father[r]
        return p

    def minimumEffortPath(self, heights: List[List[int]]) -> int:
        m, n = len(heights), len(heights[0])
        father = [i for i in range(m*n)]
        edges = dict()
        for i in range(m):
            for j in range(n):
                cur, right, down = (i, j), (i, j+1), (i+1, j)
                if j+1 < n:
                    edges[(i, j, i, j+1)] = abs(heights[i][j] - heights[i][j+1])
                if i+1 < m:
                    edges[(i, j, i+1, j)] = abs(heights[i][j] - heights[i+1][j])
        sorted_edges = sorted(edges.items(), key=lambda x: x[1])

        start, end = 0, m*n-1
        for each in sorted_edges:
            first, second, edge = each[0][0]*n + each[0][1], each[0][2]*n+each[0][3], each[1]
            self.union(first, second, father)
            if self.find(start, father) == self.find(end, father):
                return edge
        return 0