合縱連橫: 從 格子點DP框架 理解 最小成本的下降路徑

更新於 發佈於 閱讀時間約 13 分鐘

這篇文章,會帶著大家複習以前學過的格子點DP框架

並且以最小成本的下降路徑的應用題與概念為核心,

貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。


最小成本下降路徑的形式

每個格子點的值代表經過的成本。

要求從最上面那排往下方走,落到最下一排的最小成本的下降路徑


最小成本的下降路徑 I Minimum Falling Path Sum - LeetCode


這題下降路徑的規則是:

往下一排的時候,只能往左下、正下、右下的格子點

raw-image

上圖這個範例中,下墜路徑的最小成本是13

可以是 1 -> 5 -> 7 或者 1 -> 4 -> 8


反過來說,等價的說法就是:

這一排的個子點,往上回溯的時候,只有可能從 左上,正上,右上的格子點過來

再更進一步,
如果我們定義DP[row][col]代表從最上面落到[row][col]這個格子點的最小成本
通則寫成虛擬碼或者演算法的形式,就是


DP[row][col]

= 落到[row][col]的最小成本

= [row][col]這個格子點的成本 + 從第一排落到上一排格子點的成本

= matrix[rol][col] + min{ 左上格子點的下降路徑成本、正上格子點的下降路徑成本、右上格子點的下降路徑成本}

=matrix[rol][col] + min{ DP[row-1][col-1]、DP[row-1][col]、DP[row-1][col+1]}


初始條件呢?

其實初始條件就是最上排,也就是第一排的格子點,相等於起點的那一排

DP[row][col] 當 row 等於 0的時候

=[rol][col]這個格子點自己本身的成本

=matrix[row][col]


現在DP定義、通則、初始條件都有了,寫成程式碼即可。

Top-down DP程式碼

class Solution:
def minFallingPathSum(self, matrix: List[List[int]]) -> int:

h, w = len(matrix), len(matrix[0])
INF = sys.maxsize

memo = {}
def dp(row, col):

# Look-up DP table
if (row, col) in memo:
return memo[row, col]

## Base case: top row
if row == 0 and 0 <= col < w:
memo[0,col] = matrix[0][col]
return matrix[0][col]

## Base case: out-of boundary
if col < 0 or col >= w:
memo[row,col] = INF
return INF

## General case: current cost + minimal cost of neighbor on previous row
memo[row, col] = matrix[row][col] + min( dp(row-1,col+offset) for offset in (-1, 0, 1) )
return memo[row, col]

# ------------------------------------------------
return min( dp(h-1, col) for col in range(w) )


等價的Bottom-up DP程式碼

class Solution:
def minFallingPathSum(self, matrix: List[List[int]]) -> int:

size = len(matrix)

if size == 1:
# Quick response for single row
return matrix[0][0]


# Update matrix[y][x] from second row to last row
for y in range( 1, size):

# sacn each column from 0 to size-1
for x in range( size ):

# find falling path of minimal cost with optimal substructure
min_prev = matrix[y-1][x]

if x > 0:
min_prev = min( min_prev, matrix[y-1][x-1] )

if x < size-1:
min_prev = min( min_prev, matrix[y-1][x+1] )

# update the cost of falling path, destination is [y][x], with optimal substructure
matrix[y][x] = matrix[y][x] + min_prev


# the cost of minimum falling path is the minimum value of last row
return min( matrix[size-1] )


接下來再看一個變化題。

最小成本的下降路徑 II Minimum Falling Path Sum II


這題下降路徑的規則是:

往下一排的時候,不能在同一個column,也就是x座標(column)不能相同

raw-image

上圖這個範例中,下墜路徑的最小成本是13

最佳下墜路徑是 1 -> 5 -> 7


反過來說,等價的說法就是:

這一排的個子點,往上回溯的時候,只有可能從上方x座標不同的格子點過來


再更進一步,

如果我們定義DP[row][col]代表從最上面落到[row][col]這個格子點的最小成本

通則寫成虛擬碼或者演算法的形式,就是


DP[row][col]

= 落到[row][col]的最小成本

= [row][col]這個格子點的成本 + 從第一排落到上一排格子點的成本

= matrix[rol][col] + min{ 上一排x座標不同的格子點的下降路徑成本}

=matrix[rol][col] + 上一排格子點的下降路徑成本最小值
(假如正上方的格子點不是最小值)

或者

=matrix[rol][col] + 上一排格子點的下降路徑成本次小值
(假如正上方的格子點是最小值)


初始條件呢?

其實初始條件就是最上排,也就是第一排的格子點,相等於起點的那一排

DP[row][col] 當 row 等於 0的時候

=[rol][col]這個格子點自己本身的成本

=matrix[row][col]


現在DP定義、通則、初始條件都有了,寫成程式碼即可。

Top-down DP程式碼

