1. 簡單類比
假如我們要求全國人數,那么我們只要知道各個省的人數,然后將各個省的人數相加即可,要想知道各個省的人數,只要將這個省下面所有的市人數相加即可,同樣,如果想要知道各個市的人數,只要知道下面所有縣的人數即可。
這就是動態規劃的核心思想:將原問題拆解為更小的子問題,求解每個子問題的最優解后,通過組合子問題的解得到原問題的解
類比邏輯 | 動態規劃核心概念 | 關鍵說明 |
---|---|---|
全國人數(最終目標) | 原問題(Original Problem) | 需要求解的最終復雜問題。 |
省 / 市 / 縣人數 | 子問題(Subproblems) | 原問題拆解出的、規模更小的同類問題(子問題需與原問題 “同結構”,如都是 “統計某區域人數”)。 |
省 = 市相加 / 市 = 縣相加 | 狀態轉移方程(State Transition) | 子問題的解如何組合成父問題的解(你的例子中是 “求和”,DP 中可能是 “取最大 / 最小 / 累加” 等)。 |
縣人數(最底層) | 邊界條件(Base Case) | 無需再拆解的最小子問題,有直接已知的解(如 “某縣人數 = 統計局直接公布的數據”,無需再拆到鄉鎮)。 |
2. “分治” 與 “最優子結構”
如果我們有很多種解決方案,但是我們想要找出最優的方案。一種方法就是,我們將所有的方案劃分成不同的集合,這些集合之間互不重疊,結合內部也沒有重合的方案,此時我們只要求解出各個集合的最優方案,然后從最優方案中選出最優即可。
要確保通過 “劃分集合→找集合最優→選最終最優” 能得到全局最優解,需滿足以下兩點,這是避免 “漏掉最優方案” 或 “選不出真最優” 的核心:
- 前提 1:集合的 “完全覆蓋性”
劃分的所有集合必須覆蓋全部可能的解決方案,不能有遺漏。
例如:若要選 “從家到公司的最優路線”,若只劃分 “走地鐵” 和 “騎自行車” 的集合,卻漏掉 “坐公交” 的集合,可能恰好 “坐公交” 是耗時最短的最優方案,最終結果就會出錯。 - 前提 2:“最優子結構” 成立
每個集合的 “局部最優方案”,必須是支撐 “全局最優方案” 的候選。換句話說,全局最優方案一定來自某個集合的局部最優方案,不會存在 “某個集合的非最優方案,比所有集合的最優方案更好” 的情況。
例如:若劃分 “從家到公司走東邊” 和 “走西邊” 兩個集合,若 “東邊集合的最優路線(20 分鐘)” 和 “西邊集合的最優路線(25 分鐘)”,則全局最優一定是東邊的 20 分鐘,不會存在 “東邊集合里某條 22 分鐘的路線,比西邊最優還快” 的矛盾 —— 這就是最優子結構的體現。
你的通用框架 | 動態規劃(DP)的補充細節 | 貪心算法的補充細節 |
---|---|---|
劃分互不重疊的方案集合 | 劃分的 “集合” 對應 “子問題”,且子問題存在重疊性(需存儲解避免重復計算) | 劃分的 “集合” 對應 “每一步的選擇”,且需滿足貪心選擇性質(每步選局部最優就能推全局最優) |
求每個集合的最優方案 | 通過 “狀態轉移方程” 計算子問題最優解(依賴更小的子問題) | 直接選擇當前集合的 “局部最優”(無需依賴后續子問題,一步到位) |
從局部最優中選全局最優 | 最終問題的解就是最頂層子問題的最優解 | 所有步驟的局部最優累加 / 組合,直接得到全局最優 |
3. 青蛙跳臺階
假設你正在爬樓梯。需要 n 階你才能到達樓頂。
每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
我們想要知道n階的方案數,只要知道n-1階的方案數和n-2階的方案數即可。假如n=5
- 跳到第4階的方案集合
1->2->3->4->5 1->2->4->5 2->4->5 2->3->4->5
- 跳到第3階的方案集合
1->2->3->5 2->3->5 1->3->5
總之要想跳到第5階,那么一定是從第3階或者第4階過來的,方案數也就是這兩種集合方案數的和。我不知道跳到第4的階的方案是什么,也不知道跳到第3階的方案是什么,但是按照第3階和第4階,我可以將跳到第5階所有的方案分成不重合的兩個集合,只要求出這兩個集合的方案數,然后相加就是最后的方案數。
這兩個集合把所有的方案不重不漏的劃分開來,符合無重疊性和完全覆蓋性。第一個集合的所有方案,最后一步必然經過第4階臺階,第二個集合的所有方案,最后一步必然經過第3階臺階。
4. 集合的唯一劃分標準
這里有一個困惑,集合一中不是也會有方案經過3嗎,為什么不會與集合二重疊呢?
兩個集合的劃分標準是 “最后一步的來源”,而非 “方案是否經過某個中間臺階”,因此即使集合一的方案經過 3 階,也不會與集合二重疊
- 集合一(來自 n-1 階):所有方案的最后一步是 “從第 4 階跳 1 階到第 5 階”(無論這個方案在到達 4 階之前,是否經過 3 階、2 階等)。
例:方案 “1→2→3→4→5”,最后一步是 “4→5”,屬于集合一;哪怕方案是 “1→3→4→5”,最后一步仍是 “4→5”,也屬于集合一。 - 集合二(來自 n-2 階):所有方案的最后一步是 “從第 3 階跳 2 階到第 5 階”(無論這個方案在到達 3 階之前,是否經過 2 階、1 階等)。
例:方案 “1→2→3→5”,最后一步是 “3→5”,屬于集合二;哪怕方案是 “1→3→5”,最后一步仍是 “3→5”,也屬于集合二。
一個方案的 “最后一步” 是唯一的、不可拆分的,這是避免重疊的核心:
- 集合一的方案,無論中間是否經過 3 階,其 “最后一步” 必然是 “4→5”(跳 1 階);
- 集合二的方案,無論中間是否經過其他臺階,其 “最后一步” 必然是 “3→5”(跳 2 階)。
這就像 “無論你之前走了哪條路,最后一步是‘邁左腳進門’還是‘邁右腳進門’,是完全不同的兩個動作”—— 不會存在一個方案,“最后一步既從 4 階跳 1 階,又從 3 階跳 2 階”,因此兩個集合絕對無重疊。
5. 01背包問題
有N件物品和一個最多能被重量為W 的背包。第i件物品的重量是weight[i],得到的價值是value[i] 。每件物品只能用一次,求解將哪些物品裝入背包里物品價值總和最大。
比如有3個物品,a,b,c,d每個物品的價值為10, 15,20,30,重量分別為1,2,2,3,背包的重量為4
我們有2N2^N2N中選擇,也就有2N2^N2N中方案,我們從這些方案中選擇一個最優的方案,能裝下的價值最大。
如果我們能將所有的方案分組,然后獲取每個組內的最大價值,那么最大的那個價值就是最終的價值。一種簡單的劃分就是我們是否選擇某個物品,例如我們是否選擇a
- 選擇a的方案
a a, b a, c a, d a, b, c a, b, d a, c ,d
- 不選擇a的方案
b b, c b, d c c,d d
可以看到兩種方案完全沒有重合,因為集合一全部的方案都包含a,集合二所有的方案都不包含a,通過這種集合劃分方式,我們得到了所有的方案,并且兩個集合中沒有重復的方案。對于集合一和集合二,我們可以再次通過是否選擇b劃分為更小的集合。
子問題重疊
你的類比中,子問題(省、市、縣)是 “互不重疊” 的(一個縣只屬于一個市,一個市只屬于一個省),而動態規劃的典型場景中,子問題往往是 “重疊” 的(即不同父問題可能依賴同一個子問題的解)。