這篇來入門競程中最精華、最熱門的主題,動態規劃
DP 代表動態規劃 (Dynamic Programming),其中,Programming 的意思在這邊並不是我們所熟知到「寫程式」而是「規劃」,意旨規劃空間來解問題的技巧。值得注意的是,動態規劃不是一種演算法,而是一種設計演算法的「技巧」
動態規劃 (DP) 是競程的所有題型當中最熱門的一種。如果你特別會二分搜、貪心法、暴搜,那頂多只是資工系必修的演算法有學好而已。如果你特別會圖論、數論、排列組合,那你頂多只是組合數學比別人好而已。但是,如果你特別會 DP,那就可以宣稱競程實力超強。沒錯,DP 就是如此地有標誌性,打競程不被 DP 荼毒是不可能的事,就像沒吃過魯肉飯就不算到過台灣,是一樣的道理
此外,根據我學長的經驗,在 ICPC 台灣站 DP 出現的頻率大約是 題場 (整場約 題)、高中的 APCS 也差不多每場會出現 題 (整場 題),不練真的很虧
某種程度上,DP 是最考驗把問題抽象化的關鍵,所以當然很難,難到大多數人都會說 DP 是在通靈,若沒靈感很難把解題方法想出來。事實上也沒有一套方法或可以去設計 DP 的演算法,有時甚至連要不要用都不知道,但還是有些特徵可以去判斷是不是要用 DP
廣義上,只要是有使用記憶體存子問題答案的都算是 DP,所以像是最短路徑的那一坨演算法、flow 的那一大坨模板都算是 DP。但,因為那些東西通常不太會去改他們的邏輯,而且套用計算理論那個說法,我們大多時候只是把問題 reduce 到最短路徑或是 flow 的問題上再用相對應的 solver 去解他們,不是重新地去設計一個新的演算法,所以不算競程裡的 DP 問題。在這我們只討論使用 DP 技巧設計演算法的問題
這邊套用我的演算法老師的說法「動態規劃法就是一種有技巧的暴力法」。嗯怎麼說呢,這句話感覺是這樣,但好像不是這樣。在動態規劃法,我們會把所有的可能都算過一遍,但是為了避免重複算到,我們定義了一種方法將已算過的可能與未算過的可能扯上關係。如此定義,每一種可能就會有先後之分,我們在計算時就需要照著這個順序依序運算,才能得到答案
如果看過這篇應該知道我在鋪成什麼吧!!
可以用 DP 解的問題還有一些特徵 :
讀過圖靈機可解不可解問題應該會很有感吧! 這邊就是用 Turing reduction 的蓋念
這些性質也可以從數學中的遞迴看出來。舉例來說,若要求費氏數列的第 項 ,那我們可以 :
這裡可能你會覺得是廢話,但別忘了在計算機中,每一個步驟都是很嚴謹的,不可以跳步驟,像是你在設計 DP 演算法時就有可能忽略其實我們並不知道 跟 的答案,若隨便把兩者加起來,就會壞掉。這種邏輯盲區,就有可能讓你設計出會 WA 的演算法
任何 DP 都可以有向無環狀態圖來呈現。照前面的說法,我們可以 :
這時你應該要很意外地發現這跟有向圖 (圖論的那個) 的定義一模一樣,所以我們定義 :
如此一來,圖就能被畫出來。我們可以讀一次下面這張圖。狀態 可以轉移到狀態 。若我們可以找出 的答案,就可以找出 的答案
在下面這張圖中,狀態 跟 可以轉移到狀態 。若我們能算出 跟 ,那就可以算出 。具體例子可以看底下費氏數列的例子
要注意這種有向圖不存在環 (cycle)。可以用反證法說明一下,若一個狀態被轉移幾次後會回到自己,那就代表一個問題的好幾個子問題後會變成自己,這樣在計算上就會進入無限迴圈,因此這種狀況不可以存在
從另一個角度看,若我們定義解掉問題走訪節點,那麼 DP 就是在使用拓樸排序的走訪方式遍歷狀態圖!!
以上都只是概念,但實作 DP 的話會有兩種不同的方法。舉一個費氏數列的例子,由於我們的轉移式已經定義好的,長這樣 ,所以可以直接得到以下的狀態圖
這在基礎就講過了,怎麼寫出來的就先不討論
有意思的是,這種寫法就是暴力法窮舉。舉 為例,我們可以得到以下這樣的樹狀圖
不難發現,有許多子樹都是相同的,因此這種寫法也非常耗時
注意 : 此樹狀圖不等於狀態圖!!
此作法是從我們想求的答案出發,一路拆解成子問題,直到子問題有解再一路往回算。過程中,我們把算過的子問題答案用 陣列記錄起來。這種手法我們叫做 Memoization
會發現這種方法類似 DFS。其中,我們只有在節點成為黑點時,才會去解他,這剛好對應到 (部分的) 拓樸排序演算法的性質,所以這是一個解問題的過程就是拓樸排序。此外,因走訪過的狀態會被 if (dp[n]) return dp[n];
這行擋下來,因此不會再次走訪
註 : Memoization 跟 Memorization 不一樣!! Memoization 是有點像做小抄的感覺把東西記起來,Memorization 就只是單純的記憶而已
Bottom-up 顧名思義,就是先從子問題出發,先解決子問題再解決更大的問題。在費氏數列中,恰好子問題就是數字比較小的項,所以這樣從小到大解問題是合理的
照這走訪順序,不難發現解問題的順序其實也是拓樸排序,而且跟 top-down 一模一樣
有人認為 bottom-up 就是填表法,這種觀點因為沒有觸及到這方法的核心,所以感覺是這樣但也好像不是這樣
海大競程 - Dynamic Programming 動態規劃
海大競程 - Classic DP
師大演算法筆記 - dynamic programming
geeksforgeeks - Dynamic Programming (DP) and Directed Acyclic Graphs (DAG)
ShanC 程式競賽筆記
作者: ShanC
更新: 2025/7/10