動態規劃的思想是將乙個問題分解為若干子問題,並且子問題之間還有重疊,通過先求解這些子問題的最優解,將原問題的最優解通過這些子問題的最優解構造出來,從而得到原問題的最優解。
由此可以得到動態規劃演算法的應用場景:若求乙個問題的最優解(通常是求最大值或者最小值),而且該問題能夠分解成若干個子問題,並且子問題之間還有重疊的更小的子問題,就可以考慮用動態規劃來解決這個問題。
由此總結一下判斷乙個問題是否能用動態規劃來解決的基本步驟:
1.看這個問題是否是求最優解(最大值或者最小值)
2.看該問題能否分解為若干個子問題,且整體問題的最優解是可以由各個子問題的最優解得出的。(最優子結構)
3.看分解的各個子問題之間是否有重疊(這不是應用動態規劃的必要條件,但如果子問題沒有重疊,那麼使用動態規劃演算法與其他演算法相比就沒有什麼優勢,完全可以直接用分治思想解決,不必要用動態規劃)
如果乙個問題滿足以上三個特徵,那麼就可以用動態規劃的思想解決,使用動態規劃的步驟為:
1.劃分狀態,即劃分子問題。把問題劃分為若干個小問題,劃分時要注意,較大的問題的最優解一定可以由較小的問題的最優解得到。最後的求解思路就是先得到小問題的最優解,然後由小問題的最優解逐步構造大問題的最優解,最後得到想解決問題的最優解。
2.狀態表示,即如何讓計算機理解子問題。確定怎麼描述各個不同大小的問題的狀態,將問題發展到各個階段時所處於的各種客觀情況用不同的狀態表示出來,作為得到最優解的必要資訊。
3.找出狀態轉移方程,即父問題是如何由子問題推導出來的,也即是乙個較大的問題的最優解到底怎麼由較小問題的最優解得到,換句話說,就是找到較大問題的最優解和較小問題的最優解之間的具體聯絡。
4.尋找邊界條件,確定初始狀態是什麼?最小的子問題?最終狀態又是什麼?
其中的難點主要在於:怎樣劃分子問題,怎樣得到較大子問題和較小子問題的聯絡(怎麼從前乙個狀態的最優解推得後乙個狀態的最優解)。
演算法實現的步驟:
1、建立乙個一維陣列或者二維陣列,儲存每乙個子問題的結果,具體建立一維陣列還是二維陣列看題目而定,基本上如果題目中給出的是乙個一維陣列進行操作,就可以只建立乙個一維陣列,如果題目中給出了兩個一維陣列進行操作或者兩種不同型別的變數值,比如揹包問題中的不同物體的體積與總體積,找零錢問題中的不同面值零錢與總錢數,這樣就需要建立乙個二維陣列。
2、設定陣列邊界值,一維陣列就是設定第乙個數字,二維陣列就是設定第一行跟第一列的值。
3、找出狀態轉換方程,也就是說找到每個狀態和其上乙個狀態的關係,根據狀態轉化方程寫出**。
4、返回需要的值,一般是陣列的最後乙個元素或者二維陣列的最右下角的元素。
可以用動態規劃解決的問題示例有:斐波那契數列、陣列最大不連續遞增子串行、陣列最大連續子串行和、兩個字串最大公共子串行、揹包問題以及找零錢問題等等。
動態規劃和分治演算法的主要區別就是,動態規劃演算法通過先計算並儲存小問題的最優解,然後通過小問題的最優解逐步得到最終需要求解問題的最優解,避免了重複計算,本質就是通過付出額外的空間來節約計算時間。
下面用01揹包問題說明動態規劃演算法:
問題描述:
乙個旅行者有乙個最多能裝c公斤的揹包,現在有n件物品,每件的重量分別是w1、w2、……、wn,每件物品的價值分別為v1、v2、……、vn, 需要將物品放入揹包中,要怎麼樣放才能保證揹包中物品的總價值最大?
思路分析:
首先,這個問題是乙個求極值問題,求最大值價值,也即是求最大值。那麼之後可以看這個問題是否具有最優子結構。本來是要求n件物品裝入容量為c的揹包的最大價值,如果我們知道了n-1件物品裝入容量為c的揹包的最大價值,能不能推得n件物品裝入容量為c的揹包的最大價值,感覺好像是可以的(具體狀態轉移方程等會再詳細分析)。接下來看各個子問題是否有重合,我覺得其實這一步最好是在推出狀態轉移方程之後再進行。如果要用動態規劃,找不出狀態轉移方程,根本就沒法得到最終需要求的解,所以我覺得分析狀態轉移方程是重點。
接下來就試著分析狀態轉移方程:
首先明確我們要求的問題:n件物品裝入容量為c的揹包的最大價值。那麼可以怎麼劃分子問題,一種方式是考慮m(m(前i件物品裝入容量為j的揹包的最大價值)來描述各個問題的狀態,找出對應的最優解,用陣列result[i][j]表示。現在我們就完成了劃分子問題和狀態表示兩步,之後就是推出狀態轉移方程。
假設我們已經求出前i-1件物品裝入容量j的揹包的價值總和最大值為result[i-1][j],固定容量j的值不變,則對第i件物品的裝法討論如下:
首先第i件物品的重量w[i]必須小於等於容量j才行,即
1、若w[i]>j,則第i件物品肯定不能裝入容量為j的揹包,此時result[i][j]=result[i-1][j]
2、若w[i]<=j,則首先明確的是這件物品是可以裝入容量為j的揹包的,那麼如果我們將該物品裝入,則有 result[i][j]=result[i-1][j-c[i]]+v[i] ,需要注意的是,這裡求將第i件物品裝入容量為j的揹包的最大價值時,必須根據將前i-1個物品裝入容量為j-c[i]的揹包的最大價值來得到,這點很重要。
隨之而來的問題是我們要判斷第i件物品裝到容量為j的揹包後,揹包內的總價值是否是最大?那麼就要對比裝入第i件物品和不裝入第i件物品哪種方式得到的總價值更大,即如果裝了第i件物品後的總價值result[i-1][j-c[i]]+v[i]大於沒裝之前的總價值最大值result[i-1][j],則應該裝入第i件物品;反之則說明第i件物品不必裝入容量為j的揹包。
故,狀態轉移方程如下:
result[i][j] = max(result[i-1][j-c[i]]+v[i],result[i-1][j])
注意:這裡的前i件物品是給定次序的。
一種實現方式如下:
public static int mysolver(int capacity,int weight,int value)
for (int j = 0;j <= capacity;j++)
for (int j = 1;j <= capacity;j++)else }}
int myresult = result[allthingsnum][capacity];
//下面的程式是為了輸出選出的商品編號
int j = capacity;
string numstr = "";
for (int i = allthingsnum;i > 0;i--)
if (j==0)
break;
}system.out.println("選中的商品編號為:"+numstr);
return myresult;
}
動態規劃演算法的理解
動態規劃演算法主要的核心思想是 狀態和狀態轉移方程。怎麼理解這個問題呢?先拿到了乙個數字三角形的程式,閱讀了它的動態規劃的 但是看不明白!不明白在什麼地方呢?在具體的執行過程各個變數的變化方向。比如兩層for迴圈,i為逆序,j為次序。那麼這種執行次序就沒有辦法想象了。那就先不管上面的了,先看dag上...
動態規劃演算法理解
幾個月前已經弄懂了的演算法,現在回憶起來這麼費勁。又得重頭開始,真是浪費生命啊。再好的腦袋也不如爛筆頭!這裡用最長公共子串行問題 lcs 來說明演算法 給定兩個序列 x y 求x y長度最長的公共子串行。前期儲備知識 公共子串行不等於公共字串 注意區分 例如,如果x y 那麼就是x和y的公共子串行,...
動態規劃演算法 理解
動態規劃 多階段 兩段 最優化決策解決問題的過程就稱為動態規劃。1 描述優解的結構特徵。2 遞迴地定義乙個最優解的值。3 自底向上計算乙個最優解的值。4 從已計算的資訊中構造乙個最優解。1 最優化原理 問題的最優解包含的字問題也有最優解,就稱該問題具有最優子結構,滿足最優化原理。單調遞增最長子序列 ...