動態規劃(1):基本思路以及步驟
基本思想
動態規劃是針對一類求最優解的問題的演算法, 其核心是將一個問題分解成為若干個子問題(這裡對應下文的子問題使用條件), 部分類似於分治的思想(不懂得可以參考歸併排序), 通過求每一次的最優決策, 來得到一個最優解。在這裡最重要的就是子問題的思想。
另一種理解方式數是DP的核心是加法原理(下文的人人為我形遞迴)和乘法原理(下文的我為人人形遞迴), 通過這兩個原理, 在當狀態的前有限多個狀態中找到最優解來求得當前狀態, 而對於前一個或者前幾個狀態採用同樣的方法知道求出邊界狀態,這種方法最噁心的就是邊界狀態
在學會搜尋之後, 最簡單入門DP的方法就是記憶話搜尋, 但是後來會發現大多數DP題目因為時間和記憶體的限制並不能使用遞迴(函式的遞迴呼叫會佔用棧記憶體, 另外函式的遞迴呼叫也將佔用大量的時間)
子問題解決法的適用條件
需同時滿足一下三點:
1.具有相同的子問題:我們必須保證我們分割成的子問題也能按照相同的方法分割成更小的自問題, 並這些自問題的最終分割情況是可以解決的。
2.滿足最優子結構:就是一個決策的子決策也是最優的
3.無後效性:這是DP中最重要的一點, 他要求每個子問題的決策不能對後面其他未解決的問題產影響, 如果產生就無法保證決策的最優性, 這就是無後效性。往往需要我們找到一個合適的狀態。
··這條非常重要
··這條非常重要
··這條非常重要
舉個例子:我們在LIS中先求前M項的LIS, 然後依次向後求, 直到str.len, 這就是因為我們在求前M項的過程中, 對(M + 1)→(str.len)並無影響。消除後效性
常用的解題步驟
前兩天剛剛被大牛虐過DP, 姑妄言之:
第一步:確定子問題。 在這一步重點是分析那些變數是隨著問題規模的變小而變小的, 那些變數與問題的規模無關。
第二步:確定狀態:根據上面找到的子問題來給你分割的子問題限定狀態
第三步:推到出狀態轉移方程:這裡要注意你的狀態轉移方程是不是滿足所有的條件, 注意不要遺漏。
第四步:確定邊界條件:先根據題目的限制條件來確定題目中給出的邊界條件是否能直接推匯出, 如果不行也可以嘗試從邊界條件反推(舉個例子:a(n)→a(2)有遞推關係, 但是a(2)→a(1)不符合上述遞推關係, 我們就可以考慮用a(1)來倒推出a(2), 然後將遞推的終點設定為a(2));
第五步:確定實現方式
第六步:確定優化方法:很多時候你會發現走到這裡步的時候你需要返回第1步重來。首先考慮降維問題(優化記憶體), 優先佇列、四邊形不等式(優化時間)等等。
常用方法
以下是方法, 但是不要侷限在這裡, 方法是無限的
(1)模型匹配法:熟練記憶並且理解LIS、LCS、01揹包、完全揹包、區間模型、樹狀模型。基本就是將原模型加以變化後加以套用
(2)三要素法:
·······先確定階段:如數塔問題, 先確定當前選的是第幾層。
·······先確定狀態:這是最常用的絕大多數的DP都是這麼做的。
·······先確定決策:揹包問題(選還是不選第i種物品)
這是個經驗問題, 然而我還沒有這種經驗。
(3)尋找規律法:從小的狀態開始推, 耐心找規律, 或者可以在本地暴力打表, 暴力出奇跡, 不打2、3頁那都不叫打表,幾年省賽徹底領悟了,不想說啥。
(4)邊界條件法: 一般邊界時容易匯出狀態關係的地方
(5)增加約束條件法:這條就對應著上文的消除後效性,以後的部落格會有例題