1. 程式人生 > 實用技巧 >LeetCode 120. 三角形最小路徑和 | Python

LeetCode 120. 三角形最小路徑和 | Python

120. 三角形最小路徑和


題目來源:力扣(LeetCode)https://leetcode-cn.com/problems/triangle

題目


給定一個三角形,找出自頂向下的最小路徑和。每一步只能移動到下一行中相鄰的結點上。

相鄰的結點 在這裡指的是 下標上一層結點下標 相同或者等於 上一層結點下標 + 1 的兩個結點。

例如,給定三角形:

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

自頂向下的最小路徑和為 11(即,2 + 3 + 5 + 1 = 11)。

解題思路


思路:遞迴,動態規劃

首先先看題目中的提示,【相鄰的結點 在這裡指的是 下標 與 上一層結點下標 相同或者等於 上一層結點下標 + 1 的兩個結點

】。

我們設 f(i, j) 為點 (i, j) 到底部的最小路徑和。現在根據上面這個提示,可以很容易得到公式:

f(i, j) = min(f(i+1, j), f(i+1, j+1)) + triangle[i][j]

也就是說,要求的路徑和為:取當前結點相鄰的兩個結點最小值,加上當前結點的值。

遞迴

先嚐試使用遞迴的方法求解,根據上面的公式,直接貼上程式碼:

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        return self.path(triangle, 0, 0)
    
    def path(self, triangle, i, j):
        # 設定終止條件
        if i == len(triangle):
            return 0
        
        # 直接使用公式
        return min(self.path(triangle, i+1, j), self.path(triangle, i+1, j+1)) + triangle[i][j]

上面的程式碼執行超時,因為進行了大量的重複計算,現在考慮進行優化。

遞迴(優化)

在這裡,採用建立備忘錄的方法,避免重複的計算,同樣這裡貼上程式碼:

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        # 備忘錄
        memo = {}
        
        def path(triangle, i, j):
            # 設定終止條件
            if i == len(triangle):
                return 0

            if (i, j) in memo:
                return memo[(i, j)]

            memo[(i, j)] = min(path(triangle, i+1, j), path(triangle, i+1, j+1)) + triangle[i][j]

            # 直接使用公式
            return min(path(triangle, i+1, j), path(triangle, i+1, j+1)) + triangle[i][j]
        
        return path(triangle, 0, 0)

上面的方法都是自頂向下的,現在我們嘗試使用動態規劃,實現自底向上求解。

動態規劃

使用動態規劃的解法,先進行狀態定義。

狀態定義

dp[i][j] 為點 (i, j) 到底部的最小路徑和。

狀態轉移方程

同樣的,我們根據最開始得出的公式,可以得到狀態轉移方程為:

dp[i][j] = min(dp[i+1][j], dp[i+1][j+1]) + triangle[i][j]

具體程式碼實現見【程式碼實現 # 動態規劃】

動態規劃(空間優化)

上面的動態規劃演算法中,我們定義的是一個二維陣列。當我們計算 dp[i][j] 的時候,用到的是下一行的 dp[i+1][j]dp[i+1][j+1]。那我們可以直接考慮從底部往上,定義一個一維陣列。

具體程式碼實現見【程式碼實現 # 動態規劃(空間優化)】

程式碼實現


# 動態規劃
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        m = len(triangle)

        dp = [[0] * (m+1) for _ in range(m+1)]

        for i in range(m-1, -1, -1):
            for j in range(0, i+1):
                dp[i][j] = min(dp[i+1][j], dp[i+1][j+1]) + triangle[i][j]
        
        return dp[0][0]

# 動態規劃(空間優化)
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        m = len(triangle)

        dp = [0] * (m+1)

        for i in range(m-1, -1, -1):
            for j in range(0, i+1):
                dp[j] = min(dp[j], dp[j+1]) + triangle[i][j]
        
        return dp[0]

實現結果


動態規劃(優化前)

歡迎關注


公眾號 【書所集錄