LeetCode 1631. 最小體力消耗路徑【並查集】
技術標籤:演算法演算法圖論golang並查集leetcode
你準備參加一場遠足活動。給你一個二維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,1,1,1],[1,2,1,2,1],[1,2,1,2,1],[1,2,1,2,1],[1,1,1,2,1]]
輸出:0
解釋:上圖所示路徑不需要消耗任何體力。
解法:
首先想要求解這種最短路徑會想到動態規劃或Dijkstra演算法
但這題可以上下左右移動,推不出遞推公式;最短路徑演算法的證明過程理解起來又比較繁瑣,所以選擇了並查集來解:
1、二維陣列的點集可以轉換為有權無向圖的邊,邊的權重就是相鄰兩個點的abs(高度差)、每個點的唯一編號可以通過 i*len(arr[0])+j來表示
舉例:與(0, 0)聯通兩個頂點和邊權分別是(0,1) abs(heights[0][0] -heights[0][1]),(1,0)abs(heights[0][0] -heights[1][0])
遍歷二維陣列得到edge陣列
2、根據邊權升序排序edge陣列
3、遍歷edge陣列,依次向並查集新增邊,直到(0,0)點與(len,len)點聯通:最後新增的這條邊的權重,即為整個通路的最小權重
這裡的2、3兩步有沒有克魯斯卡演算法的影子?哈哈
上程式碼:
func minimumEffortPath(heights [][]int) int { if len(heights) <= 0 { return 0 } var edges [][]int // 構造邊和權值 n, m := len(heights), len(heights[0]) for i := range heights { for j, h := range heights[i] { id := i * m + j if i > 0 { edges = append(edges, []int{id - m, id, abs(h - heights[i-1][j])}) } if j > 0 { edges = append(edges, []int{id - 1, id, abs(h - heights[i][j-1])}) } } } // 根據權值排序 sort.Slice(edges, func(i,j int) bool { return edges[i][2] < edges[j][2] }) // 使用並查集,新增權值並維護max,只要0和i * len(heights) + j聯通,則證明是最短路徑了 u := NewUnion() s :=0 e := (n-1) * m + m-1 for _, edge := range edges { u.merge(edge[0], edge[1]) // 因為權值是升序,所以可認為最後一次加入的邊,肯定是需要用到的最大權值 if u.search(s) == u.search(e) { return edge[2] } } fmt.Println(u.m,e) return 0 } type union struct { m map[int]int } func NewUnion() *union { return &union{m: make(map[int]int)} } func (u *union) search(i int) int { p, ok := u.m[i] if !ok { return i } if u.m[p] != p { u.m[i] = u.search(p) } return u.m[i] } func (u *union) merge(i,j int) { pI, pJ := u.search(i), u.search(j) if pI == pJ { return } u.m[pI] = pJ } func abs(i int) int { if i < 0 { i = -i } return i }