【數據結構和算法】常用基本算法彙總2 動態規劃

原文參考自 頭條 Java後端開發

動態規劃

一、基本概念

每次決策依賴於當前狀態,又隨即引起狀態的轉移,一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱爲動態規劃。

二、基本思想

基本思想:與分治法類似,也是將待求解的問題分解爲若干個子問題,按順序求解子問題,前一子問題的解,爲後一子問題的求解提供了有用的信息。在求解任一子問題時,列出各種可能的局部解,通過決策保留那些有可能達到最優的局部解,丟棄其他局部 解,依次解決各子問題,最後一個子問題就是初始問題的解。

與分治法最大的差別:適合於用動態規劃法求解的問題,經分解後得到的子問題往往不是互相獨立的(即下一個子階段的求解是建立在上一個子階段的解的基礎上,進行進一步的求解)。

三、適用的情況

(1)最優化原理:如果問題的最優解所包含的子問題的解也是最優的,就稱該問題具有最優子結構,即滿足最優化原理。

(2)無後效性:即某階段狀態一旦確定,就不受這個狀態以後決策的影響,也就是說,某狀態以後的過程不會影響以前的狀態,只與當前狀態有關。

(3)有重疊子問題:即子問題之間是不獨立的,一個子問題在下一階段決策中可能被多次使用到。(該性質並不是動態規劃適用的必要條件,但是如果沒有這條性質,動態規劃算法同其他算法相比就不具備優勢)

四、解題步驟

動態規劃所處理的問題是一個多階段決策問題,一般由初始狀態開始,通過對中間階段決策的選擇,達到結束狀態。這些決策形成了一個決策序列,同時確定了完成整個過程的一條活動路線(通常是求最優的活動路線)。動態規劃的設計都有着一定的模式,一般要經歷以下幾個步驟:

初始狀態→│決策1│→│決策2│→…→│決策n│→結束狀態

(1)劃分階段:按照問題的時間特徵,把問題分爲若干個階段,在劃分階段時,注意劃分後的階段一定要是有序的或者是可排序的,否則問題就無法求解。

(2)確定狀態和狀態變量:將問題發展到各個階段時所處於的各種客觀情況用不同的狀態表示出來,當然,狀態的選擇要滿足無後效性。

(3)確定決策並寫出狀態轉移方程:因爲決策和狀態轉移有着天然的聯繫,狀態轉移就是根據上一階段的狀態和決策來導出本階段的狀態。所以如果確定了決策,狀態轉移方程也就可寫出。但事實上常常是反過來做,根據相鄰兩個階段的狀態之間的關係來確定決策方法和狀態轉移方程。

(4)尋找邊界條件:給出的狀態轉移方程是一個遞推式,需要一個遞推的終止條件或邊界條件。

一般,只要解決問題的階段、狀態和狀態轉移決策確定了,就可以寫出狀態轉移方程。實際應用中可以按以下幾個簡化的步驟進行設計:

(1)分析最優解的性質,並刻畫其結構特徵。

(2)遞歸的定義最優解。

(3)以自底向上或自頂向下的記憶化方式(備忘錄法)計算出最優值。

(4)根據計算最優值時得到的信息,構造問題的最優解。

五、算法實現的說明

動態規劃的主要難點在於上面4個步驟的確定,一旦設計完成,實現部分就會非常簡單。使用動態規劃求解問題,最重要的就是確定動態規劃三要素:

(1)問題的階段

(2)每個階段的狀態

(3)從前一個階段轉化到後一個階段之間的遞推關係。

遞推關係必須是從次小的問題開始到較大的問題之間的轉化,從這個角度來說,動態規劃往往可以用遞歸程序來實現,不過因爲遞推可以充分利用前面保存的子問題的解來減少重複計算,所以對於大規模問題來說,有遞歸不可比擬的優勢,這也是動態規劃算法的核心之處。

確定了動態規劃的這三要素,整個求解過程就可以用一個最優決策表來描述,最優決策表是一個二維表,其中行表示決策的階段,列表示問題狀態,表格需要填寫的數據一般對應此問題的在某個階段某個狀態下的最優值(如最短路徑,最長公共子序列,最大價值等),填表的過程就是根據遞推關係,從1行1列開始,以行或者列優先的順序,依次填寫表格,最後根據整個表格的數據通過簡單的取捨或者運算求得問題的最優解。

f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}

六、經典例子

  • 孩子有多像爸爸--最長公共子序列:

c[i][j]表示Xi和Yi的最長公共子序列長度。

  • DNA基因鑑定--編輯距離:

d[i][j]表示Xi和Yi的編輯距離。

  • 長江一日遊--遊艇租賃:

這個題目要從規模d=3開始循環計算,爲什麼要這樣呢?

