DP動態規劃 深入淺出 以Fibonacci Number 費式數列 為例

小松鼠-avatar-img
發佈於演算法題目解析 個房間
更新 發佈閱讀 5 分鐘
raw-image

Dynamic programming 對初學者似乎是很大的一道門檻。

教科書往往寫得太生硬,用幾行數學式子去代表背後的數學意義,對於剛開始接觸的同學會學得很吃力。

常常想破頭也想不出來,但是看到解答又好像看得懂,但不知道破題的關鍵到底從哪裡跑出來的。

別害怕,筆者也走過同樣的路。 XD

今天會帶領讀者從DP的大原則出發,從簡單的題目開始理解DP解題的精隨框架


Dynamic programming本質是透過遞迴關係式,去解決一個大規模的問題,而這個大規模的問題又可以被分解為較小規模的子問題,而且子問題往往彼此重複

需要複習的話,可以點這篇 究竟什麼是 動態規劃DP?


Dynamic programming的解題框架可分為三大步驟:

1. 定義DP狀態 [我在哪裡]

2. 推導DP狀態轉移關係式(通則) [我從哪裡來] => [答案從哪裡推導而來]

3. 釐清初始狀態(也可以說是遞迴的終止條件) [第一步怎麼走,怎麼出發的]


以一個大家耳熟能詳的費式數列拿當作第一道題目破題作為練習。

這篇專欄有一個專屬的解題教學影片,搭配服用,效果更佳。


題目敘述

詳細的原文題目可在這裡看到

題目要求我們求出費式數列的第n項,並且已經給定初始條件和遞迴關係式如下:

F(0) = 0, F(1) = 1
F(n) = F(n - 1) + F(n - 2), for n > 1.

雖然題目已經直接奉送DP所需要的遞迴關係式和初始條件,但這裡我們還是先走過一遍解題框架可分為三大步驟,藉此鞏固解題基礎。

(備註: 遞迴關係式初始條件往往是要自己推導出來的,特別是在Medium/Hard或者競賽題)


1. 定義DP狀態
[我在哪裡]

這題沒有什麼疑問,直接定義DP[n] = 費式數列的第n項即可。

比較習慣數學符號思考的讀者,可以想成令f(n) = 費式數列的第n項。


2.推導DP狀態轉移關係式(通則)
[我從哪裡來] => [答案從哪裡推導而來]


這裡題目也已經給了,不過就算沒給,我們也知道費式數列的通則就是

費式數列的第n項 = 費式數列的第n-1項 + 費式數列的第n-2項

寫成遞迴關係式就是DP[n] = DP[n-1] + DP[n-2]


比較習慣數學符號思考的讀者,可以想成令f(n) = f(n-1) + f(n-2)

留意,其實到了這一步,我們已經把大規模的問題DP[n]分解成兩個較小規模的子問題DP[n-1]、 DP[n-2]。


並且,我們知道,在得到子問題的答案之後,可以推導出原本所求

DP[n] = DP[n-1] + DP[n-2]


3. 釐清DP初始狀態(終止條件)
[第一步怎麼走,怎麼出發的]


這裡題目已經指定,第零項F(0) = 0, 第一項F(1) = 1。

也就是說DP[0] = 0, DP[1] = 1

比較習慣數學符號思考的讀者,可以想成給定

f(0) = 0 且 f(1) = 1


到這裡,已經填滿解題框架的所有內容,接著我們把它轉成程式碼,

這裡以Python作為示範


程式碼

class Solution:
 def fib(self, n: int) -> int:
  
  def dp(n):
   
   ## look-up table
   if n in table:
    return table[n]
   
   ## general cases
   # DP[n] = DP[n-1] + DP[n-2]

   table[n] = dp(n-1) + dp(n-2)
   
   return table[n]
  
  # ---------------------------------

  # base case
  # DP[0] = 0
  # DP[1] = 1
  table = {0:0, 1:1}
  
  return dp(n)

複雜度分析

時間複雜度: O( n )

總共有O(n)層呼叫,每層呼叫在O(1)時間內可以計算完。

空間複雜度: O( n )

遞迴深度最深為O( n ),DP table空間所需也為O( n )


下一篇: 一魚多吃 用費式數列的DP模型來解 爬樓梯Climbing Stairs_Leetcode #70


Refernce

[1] Python from Naive method to DP + optimization [w/ Comment]

