理解動態規劃

2021-05-23 23:13:07 字數 3058 閱讀 1042

看了演算法導論上對動態規劃的講解,覺得自己對動態規劃的理解又進了一步,之前在讀到《演算法之道》相關章節時就有這感覺,但是仍然不敢說自己已經完全掌握了動態規劃,只是比以前又透徹了一些,說說自己新的理解,其實就是複述一下演算法導論上的內容而已。

裝配線排程問題

乙個產品要經過n道工序,有兩條裝配鏈提供著n道工序,在任何一道工序i時產品都可以選擇在兩條線上的一條進行加工,在裝配線1上加工工序i的時間為a[i][1],裝配線2上類似。在同一條裝配線上前進不花時間,跳轉到另一條裝配線上需要一定的時間t[1][i](從裝配線1的工序i跳轉到裝配線2的工序i+1所花費的時間)和t[2][i](類似)。

問題是求解乙個產品最快完成所有工序所需的時間。

動態規劃解法:

我們用dp[i][j]存放在第j條裝配線完成第i到工序所用的最少時間,其中i=1...n,j=1,2。因為要麼是從同條裝配線的上一道工序而來,要麼是從另一條裝配線的上一道工序跳轉而來,而最少時間應該是這兩種情況裡較少的那種。注意應該認為dp[i-1][1]和dp[i-1][2]已經求解完畢,或者說不要關心它們,只要認為它們存放的是正確答案就可以了,為何可以如此,見後面。則有:

dp[i][1] = min(dp[i-1][1]+a[i][1],dp[i-1][2]+t[i-1][2]+a[i][1])

dp[i][2] = min(dp[i-1][1]+t[i-1][1]+a[i][2],dp[i-1][2]+a[i][2])

這樣min(dp[n][1],dp[n][2])就是想要的答案。

矩陣鏈乘法問題

兩個矩陣a(lxm)*b(mxn)相乘所需要的代價是o(lmn),而相乘得到的矩陣式c(lxn)型的,l和n作為矩陣的行列數量仍然儲存在了矩陣鏈中,因此對於乙個矩陣鏈的乘法來說,做乘法的順序不同,所花費的時間就可能相差很多,矩陣鏈乘法問題就是對於乙個給定的矩陣鏈相乘,求解最少的計算時間(如何安排乘法的計算順序)。

動態規劃解法:

我們用dp[i][j]表示矩陣鏈中從第i個乘到第j個所需的最少時間,用r[i]和c[i]分別表示第i個矩陣的行數和列數。有:

dp[i][j]=min(k)(dp[i][k]+dp[k+1][j]+r[i]*c[k]*c[j])

注意c[k]==r[k+1]

即,i到j這一部分矩陣鏈的最優計算一定是在某處k(i<=kdynamic programming這裡的programming指的是查表,表裡存放的是之前求解過的子問題的解。動態規劃就是動態查表,即在求解的過程中的每一步都需要查表來得到對當前子問題求解所需的資訊。如在裝配線問題上dp[i][1]就分別查表找到了dp[i-1][1]和dp[i-1][2];矩陣乘法鏈問題上dp[i][j]的求解查表找到了對應於不同k值的dp[i][k]和dp[k+1][j]。

正因為存放在了表裡,所以不用再次求解,才達到了較高的效率,將傳統回溯解法指數的複雜度降低為多項式程度,關於複雜度後面還會說到。

乙個問題要想用動態規劃的方法來解決,必須具有最優子結構,至於為什麼具有最優子結構的問題就可以用動態規劃來解決,後面說。現在說說什麼叫最優子結構。

最優子結構是指乙個問題的解是乙個選擇過程,可能是二選一(裝配線問題選擇從前一道工序的哪條線上過來)、可能是多選一(矩陣鏈決定從**斷開,即先做哪一處乘法)。假設作出了正確的選擇後(這其實是需要子問題資訊來做選擇依據的),原問題就退化成乙個或多個子問題(在裝配線問題中,退化成所選擇的那條裝配線上完成第i-1道工序需要的最少時間,這是乙個子問題;在矩陣乘法鏈問題中,退化成兩條矩陣鏈i到k和k+1到j的最優求解問題,這是兩個子問題),所謂子問題指的是與原問題同構但規模較小的問題。

光有這個性質還不叫具有最優子結構,最優子結構的另乙個要求是原問題的最優解必須包含子問題的最優解。在驗證這一點時,可以通過剪貼+反證法的思路,即如果該解法用到的子問題的解不是最優的,那麼以最優的解代替之一定可以得到該問題的乙個更好的解。還是通過例子說,裝配線問題中,不管加工工序i的最優選擇是從哪條線過來,所用到的在該線上加工工序i-1的時間一定需要是最優的,否則用最優的替換之可得到對於工序i的乙個更優的解。

有了這兩條性質——1.做選擇後問題退化為乙個或幾個子問題、2.問題的最優解包含子問題的最優解,乙個問題就具備了最優子結構。

如果乙個問題具有最優子結構的第一條性質,就可以使用分而治之的思想求解,比如遞迴。通常做法是將問題分解,然後對於分解出現的各個子問題分別遞迴求解,如果這種分解是多分枝的,那麼複雜度應該是指數級的。

同遞迴思想一樣,動態規劃也需要對所有子問題都全部求解才能得到最終的答案,但是動態規劃不願意單純地像遞迴那樣做,而希望能夠通過一些手段只做必要的工作,多餘的一點都不想做。

動態規劃是基於如下兩點觀察:

1)某個子問題的求解只和規模更小的子問題求解有關,而與與其同規模或者更大規模的子問題求解無關。

