long time ago, 當我剛看到動態規劃這個響亮的大名時,瞬間陷入了沉思,腦中浮想聯翩,揣摩著這個演算法應該很帶感。
查了一下維基百科(不建議你閱讀)
動態規劃在尋找有很多重疊子問題的情況的最佳解時有效。它將問題重新組合成子問題。為了避免多次解決這些子問題,它們的結果都逐漸被計算並被儲存,從簡單的問題直到整個問題都被解決。因此,動態規劃儲存遞迴時的結果,因而不會在解決同樣的問題時花費時間。看了上面這一段之後,我完全懵逼了。動態規劃只能應用於有最佳子結構的問題。最佳子結構的意思是區域性最佳解能決定全域最佳解(對有些問題這個要求並不能完全滿足,故有時需要引入一定的近似)。簡單地說,問題能夠分解成子問題來解決。
然後找了乙個入門動態規劃的簡單例子(斐波那契數列),看懂後,再看看這四個響亮的大字"動態規劃",我更加混亂了。
當查了許多資料,確認自己最終理解後,再看這四個大字"動態規劃",我。。。。
說好的動態呢?這明明是高中數列題的魔改版好嗎?這是誰取的名字,我真看不出這演算法**動態了!這望文生義害人不淺啊。如果我來命名,可能會取:分步規劃,分步儲存法, 遞推儲存法,數列遞推法,狀態轉移法.....但我就是想不出動態規劃啊。
所以,入門動態規劃第一條:切忌望文生義,切忌用名字反推算法!
古語有云:talk is cheap,show me the code。接下來用3個例子印證上面的思想,例子均用python3(不懂沒關係看備註)。例子是從簡單,困難到地獄級別的題目。
斐波那契數列(簡單)
斐波那契數列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……它遵循這樣的規律:當前值為前兩個值的和。
那麼第 個值為多少?
2. 動態規劃
# 狀態轉移函式(目標1)
def f(pattern, i, string, j, results):
# 當前是星號
if pattern[i] == '*':
m_ij = pattern[i - 1] == string[j] or pattern[i - 1] == '.'
r = results[i - 2][j] | results[i - 1][j] | results[i][j - 1] & m_ij
# 當前不是星號
else:
m_ij = pattern[i] == string[j] or pattern[i] == '.'
r = results[i - 1][j - 1] & m_ij
return r
# 主匹配函式
def is_match(string, pattern):
# 初始化二維陣列(目標2)
len_string = len(string) + 1 # 給二維陣列加哨兵,所以+1
len_pattern = len(pattern) + 1
results = [[false] * len_string for i in range(len_pattern)]
results[0][0] = true
pattern = '_' + pattern # 相容哨兵
string = '_' + string
# 異常處理
if len_pattern == len_string == 1:
return true
if len_pattern == 1:
return false
if pattern[0] == '*':
return false
# 外迴圈遍歷pattern(目標3)
for i in range(1, len_pattern):
# 這裡是哨兵處理相關(與星號的情況1相關)
if pattern[i] == '*':
results[i][0] = results[i - 2][0]
# 內迴圈遍歷string(目標3)
for j in range(1, len_string):
# 狀態轉移函式(目標1),以及復用中間結果(目標2)
results[i][j] = f(pattern, i, string, j, results)
return results[-1][-1]
if __name__ == '__main__':
string = "aab"
pattern = "c*a*b"
result = is_match(string, pattern) # 結果為true
以上三個例子的空間複雜度是可以進一步優化的,這與動態規劃的思想關係不大,不做細說,讀者自己思考完成。
動態規劃與其說是乙個演算法,不如說是一種方**。該方**主要致力於將合適的問題拆分成三個子目標一一擊破:
建立狀態轉移方程
快取並復用以往結果
按順序從小往大算
完成該三個目標,你將所向披靡。
如何簡單的理解動態規劃?
動態規劃是一種類似於回溯的思想,他通過空間換時間的方法提高效率,一般用於求最優解問題。在上面我說道,動態規劃其實和回溯很類似。回溯思想在前面的文章我提到過,回溯是一種窮舉可能的方法。動態規劃則是通過額外的空間將回溯的遞迴樹中不需要的可能提前剪除達到提高效率的目的。我們來回憶一下回溯能解決的問題的特徵...
理解動態規劃
看了演算法導論上對動態規劃的講解,覺得自己對動態規劃的理解又進了一步,之前在讀到 演算法之道 相關章節時就有這感覺,但是仍然不敢說自己已經完全掌握了動態規劃,只是比以前又透徹了一些,說說自己新的理解,其實就是複述一下演算法導論上的內容而已。裝配線排程問題 乙個產品要經過n道工序,有兩條裝配鏈提供著n...
理解動態規劃
通過了解契波那契數列學習動態規劃 問題 斐波那契數列為1 1 2 3 5 8 13 21 34 寫乙個函式,輸入n,求斐波那契 fibonacci 數列的第n項。遞迴方法 includeusing namespace std int dfs int x int main return a x int...