留言
avatar-img
留言分享你的想法!
avatar-img
小松鼠的演算法樂園
96會員
427內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
2024/09/13
給定一個整數陣列arr,和一串區間XOR請求queries。 請計算queries所請求的區間XOR值,並且以陣列的形式返回答案。
Thumbnail
2024/09/13
給定一個整數陣列arr,和一串區間XOR請求queries。 請計算queries所請求的區間XOR值,並且以陣列的形式返回答案。
Thumbnail
2024/08/27
Path with Maximum Probability 題目給定一個無向圖(雙向移動皆可), 提供每條邊的起終點,和每條邊對應的通過時的成功機率。 請問從起點start走到終點end的最高成功機率是多少? 如果完全沒有路徑可以抵達,則返回0。
Thumbnail
2024/08/27
Path with Maximum Probability 題目給定一個無向圖(雙向移動皆可), 提供每條邊的起終點,和每條邊對應的通過時的成功機率。 請問從起點start走到終點end的最高成功機率是多少? 如果完全沒有路徑可以抵達,則返回0。
Thumbnail
2024/08/21
題目敘述 664. Strange Printer 有一台奇怪的印表機, 每次操作只能連續印同樣的字母,但是列印的長度可以自由控制。 而且,印刷的時候,可以蓋過去舊的字元。 (這邊當然不合常理,讀者可以理解成塗了立可帶再蓋過去的情境) 給定一個輸入字串s,請問最少需要幾次操作,才能印出字串s?
Thumbnail
2024/08/21
題目敘述 664. Strange Printer 有一台奇怪的印表機, 每次操作只能連續印同樣的字母,但是列印的長度可以自由控制。 而且,印刷的時候,可以蓋過去舊的字元。 (這邊當然不合常理,讀者可以理解成塗了立可帶再蓋過去的情境) 給定一個輸入字串s,請問最少需要幾次操作,才能印出字串s?
Thumbnail
看更多
你可能也想看
Thumbnail
這題算是路徑計數類的DP衍伸題(路徑數、方法數、組合數...等等這種枚舉類的題目,第一時間切入除了想到DFS+回溯法之外,也可以留意DP動態規劃解題的可能性) 題目會給我們一個指定長度為arrLen的陣列,起點從index=0開始出發,每次移動可以往左移一格,往右移一格,或是留在原地不
Thumbnail
這題算是路徑計數類的DP衍伸題(路徑數、方法數、組合數...等等這種枚舉類的題目,第一時間切入除了想到DFS+回溯法之外,也可以留意DP動態規劃解題的可能性) 題目會給我們一個指定長度為arrLen的陣列,起點從index=0開始出發,每次移動可以往左移一格,往右移一格,或是留在原地不
Thumbnail
這題基本上是前一題巴斯卡三角形的孿生題,那題和這題的本質是完全一樣的,只是題目要求稍有不同。 前一題求的是整個巴斯卡三角形,這一題求的是巴斯卡三角形的最後一層。
Thumbnail
這題基本上是前一題巴斯卡三角形的孿生題,那題和這題的本質是完全一樣的,只是題目要求稍有不同。 前一題求的是整個巴斯卡三角形,這一題求的是巴斯卡三角形的最後一層。
Thumbnail
在學習過比較基本的DP模型 費式數列、爬樓梯、找零錢...等之後, 來看一個比較進階而且實用的DP模型,前綴和(Prefix sum), 可以再加以延伸推廣,來計算 區間和(Range Sum)。
Thumbnail
在學習過比較基本的DP模型 費式數列、爬樓梯、找零錢...等之後, 來看一個比較進階而且實用的DP模型,前綴和(Prefix sum), 可以再加以延伸推廣,來計算 區間和(Range Sum)。
Thumbnail
如同這個Dynamic programming 深入淺出系列的開始, 在經過比較簡單的入門題(Coin Change)之後, 來看比較進階的二維DP題目Unique Path
Thumbnail
如同這個Dynamic programming 深入淺出系列的開始, 在經過比較簡單的入門題(Coin Change)之後, 來看比較進階的二維DP題目Unique Path
Thumbnail
今天再來看一題入門的2D DP題目: 巴斯卡三角形 再次複習Dynamic programming的解題框架,可分為三大步驟 1.定義狀態 [我在哪裡] 2. 定義狀態轉移關係式(通則) [我從哪裡來] => [答案從哪裡推導而來] 3. 釐清初始狀態(也可以說是遞
Thumbnail
今天再來看一題入門的2D DP題目: 巴斯卡三角形 再次複習Dynamic programming的解題框架,可分為三大步驟 1.定義狀態 [我在哪裡] 2. 定義狀態轉移關係式(通則) [我從哪裡來] => [答案從哪裡推導而來] 3. 釐清初始狀態(也可以說是遞
Thumbnail
這題乍看之下是新題目,但仔細一想, 其實只是前面介紹過的費式數列的小變形而已,解題思想基本不變。 題目說每次可以爬一階樓梯或兩階樓梯,問爬到n階的方法數是多少。
Thumbnail
這題乍看之下是新題目,但仔細一想, 其實只是前面介紹過的費式數列的小變形而已,解題思想基本不變。 題目說每次可以爬一階樓梯或兩階樓梯,問爬到n階的方法數是多少。
Thumbnail
在經過比較簡單的入門題(Coin Change)之後, 來看進階一點的DP題目Coin Change II 整零錢的全部方法數。 不免俗,再次強調DP的解題框架,鞏固知識點。
Thumbnail
在經過比較簡單的入門題(Coin Change)之後, 來看進階一點的DP題目Coin Change II 整零錢的全部方法數。 不免俗,再次強調DP的解題框架,鞏固知識點。
Thumbnail
深入淺出,從最基本的 費式數列, 一探動態規劃的奧秘與精隨。
Thumbnail
深入淺出,從最基本的 費式數列, 一探動態規劃的奧秘與精隨。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News