2)某個子問題的求解只用到其子問題的最優解,也就是說對於乙個子問題來說,只有其最優解是有價值的。

通過觀察1)我們知道只要在求解所有子問題時控制子問題的求解順序按照子問題規模遞增來進行,就能保證求解任意子問題時,它所依賴的規模較小的子問題都已經求解完畢。

通過觀察2)我們知道只有最優解是有意義的,因此我們將最優解存在表裡以供後面用到時查詢,就可以保證某個子問題只被求解一遍,而且有了上乙個觀察,在求解某個子問題時,可以保證它所需要的**內容都已經準備好了。

所以動態規劃的解法已經呼之欲出了。

首先證明乙個問題具有最優子結構,然後發現描述該問題的子問題應該是什麼形式的(裝配線問題中的dp[i][1]、dp[i][2]和矩陣鏈乘法問題中的dp[i][j]),按照子問題規模遞增的順序求解所有子問題,最後得到整個問題的解。

而,這裡的關鍵就是找出子問題的形式,和子問題在經過選擇後如何向更小規模的子問題退化。

下面給出兩個例子的偽**

動態規劃的過程就是求解所有子問題的過程,因此首先複雜度由子問題的個數決定,而子問題的個數又有子問題的形式所決定。在裝配線問題中子問題形式是dp[i][1 or 2],其中只有i是變化的,因此子問題個數為o(n);在矩陣鏈乘法問題中,子問題形式是dp[i][j],i和j都在變化,因此子問題個數是o(n^2)。另外由於前面所提到的每乙個子問題都是乙個選擇,可能是二選一,可能是多選一,而要解答此子問題就要考察所有的選項,因此每乙個子問題的求解時間還和面臨的選擇個數有關,因此複雜度應該是子問題個數*每個子問題面臨選擇數。這樣,裝配線問題的時間複雜度o(n),矩陣鏈乘法問題的時間複雜度為o(n^3),正如**中所示。

理解動態規劃

通過了解契波那契數列學習動態規劃 問題 斐波那契數列為1 1 2 3 5 8 13 21 34 寫乙個函式,輸入n,求斐波那契 fibonacci 數列的第n項。遞迴方法 includeusing namespace std int dfs int x int main return a x int...

動態規劃的理解

動態規劃的理解 什麼是動態規劃 動態規劃是一種非常精妙的演算法思想,它沒有固定的寫法,極其靈活,常常需要具體問題具體分析。和之前介紹的大部分演算法不同,一開始就直接討論動態規劃的概念並不是很好的學習方式,反而先接觸一些經典模型會有更好的效果。因此本章主要介紹一些動態規劃的經典模型,並在其中穿插動態規...

動態規劃 基礎理解

動態規劃 英語 dynamic programming,簡稱 dp 是一種在數學 管理科學 電腦科學 經濟學和生物資訊學中使用的,通過把原問題分解為相對簡單的子問題的方式求解複雜問題的方法。動態規劃常常適用於有重疊子問題和最優子結構性質的問題,動態規劃方法所耗時間往往遠少於樸素解法。動態規劃背後的基...