關於動態規劃的講解有很多材料,這裏只是按照我自己的理解來表述動態規劃。可能並不詳細,也不一定完全準確。這裏主要通過兩個例子LIS和最小編輯距離進一步加深對於動態規劃的直觀理解。
1. 動態規劃入門理解
動態規劃方法是把問題向前分解,想要解決一個問題,需要先解決這個問題的子問題,那麼要解決子問題,又需要解決子問題的子問題。通過先解決最小的子問題,再不斷的解決更上一層的問題,那麼就可以解決最終的問題。
欲用動態規劃解決問題,首先需要對問題進行定義,利用數學語言描述問題。然後再對問題進行求解,求解的過程中是先求解小問題,再利用小問題的解去求解大問題。這是動態規劃中常用的流程。
對問題的定義和求解小問題再求解大問題,其實就是定義狀態和解決狀態轉移。下面就用兩個例子(LIS和最小編輯距離)來解釋如何定義問題的狀態以及推導狀態轉移公式。
2. LIS(最長遞增子序列)
LIS問題是求一個長序列的子序列,使的這個子序列是遞增的,且是所有遞增的子序列中最長的。比如對於序列2 5 4 9 10 6 7 8 11,最長遞增子序列有兩個,2 5 6 7 8 11和 2 4 6 7 8 11。最長遞增子序列問題有時候是隻需要知道長度即可,對於前面的序列,最長遞增子序列的長度是唯一的,即=6。
當然LIS問題是可以用動態規劃的思想來解決的。那麼首先思考如何定義問題的狀態。最容易想到的定義如下:
: 表示序列前k項的最長子序列
對於這個定義,很明顯,子問題就是 , 等,分別表示前 和前 項的最長子序列。對問題進行定義後,是否合適呢?能否通過該定義推導出狀態轉移呢?如果對於每一項都求最長的子序列,並且狀態轉移使用 項和其它的最長子序列相比較的方式,選擇是否把 項加入。
對於序列2 5 4 9 10 6 7 8 11
第一項:2
第二項:2 5
第三項:2 5
第四項:2 5 9
第五項:2 5 9 10
第六項:2 5 9 10
第七項:2 5 9 10
….
從這裏就可以看出來,該種狀態的定義和狀態轉移的定義是有問題的。6 7 8 這幾項不能發揮作用了。
- 因此這裏選擇另外一種定義:
: 表示以第k項結尾的最長子序列
- 那麼求出所有以1,2,3,4,5…,N項結尾的最長子序列後,選擇最長的作爲輸出即可。那麼對於這種狀態的定義,狀態的轉移是什麼樣子的呢?
也就是,求解序列[ , , , …, ]的最長遞增子序列,可以通過[ , , , … ]序列、[ , , , … ]、[ , ]和[ ]的最長遞增子序列得到。要求長度爲k的序列的[ , , , …, ]最長遞增子序列,需要先求出序列[ , , , …, ]中以各元素( , , )作爲最大元素的最長遞增序列,然後把所有這些遞增序列與 比較,如果序列的末尾元素比 要小,則將元素 加入這個遞增子序列,如果序列末尾的元素比 大,則取 ,那麼現在所有的序列都是以 結尾的,取出序列長度最長的作爲以第k項結尾的最長遞增子序列。(這裏可能會出現兩個序列長度一樣的情況,那麼可以隨機選一個,或者是選擇第一個)。
- 對於前面的序列2 5 4 9 10 6 7 8 11,求解順序如下(存在兩個一樣長的序列時,選擇第一個):
第一項:2
第二項:2 5
第三項:2 4
第四項:2 5 9
第五項: 2 4 9 10
第六項:2 5 6
第七項:2 5 6 7
第八想:2 5 6 7 8
第九項:2 5 6 7 8 11
另外,如果是求子序列,那麼是一邊計算,也是一邊需要保存各項的子序列的。如果是隻求子序列的長度,那麼只需要保存長度即可。
採用這種方式求解最長遞增子序列,時間複雜度爲 ,還有更快的,時間複雜度爲 的算法,這裏就不再解釋了。
3. 最小編輯距離
- 最小編輯距離的問題,比較普遍,在平常的編碼過程中經常可能會遇到這個問題。最小編輯距離是指把字符串A轉換成字符串B所需要的最少操作數,這裏的操作包括刪除字、增加字、替換字三種操作。
比如小明讓小紅告訴小剛:“今天天氣真好啊”,而小紅卻告訴小剛:“今天正好呀哈”。 那麼就可以用最小編輯距離表示這兩句話的區別。這裏,增加“天”字,增加“氣”字,修改“正”爲“真”,修改“呀”爲”啊”,刪除“哈”字。那麼最小編輯距離爲5。
要用動態規劃的思想解決該問題,同樣需要考慮如何定義狀態和建立狀態轉移公式。
首先對問題進行定義:
: 表示從句子A的前i個字轉移到句子B的前j個字的最小編輯距離
狀態轉移公式如下(假設句子A有N個字符,而句子B有M個字符):
其中
更詳細的最小編輯距離算法的說明,可以參考博客https://blog.csdn.net/qq_34552886/article/details/72556242