class Solution:
def minFallingPathSum(self, matrix: List[List[int]]) -> int:

h = w = len(matrix)

memo = {}
smallest_2 = {}
def dp(row, col):

# Look-up DP table
if (row, col) in memo:
return memo[row, col]

## Base case: top row
if row == 0 and 0 <= col < w:
memo[row, col] = matrix[0][col]
return memo[row, col]

## General case: current cost + minimal cost of neighbor on previous row
if row-1 not in smallest_2:
smallest_2[row-1] = heapq.nsmallest(2, [dp(row-1, col) for col in range(w)])


# Cannot fall to same column
if memo[row-1,col] == smallest_2[row-1][0]:
memo[row, col] = matrix[row][col] + smallest_2[row-1][1]

else:
memo[row, col] = matrix[row][col] + smallest_2[row-1][0]


return memo[row, col]

# ------------------------------------------------
return min( dp(h-1, col) for col in range(w) )


等價的Bottom-up DP程式碼

class Solution:
def minFallingPathSum(self, arr: List[List[int]]) -> int:

h = w = len(arr)

## Base case:
# top row
if h == 1:
return arr[0][0]

## General case:
# current cost + minimal cost of neighbor on previous row
for y in range(1, h):

min_prev_candidates = heapq.nsmallest(2, arr[y - 1])
for x in range(w):

if arr[y-1][x] == min_prev_candidates[0]:
arr[y][x] = arr[y][x] + min_prev_candidates[1]
else:
arr[y][x] = arr[y][x] + min_prev_candidates[0]

return min( arr[h-1] )



結語

好,今天一口氣介紹了最精華的部分,

通用的格子點DP框架,還有相關的最小成本的下降路徑應用題與演算法建造,


希望能幫助讀者、觀眾徹底理解它的原理,

並且能夠舉一反三,洞察其背後的本質和了解相關的應用領域!


感謝收看囉! 我們下篇文章再見~

