【乾貨】動態規劃十問十答

九章演算法發表於2019-03-03

專欄 | 九章演算法
網址 | www.jiuzhang.com

問1 動態規劃是個什麼鳥蛋?

答:動態規劃是一種通過“大而化小”的思路解決問題的演算法。區別於一些固定形式的演算法,如二分法,寬度優先搜尋法,動態規劃沒有實際的步驟來規定第一步做什麼第二步做什麼。所以更加確切的說,動態規劃是一種解決問題的思想。這種思想的本質是,一個規模比較大的問題(假如用2-3個引數可以表示),是通過規模比較小的若干問題的結果來得到的(通過取最大,取最小,或者加起來之類的運算)所以我們經常看到的動態規劃的核心——狀態轉移方程都長成這樣:

  • f[i][j] = f[i - 1][j] + f[i][j - 1]
  • f[i] = max{f[j] if j < i and …} + 1
  • f[i][j] = f[0][j - 1] && judge(1,i) || f[1][j - 1] && judge(2,i) || …

問2 動態規劃面試考得多麼?

答:多。並且越來越多。隨著CS從業與求職者的增加,並伴隨大家都是“有備而來”的情況下,一般簡單的反轉連結串列之類的題目已經無法再在面試中堅挺了。因此在求職者人數與招聘名額的比例較大的情況下,公司會傾向於出更難的面試問題。而動態規劃就是一種比較具有難度,又比較“好出”的面試問題。相比其他的演算法與資料結構知識來說,貪心法分治法太難出題了,搜尋演算法往往需要耗費求職者過長的程式編寫時間一般也不傾向於出,二叉樹連結串列等問題題目並沒有那麼多,而且求職者也都會著重準備這一塊。因此動態規劃這一類的問題,便越來越多的出現在了面試中。

問3 動態規劃快在哪兒?

答:動態規劃一般來說是“高效”的代名詞,因為其解決的問題一般退而求其次的演算法只有搜尋了。以“數字三角形”一題為例子(數字三角形link),在“三角矩陣”中找一條從上到下的路徑,使得權值之和最小。如果使用暴力搜尋的演算法,那麼需求窮舉出2^(n-1)條路徑(n為三角形高度),而使用動態規劃的話,則時間複雜度降低到了n^2,完成了質的飛躍。那麼究竟為什麼這麼快呢?原因在於動態規劃演算法去掉了“無用和重複的運算”。在搜尋演算法中,假如從A->B有2條路徑,一條代價為10,另外一條代價為100,B->終點有1024條路徑。當我們選擇了代價為10的那條路徑走到B時,可以繼續往下走完1024條路徑到終點,但是在此之後,我們再從代價為100的路徑從A走到B時,我們可以發現此時無論如何走,都不可能有剛才從10的路徑走過來更好,所以這些計算是“無用”的計算,也可以說是“重複”的計算。這就是動態規劃之所以“快”的重要原因。

問4 學習動態規劃有什麼捷徑?

答:我們將動態規劃的常見型別分為如下幾種:

  • 矩陣型
  • 序列型
  • 雙序列型
  • 劃分型
  • 區間型
  • 揹包型
  • 狀態壓縮型
  • 樹型

其中,在技術面試中經常出現的是矩陣型,序列型和雙序列型。劃分型,區間型和揹包型偶爾出現。狀態壓縮和樹型基本不會出現(一般在演算法競賽中才會出現)。
每種型別都有著自己的題目特點和狀態的表示方法。以矩陣型動態規劃為例,一般題目會給你一個矩陣,告訴你有一個小人在上面走動,每次只能向右和向下走,然後問你比如有多少種方案從左上走到右下(不同的路徑link)。這種型別狀態表示的特點一般是使用座標作為狀態,如f[i][j]表示走到(i,j)這個位置的時候,一共有多少種方案。狀態的轉移則是考慮是從哪兒走到(i,j)這個座標的。而序列型的動態規劃,一般是告訴你一個序列;雙序列的動態規劃一般是告訴你兩個字串或者兩個序列。
將所做過的動態規劃問題按照這些類別進行歸類,分析狀態的表示方法和狀態轉移方程的構造方法在每種型別中的近似之處,會讓你更快的學會動態規劃。

問5 什麼樣的問題適合使用動態規劃?

答:可以使用動態規劃的問題一般都有一些特點可以遵循。如題目的問法一般是三種方式:

  1. 求最大值/最小值
  2. 求可不可行
  3. 求方案總數

如果你碰到一個問題,是問你這三個問題之一的,那麼有90%的概率是使用動態規劃來求解。
要重點說明的是,如果一個問題讓你求出“所有的”方案和結果,則肯定不是使用動態規劃。

問6 解決一個動態規劃問題的步驟是什麼?

答:首先根據“問5”判斷是否是動態規劃的問題,如果是,則嘗試將其按照“問4”進行分類,找到對應的類別和相似的問題。接著從下面的4個要素去逐步剖析解決這道題:

  1. 狀態是什麼
  2. 狀態轉移方程是什麼
  3. 狀態的初始值是什麼
  4. 問題要求的最後答案是什麼

每個步驟分析完成之後,就基本上解決了整道動態規劃的問題。

問7 怎樣優化動態規劃的時間?

答:一般來說,使用動態規劃求解的問題,時間上已經比暴力搜尋要優化很多了。但是仍然存在著一些可以優化的空間。通常來說,動態規劃的時間優化,有如下兩種常見的方式:

  1. 通過變換狀態優化
  2. 通過決策單調優化

對於通過變換狀態來優化的問題比較難,需要一些經驗和靈感。而對於決策單調的優化,則比較簡單,但適用範圍不廣,一般只適用於劃分型動態規劃當中,通常這個方法可以將複雜度降低一個數量級。

問8 怎樣優化動態規劃的空間?

答:動態規劃的空間優化只有一種方法,就是使用滾動陣列進行優化。以一個二維的動態規劃為例子。假如狀態轉移方程如下:f[i][j] = f[i - 1][j] + f[i][j - 1]。我們可以發現,第i層的狀態,已經和第i-2層的狀態沒有關係了,那麼這種情況下,用於儲存第i-2層的空間就可以被重複利用。方法非常簡單,把陣列的第一維對2取模就可以了:f[i % 2][j] = f[(i - 1) % 2][j] + f[i % 2][j-1]。這種方法通常可以將空間複雜度降低一個數量級。

問9 有什麼書籍和參考資料可以推薦麼?

著名的揹包九講:
揹包九講link
(也可以直接在網上搜尋揹包九講)

問10 有哪些動態規劃題目必須要練習的?

在LintCode上包含了30餘道動態規劃的練習題,都是從實際的面試問題中彙總的精選練習:
動態規劃練習題link


歡迎關注我的微信公眾號:九章演算法(ninechapter)。
精英程式設計師交流社群,定期釋出面試題、面試技巧、求職資訊等

【乾貨】動態規劃十問十答
九章演算法,IT教育領域的深耕者

相關文章