1. 程式人生 > 其它 >學習筆記——線性動態規劃

學習筆記——線性動態規劃

線性動態規劃是OIer學習DP的基礎,只有學好了線性DP,才能更好的理解之後學習的揹包、區間DP、樹形DP等一系列難題,那麼廢話不多說,開始我們的學習之旅吧!

1.DP的概念

1.${\color{Red}\colorbox{White}{子問題重疊性}}$:DP演算法把原問題視作若干個重疊子問題的逐層遞進,每個子問題的求解過程都構成一個“階段”。在完成前一個階段的計算後,DP才會執行下一階段的計算。

2.${\color{Red}\colorbox{White}{無後效性}}$:為了保證DP中每一階段的計算能夠按順序、不重複的進行,DP要求已經求解的子問題不受後續階段的影響

我們身為OIer自然要更加重視知識的關聯性。運用類比的思想,我們可以發現DP與圖論這兩個看似毫不相干的兩個知識竟然有著千絲萬縷的聯絡。

${\color{Blue}\colorbox{White}{無後效性的另一種理解}}$:${\color{Black}\colorbox{Yellow}{DP對狀態空間的遍歷}}$構成了一張${\color{Black}\colorbox{Yellow}{有向無環圖(簡稱DAG)}}$,遍歷順序就是該有向無環圖的一個${\color{Black}\colorbox{Yellow}{拓撲序}}$,該DAG中節點對應問題中的狀態,圖中的邊對應狀態之間的轉移轉移的選取對應DP中的決策

3.${\color{Red}\colorbox{White}{最優子結構性質}}$:

(1).${\color{Blue}\colorbox{White}{狹義理解}}$:下一階段的最優解能夠由前面各階段子問題的最優解匯出

(2).${\color{Blue}\colorbox{White}{廣義理解}}$:DP在階段計算完成時,只會在每個狀態上保留與最終解集相關的部分代表資訊,這些代表資訊應該具有可重複的求解過程,並能夠匯出後續階段的代表資訊

4.${\color{Red}\colorbox{White}{總結}}$

綜上所述,狀態、階段和決策是構成DP演算法的三要素,而子問題重疊性、無後效性和最優子結構性質是問題能用DP求解的三個基本條件。

2.線性DP的定義

如果一個DP演算法的狀態包含多個維度,但在每一個維度上都具有線性變化的階段,則該DP演算法稱為線性DP。

3.線性DP的核心思想

首先,我們可以先用自己的方式進行題意簡述

,把握題目大意,再想好每一個狀態的狀態表示階段劃分,並在此基礎上推出狀態轉移方程,最後確定DP的邊界條件及DP目標後就可以開始碼程式碼了。(這裡推薦畫一個表格,這樣更能一目瞭然地看出狀態轉移方程)。

舉個栗子(LIS問題--最長上升子序列)

題意簡述 給定一個長度為$N$的數列$A$,求數值單調遞增的子序列的最長長度
狀態表示 令$F[i]$表示以$A[i]$結尾的最長上升子序列的長度
階段劃分 子序列的結尾位置(數列$A$中的位置,從前到後)
轉移方程 $F[i]=\max(F[i],F[j]+1)(0 \leqslant j < i,A[j]<A[i]$)
邊界條件 $F[0]=0$
目標 $\max(F[i])(1 \leqslant i \leqslant N$)

4.線性DP的一些優化技巧

1.在設計DP的狀態轉移方程時,可以以如何計算出一個狀態
(直接)的形式給出,也可以考慮一個已知狀態應該更新那些後續階段的未知狀態(間接)。它們各有千秋,視具體題目選擇合適的方法更有利於解題。

2.滾動陣列真的好用:我們對於一些可能MLE的DP狀態進行分析,如果我們發現這一狀態的DP只與前幾狀態的DP有關,就可以只定義這個數+1個的該維度空間(舉個栗子:若i的狀態只與i-1的狀態有關,則對於i的維度可以只開到2的空間,迴圈使用0和1)。但是滾動陣列會覆蓋掉之前求的狀態,如果只用求最終狀態則建議使用滾動陣列進行優化,若要求每一個狀態的最優解,千萬別用!(不過這類題一般不會爆MLE,要不然就無解了)(滾動陣列具體用法參照T7我的程式碼)

3.在實現狀態轉移方程時,要注意觀察決策集合的範圍隨狀態的變化情況,若決策集合有規律的變化,就可以考慮維護一個新的變數記錄決策集合的當前資訊,避免重複掃描,把轉移的複雜度降低一個量級。(要栗子的話就是T2我用優化打了個N方DP過了NlogN的題(與資料水也有很大的關係))

別問為什麼沒有程式碼,DP這毒瘤玩意根本沒有模板

好了,最後我再扔幾道題就閃吧!

1.線性DP的入門水題(一篇比較了DP與貪心、搜尋、記搜複雜度的好題解)(碼了個10min競速程式碼)

通過這道水題,我知道了兩點:

(1).我的手速真的不行,碼程式碼碼不快(慢慢練吧)

(2).沒有立刻想到最優解(明顯從下傳到上比從上傳到下更快,因為前者只用輸出$ans[1][1]$,而後者要多一個$\Theta(n)$的迴圈求最大值),要多鍛鍊自己的思維水平。

2.棧+二分、樹狀陣列版二維偏序吊打DP的偽DP題(通俗易懂的棧+二分題解)(還不會的樹狀陣列版二維偏序)(DP思路+DP常數優化+我的程式碼)

3.正反兩遍最長上升子序列的水題(題解)(我的程式碼)

4.做兩遍線性DP的水題(題解)(我的程式碼)

5.四種牌分別處理的線性DP(有點揹包的味道)(講的不錯的題解)(我的程式碼)

6.第一個自己獨立做出的DP綠題(O(N)做法的題解)(二分做法的NlogN的題解)(我的程式碼)

7.滾動陣列+三維DP(講的很詳細的題解)(我的程式碼)

隨手取模真的很重要,不然100pts->60pts。以後遇到這類題也不用太慌,先明確自己設定的DP陣列每個維度都代表著什麼(最好在程式碼中的相應陣列下寫一點註釋),然後考慮未知的狀態可以由那些已經求出的狀態推出(可以在草稿紙上寫出DP方程式),明確思路後再碼程式碼。

8.構造新數列求最長不下降子序列+推新結論的噁心題(有動圖的良心題解)(碼了半天的程式碼(一個晚上+半個上午))

比較難的DP題真的就是在比耐心和細心。

問題一:本題運用正難則反的思想,對於暫時毫無思路的問題,可以考慮構造新數列進行求解(可以將題面簡化為一堆關係式,然後對其進行化簡,找出關係式中的共同點,在將其用一個新陣列儲存,可以極大的簡化程式碼、降低複雜度和輔助思考)。

那個推新結論是人能想到的嗎?

問題二:對於一些更難的問題,可以多畫圖輔助思考,排除一些矛盾的情況,找到一些類似的情形,然後轉化為DP的形式進行狀態轉移方程的推導。

9.第一道獨立做出的DP紫題(當然要寫一篇題解好好慶祝)

10.雙DP陣列+字首和優化的水題(講的不錯的題解(只看思路就可以打程式碼))(我的程式碼)

最後再挖兩個坑,以後再填吧!(畢竟現在不是刷黑題的時候)

1.學完高數再做的黑DP

2.打完豬國殺之後用耐心做的大模擬+DP