留言
avatar-img
留言分享你的想法!
avatar-img
小松鼠的演算法樂園
95會員
426內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
2024/06/03
給定一個二維的二元矩陣,計算正方形的最大面積。利用DP演算法及最大化正方形邊長的方法,遍歷矩陣,釐清DP初始狀態並推導出DP狀態轉移關係式。複雜度分析說明了時間複雜度和空間複雜度。關鍵知識點是找出最大的正方形邊長。
Thumbnail
2024/06/03
給定一個二維的二元矩陣,計算正方形的最大面積。利用DP演算法及最大化正方形邊長的方法,遍歷矩陣,釐清DP初始狀態並推導出DP狀態轉移關係式。複雜度分析說明了時間複雜度和空間複雜度。關鍵知識點是找出最大的正方形邊長。
Thumbnail
2024/06/01
動態規劃Dynamic Programming其實是 一種泛用的演算法思考方式與演算法建構框架。 動態規劃並不拘束於只能解課本上特定的的範例題。 只要我們能找出DP狀態定義、DP遞迴結構、初始條件(終止條件),就能適用動態規劃來解題,以數學的形式表達,並且在紙筆上或者電腦上、計算機上計算
Thumbnail
2024/06/01
動態規劃Dynamic Programming其實是 一種泛用的演算法思考方式與演算法建構框架。 動態規劃並不拘束於只能解課本上特定的的範例題。 只要我們能找出DP狀態定義、DP遞迴結構、初始條件(終止條件),就能適用動態規劃來解題,以數學的形式表達,並且在紙筆上或者電腦上、計算機上計算
Thumbnail
2024/06/01
本文章討論如何使用動態規劃和回頭查看技巧來計算最長遞增子序列的長度,並提供了相關的測試案例和範例。本文還包括了詳細的演算法和程式碼示例,以及時間和空間複雜度的分析。
Thumbnail
2024/06/01
本文章討論如何使用動態規劃和回頭查看技巧來計算最長遞增子序列的長度,並提供了相關的測試案例和範例。本文還包括了詳細的演算法和程式碼示例,以及時間和空間複雜度的分析。
Thumbnail
看更多
你可能也想看
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
題目敘述 Triangle 題目會給我們一個三角形的二維陣列triangle ,每個元素分別代表每個格子的成本,請問我們從最頂端到底部的下墜路徑的最小成本總和是多少? 每次下墜到下一排的時候,可以有兩種選擇: 1.往左下方的格子點移動。 2.往右下方的格子點移動。 測試範例 Examp
Thumbnail
題目敘述 Triangle 題目會給我們一個三角形的二維陣列triangle ,每個元素分別代表每個格子的成本,請問我們從最頂端到底部的下墜路徑的最小成本總和是多少? 每次下墜到下一排的時候,可以有兩種選擇: 1.往左下方的格子點移動。 2.往右下方的格子點移動。 測試範例 Examp
Thumbnail
Minimum Path Sum 給定一個矩陣,每個格子點代表經過的對應成本。 每回合可以往右移動一格或往下移動一格。 請問從起點左上角 走到 終點右下角的最小路徑成本總和是多少?
Thumbnail
Minimum Path Sum 給定一個矩陣,每個格子點代表經過的對應成本。 每回合可以往右移動一格或往下移動一格。 請問從起點左上角 走到 終點右下角的最小路徑成本總和是多少?
Thumbnail
這篇文章,會帶著大家複習以前學過的 格子點DP框架, 並且以最小成本的下降路徑的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 最小成本下降路徑的形式 每個格子點的值代表經過的成本。 要求從最上面那排往下方走,落到最下一排的最小成本的下降路徑。
Thumbnail
這篇文章,會帶著大家複習以前學過的 格子點DP框架, 並且以最小成本的下降路徑的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 最小成本下降路徑的形式 每個格子點的值代表經過的成本。 要求從最上面那排往下方走,落到最下一排的最小成本的下降路徑。
Thumbnail
這篇文章,會帶著大家複習以前學過的數列DP框架, 並且以費式數列、爬樓梯、骨牌拚接的應用與遞迴數列概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 數列DP與遞迴數列常見的形式 如果是遞迴數列,常常看到以函數型式表達
Thumbnail
這篇文章,會帶著大家複習以前學過的數列DP框架, 並且以費式數列、爬樓梯、骨牌拚接的應用與遞迴數列概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 數列DP與遞迴數列常見的形式 如果是遞迴數列,常常看到以函數型式表達
Thumbnail
這篇文章,會帶著大家複習以前學過的 區間DP框架, 並且以回文子字串、回文子序列的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 回文字串的基本定義 s = s[::-1] 也就是說字串s的正序 和 逆序完全相同。 回文字串的基本結構 空字串"
Thumbnail
這篇文章,會帶著大家複習以前學過的 區間DP框架, 並且以回文子字串、回文子序列的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 回文字串的基本定義 s = s[::-1] 也就是說字串s的正序 和 逆序完全相同。 回文字串的基本結構 空字串"
Thumbnail
這篇文章,會帶著大家複習以前學過的DFS框架, 並且以圖論的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): # 邊界條件 if base case or stop cond
Thumbnail
這篇文章,會帶著大家複習以前學過的DFS框架, 並且以圖論的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): # 邊界條件 if base case or stop cond
Thumbnail
這篇文章,會帶著大家複習以前學過的格子點DP框架, 並且以移動路徑Unique Path的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 格子點DP框架 依循題目的定義和規則,找出格子點移動的共同模式。 以本篇文章的例題為例,每一步可以選擇往右走一個
Thumbnail
這篇文章,會帶著大家複習以前學過的格子點DP框架, 並且以移動路徑Unique Path的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 格子點DP框架 依循題目的定義和規則,找出格子點移動的共同模式。 以本篇文章的例題為例,每一步可以選擇往右走一個
Thumbnail
這篇文章,會帶著大家複習以前學過的DFS框架 結合樹型DP, 並且以路徑和Path Sum的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): if base case or sto
Thumbnail
這篇文章,會帶著大家複習以前學過的DFS框架 結合樹型DP, 並且以路徑和Path Sum的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): if base case or sto
Thumbnail
最近會試著寫一些統整類的文章, 幫助讀者、觀眾整理、吸收、複習已經學習到的演算法框架。 找零錢框架 在以前學過的題目中,我們已經學會了考零錢的抽象思考邏輯與框架,就是試著用每一種銅板去湊出n元(也就是找零錢的過程) 寫成虛擬碼或演算法,找零錢用了幾枚銅板可以這樣表達 # 銅板數目累加​
Thumbnail
最近會試著寫一些統整類的文章, 幫助讀者、觀眾整理、吸收、複習已經學習到的演算法框架。 找零錢框架 在以前學過的題目中,我們已經學會了考零錢的抽象思考邏輯與框架,就是試著用每一種銅板去湊出n元(也就是找零錢的過程) 寫成虛擬碼或演算法,找零錢用了幾枚銅板可以這樣表達 # 銅板數目累加​
Thumbnail
題目敘述 題目會給我們一個二維陣列matrix,分別代表每個格子的成本,請問我們從最頂端到底部的下墜路徑的最小成本總和是多少? 每次下墜到下一排的時候,可以有三種選擇: 1.往左下角移動。 2.往正下方移動。 3.往右下角移動。 題目的原文敘述 測試範例 Example 1:
Thumbnail
題目敘述 題目會給我們一個二維陣列matrix,分別代表每個格子的成本,請問我們從最頂端到底部的下墜路徑的最小成本總和是多少? 每次下墜到下一排的時候,可以有三種選擇: 1.往左下角移動。 2.往正下方移動。 3.往右下角移動。 題目的原文敘述 測試範例 Example 1:
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News