昨天同事說動態規劃很難,我說不會啊,理解了就很簡單,我同事表示不屑,以為我在炫技。於是乎我問了一個工作六年的前同事,他居然也覺得高大上,並且表示接觸過會動態規劃的朋友,覺得很牛逼。
我了個天,表示震驚了,簡直嚇的我瑟瑟發抖發抖好麼。既然如此,那我一定要讓大家理解,讓大家也牛逼牛逼。
以題舉例
以中國leetcode(力扣)的121道題《買賣股票的最佳時機》舉例,題目如下:
給定一個陣列,它的第 i 個元素是一支給定股票第 i 天的價格。如果你最多隻允許完成一筆交易(即買入和賣出一支股票),設計一個演算法來計算你所能獲取的最大利潤。注意你不能在買入股票前賣出股票。示例 1:輸入: [7,1,5,3,6,4]輸出: 5解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格。示例 2:輸入: [7,6,4,3,1]輸出: 0解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。複製程式碼
首先說明一點,在這道題動態規劃並不算是高效的演算法,這道題即使是暴力法也能比動態規劃法要快一些,但本文是為了講動態規劃才講這道題的,而非為了這道題講動態規劃的。純粹是這道題講動態規劃更簡單。
不瞭解動態規劃的朋友可能不知道,動態規劃幾乎都是存在套路的,步驟如下:
- 將問題拆解成單一問題製表
- 根據表中結果進行求解
第一步:將問題拆解成單一問題製表
這是動態規劃的重中之重,拆的好壞基本決定你動態規劃寫的好壞,動態規劃更是一種思想。
這道題就很簡單了,我們可以把這個問題,先拆成單一問題,即如果我在這天買入,在哪天賣出最高?
我們按題目中的示例 1舉例,價格陣列為[7,1,5,3,6,4]。
比如我在第一天買入,在哪天賣出最高呢?我們可以得到這樣一張表(不能交易標記X):
買入時間 | 買入價格 | 單天價格 | 7 | 1 | 5 | 3 | 6 | 4 |
---|---|---|---|---|---|---|---|---|
— | — | 賣出時間 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
第一天 | 7 | 獲利程度 | X | -6 | -2 | -4 | -1 | -3 |
填表原因如下:
- 即因為你是第一天買入的,所以第一天不能交易,所以填X
- 我們第一天買入的,買入價格為7,第二天值1,所以我們賣出則賺了-6.
- 我們第一天買入的,買入價格為7,第三天值5,所以我們賣出則賺了-2.
- 我們第一天買入的,買入價格為7,第四天值3,所以我們賣出則賺了-4.
- 我們第一天買入的,買入價格為7,第五天值6,所以我們賣出則賺了-1.
- 我們第一天買入的,買入價格為7,第六天值4,所以我們賣出則賺了-3.
有人說這個不是暴力解法麼?額,這道題的動態規劃的製表過程確實有點像。
那麼根據以上規則,如果我們是第二天買入的話,這個表格是不是就是這樣的。
買入時間 | 買入價格 | 單天價格 | 7 | 1 | 5 | 3 | 6 | 4 |
---|---|---|---|---|---|---|---|---|
— | — | 賣出時間 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
第二天 | 1 | 獲利程度 | X | X | 4 | 2 | 5 | 3 |
那麼我合併這兩張表是不是就是這樣的:
買入時間 | 買入價格 | 單天價格 | 7 | 1 | 5 | 3 | 6 | 4 |
---|---|---|---|---|---|---|---|---|
— | — | 賣出時間 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
第一天 | 7 | 獲利程度 | X | -6 | -2 | -4 | -1 | -3 |
第二天 | 1 | 獲利程度 | X | X | 4 | 2 | 5 | 3 |
那麼我們把這張表弄完整,即把第三天到第五天的買入,那是不是就是這樣的。
買入時間 | 買入價格 | 單天價格 | 7 | 1 | 5 | 3 | 6 | 4 |
---|---|---|---|---|---|---|---|---|
— | — | 賣出時間 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
第一天 | 7 | 獲利程度 | X | -6 | -2 | -4 | -1 | -3 |
第二天 | 1 | 獲利程度 | X | X | 4 | 2 | 5 | 3 |
第三天 | 5 | 獲利程度 | X | X | X | -2 | 1 | -1 |
第四天 | 3 | 獲利程度 | X | X | X | X | 3 | 1 |
第五天 | 6 | 獲利程度 | X | X | X | X | X | -2 |
第六天 | 4 | 獲利程度 | X | X | X | X | X | X |
因為第六天買了的話,已經是最後一天了,所以沒辦法賣了,所以全是X。
第二步:根據表中結果進行求解
上一步,已經把如果是某天買的,每天的獲利情況列了一遍,就是拆解成了單一問題。
那麼看最大利潤,很容易就能看出來了,就看那個數字最大就好了。
買入時間 | 買入價格 | 單天價格 | 7 | 1 | 5 | 3 | 6 | 4 |
---|---|---|---|---|---|---|---|---|
— | — | 賣出時間 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
第一天 | 7 | 獲利程度 | X | -6 | -2 | -4 | -1 | -3 |
第二天 | 1 | 獲利程度 | X | X | 4 | 2 | 5⃣️ | 3 |
第三天 | 5 | 獲利程度 | X | X | X | -2 | 1 | -1 |
第四天 | 3 | 獲利程度 | X | X | X | X | 3 | 1 |
第五天 | 6 | 獲利程度 | X | X | X | X | X | -2 |
第六天 | 4 | 獲利程度 | X | X | X | X | X | X |
沒錯emoji的5的就是,那麼我們只要簡單的遍歷一下這個表是不是就能把結果取出來了?嗯,對的
最後
但一般來說動態規劃的題目不是這麼簡單的,通常在製表的時候會涉及一個疊加問題,在根據表計算結果一般也不是簡單的遍歷就能完成的,但理解動態規劃思想應該是沒什麼問題了。
個人認為動態規劃雖然效能不強,但是能把問題變得很直觀,讓人更簡單的解決問題。同時演算法的雜合度不高,很方便使用分散式為問題的每個單一問題進行求解。