其實也蠻容易理解的,爲了求m[i][j] 要知道m[i][k]然後再求m[k][j],這裏要首先m[i][k]是最優解了,所以要一步一步先算出m[i][k]的最優解。

  • 快速計算--矩陣連乘:

這個題目和之前的題目類似,問題的規模從2開始循環遞增。

  • 切披薩--最優三角刨分:

這個題目的數學建模有點複雜,轉化成凸多邊形和鄰接矩陣就簡單多了。

還有,最優三角刨分就是要三角刨分。

m[i][j]表示凸多邊形從i-1頂點到j頂點的三角刨分的最優值(權值)

  • 石子合併:

Min[i][j]代表從第i堆石子到第j堆石子的最小花費

  • 大賣場購物車1--0-1揹包問題:

這個題目其實簡化了,每個物品只有1個,要麼放要麼不放。

c[i][j]表示前i個物品放入一個容量位j的購物車可以獲得的做大價值。

  • 快速定位--最優二叉搜索樹:

optimal binary search tree OBST

這個題目略難,其實難點就是在於建模。

最後寫兩個function,一個是optimal_BST,一個是construct_optimal_BST;

最後還有四邊形不等式的優化,O(n^2)。四邊形不等式優化講解(詳解)

七、算法祕籍

陳小玉老師在趣學算法裏做了很好的總結:

動態規劃求解最優化問題時需要考慮兩個性質:最優子結構和子問題重疊,只要滿足最優子結構性質就可以使用動態規劃,如果還具有子問題重疊,則更能彰顯動態規劃的優勢。判斷可以使用動態規劃後,就可以分析其最優子結構特徵,找到原問題和子問題的關係,從而得到最優解遞歸式。然後按照最優解遞歸式自底向上求解,採用備忘機制(查表法)有效解決子問題重疊,重複的子問題不需要重複求解,只需查表即可。

動態規劃的關鍵:

(1) 最優子結構判定

a. 作出一個選擇;

b. 假定已經知道了哪種選擇是最優的;

例如矩陣連乘問題,我們假設已經知道在第k個矩陣加括號是最優的,即(AiAi+1…Ak)(Ak+1Ak+2…Aj)。

c. 最優選擇後會產生哪些子問題;

例如矩陣連乘問題,我們作出最優選擇後產生兩個子問題:(AiAi+1…Ak),(Ak+1Ak+2…Aj)。

d. 證明原問題的最優解包含其子問題的最優解。

通常使用“剪切—粘貼”反證法。證明如果原問題的解是最優解,那麼子問題的解也是最優解。反證:假定子問題的解不是最優解,那麼就可以將它“剪切”掉,把最優解“粘貼”進去,從而得到一個比原問題最優解更優的解,這與前提原問題的解是最優解矛盾。得證。

例如:矩陣連乘問題,c=a+b+d,我們只需要證明如果c是最優的,則a和b一定是最優的(即原問題的最優解包含子問題的最優解)。

反證法:如果a不是最優的,(AiAi+1…Ak) 存在一個最優解aˊ,aˊ<a,那麼,aˊ+b+d<c,這與假設c是最優的矛盾,因此如果c是最優的,則a一定是最優的。同理可證b也是最優的。因此如果c是最優的,則a和b一定是最優的。因此,矩陣連乘問題具有最優子結構性質。

(2) 如何得到最優解遞歸式

a.分析原問題最優解和子問題最優解的關係;

例如矩陣連乘問題,我們假設已經知道在第k個矩陣加括號是最優的,即(AiAi+1…Ak)(Ak+1Ak+2…Aj)。作出最優選擇後產生兩個子問題:(AiAi+1…Ak),(Ak+1Ak+2…Aj)。如果我們用m[i][j]表示AiAi+1…Aj矩陣連乘的最優解,那麼兩個子問題:(AiAi+1…Ak),(Ak+1Ak+2…Aj)對應的最優解分別是m[i][k],m[k+1][j]。剩下的只需要考察(AiAi+1…Ak)和(Ak+1Ak+2…Aj)的結果矩陣相乘的乘法次數了。兩個結果矩陣相乘的乘法次數是pi*pk+1*qj。

因此,原問題最優解和子問題最優解的關係:m[i][j]= m[i][k]+m[k+1][j]+ pi*pk+1*qj

b.考察有多少種選擇;

實質上,我們並不知道哪種選擇是最優的,因此就需要考察有多少種選擇,然後從這些選擇中找到最優解。

例如矩陣連乘問題,加括號的位置k(AiAi+1…Ak)(Ak+1Ak+2…Aj),k的取值範圍是{i,i+1,…,j-1},即i≤k<j,那麼我們考察每一種選擇,找到最優值。

c.得到最優解遞歸式。

例如矩陣連乘問題,m[i][j]表示AiAi+1…Aj矩陣連乘的最優解,根據最優解和子問題最優解的關係,並考察所有的選擇,找到最小值就是我們要的最優解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章