動態規劃引入
首先我們以乙個最基本的例子來分析——菲波那切數列。
我們都知道,菲波那切數列的遞推公式f(n) = f(n-1)+f(n-2) (這裡我就說明一般情況,不列舉邊界條件了),很簡單,如果我們用遞迴的方法來求解f(n),兩三行**就出來了。那麼我們深入分析一下這樣有什麼問題?
f(2) = f(1) + f(0);
f(3) = f(2) + f(1);
f(4) = f(3) + f(2);
f(5) = f(4) + f(3);
......
計算乙個f(5)我們需要計算乙個f(4)和乙個f(3),而乙個f(4)又需要乙個f(3)和乙個f(2),這其中就有了乙個重複的f(3),那麼在繼續往下推導,會發現有越來越多的重複。當我們在計算機中計算f(40)並輸出時,我們會發現已經有相當長時間的延時了,為什麼?因為這樣的遞迴重複計算太多了,導致整個演算法效率非常低。
由於上述過程存在著大量的重複計算,我們可以用乙個陣列儲存所有已經計算過的項,這樣便可以達到用空間換時間的目的,在這種情況下,時間複雜度為o(n),而空間複雜度也為o(n)。事實上,我們所述的這種演算法就是利用了動態規劃的思想。
動態規劃演算法的思想與分治法類似,也是通過組合子問題的解而解決整個問題。其基本思路是利用乙個表來記錄所有已解的子問題的答案,不管該子問題以後是否被用到,只要它被計算過,就將結果填入表中,這樣就可以避免重複計算問題。
動態規劃演算法的設計可以分為如下幾個步驟:
1)描述最優解的結構;
2)遞迴定義最優解的值;
3)按自底向上的方式計算最優解的值;
4)由計算出的結果構造乙個最優解;
其中第1~3步構成問題的動態規劃解的基礎。
適合動態規劃方法的最優化問題的兩個要素:最優子結構,重疊子問題。
例1:求用1*2的瓷磚覆蓋2*m的地板有幾種方式?
分析:假設所求問題的解為f(m),有下面兩種情況:
當第一塊瓷磚豎著放的時候,問題轉換成求用1*2的瓷磚覆蓋剩下的2*(m-1)的方式,即f(m-1)。
當第一塊瓷磚橫著放的時候,則必有另一塊瓷磚橫著放在其下面,問題轉換成求用1*2的瓷磚覆蓋 剩下的2*(m-2)的方式,即f(m-2)。
在求f(m-1)和f(m-2)時,由於第一列地板的覆蓋方式已經不同,故f(m-1)種覆蓋方式和f(m-2)中覆蓋方式沒有重疊,故:
f(m) = f(m-1)+f(m-2)
其中,f(1) = 1,f(2) = 2。可見我們能夠將問題規模縮小。
仔細看遞推式,其實就和菲波那切數列是一樣的,既然這樣,我們就不要用上面的遞迴方式進行求解f(m)了,而是用動態規劃的方式,建立乙個**,儲存每一步驟的f(i).
例2:lcs(最長公共子串行問題)//...
int a[100];
int func(int m){}
a[1] = 1;
a[2] = 2;
for(int i = 3; i <= m; ++i)
a[i] = a[i-1] + a[i-2];
return a[m];
}//...
注:lcs問題不要求所求得的字元在所給的字串中是連續的。
分析:假設x=和y=的乙個最長公共子串行為z=,
1)若x(m) = y(n),則必然有z(k) = x(m) = y(n),且z(k-1)是x(m-1)和y(n-1)的最長公共子串行;
2)若x(m) != y(n)且z(k) != x(m),則z是x(m-1)和y的最長公共子串行;
3)若x(m) != y(n) 且z(k) != y(n),則z是x和y(n-1)的最長公共子串行;
也就是說,
當x(m) = y(n)時,lcs(x(m),y(n)) = lcs(x(m-1),y(n-1)) + 1;
當x(m) != y(n)時,lcs(x(m),y(n)) = max;
若用乙個二維**c來儲存lcs,則c[i][j]表示x和y長度分別為i和j時的lcs,顯然,當x或y為空時,lcs為0.
下面給出動態規劃演算法的**:
下面再給出一種非遞迴的方法。/* 動態規劃:最長公共子串行問題lcs */
const int inf = 99999;
int c[100][100];
int lcs_memo(string a, string b, int i, int j)
return c[i][j];
}int lcs_length(string a, string b)
例3: 01揹包問題:乙個揹包有一定的承重cap,有n件物品,每件都有自己的價值,記錄在陣列v中,也都有自己的重量,記錄在陣列w中,每件物品只能選擇要裝入揹包還是不裝入揹包,要求在不超過揹包承重的前提下,選出物品的總價值最大,給定物品的重量w價值v及物品數n和承重cap。請返回最大總價值。//dp[i][j]存放的是長度分別為i、j的字串a、b的lcs
int lcs(string a, string b, int m, int n)
} return dp[m][n];
}
分析:這裡以行n,列cap建立二維**dp[n+1][cap+1],其中dp[i][j]表示重量不超過j時的最大價值。那麼這裡就有兩種情況:
1)選擇第i件物品,則前i-1件物品的重量不能超過j-w[i];
2)不選擇第i件物品,則前i-1件物品的重量不能超過j;
即dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
int getmaxvalue(vectorw, vectorv, int n, int cap)
}}
動態規劃經典問題總結
假設有幾種硬幣,如1 3 5,並且數量無限。請找出能夠組成某個數目的找零所使用最少的硬幣數。這是一道經典的動態規劃方法,我們可以維護乙個一維動態陣列dp,其中dp i 表示錢數為i時的最小硬幣數的找零,遞推式為 dp i min dp i dp i coins j 1 其中coins j 為第j個硬...
動態規劃經典問題
from 實現在 維基百科對動態規劃的定義 動態規劃 英語 dynamic programming,簡稱dp 是一種在數學 電腦科學和經濟學中使用的,通過把原問題分解為相對簡單的子問題的方式求解複雜問題的方法。動態規劃常常適用於有重疊子問題 1 和最優子結構性質的問題,動態規劃方法所耗時間往往遠少於...
動態規劃經典問題
子串 在給定的字串中選取連續的一段 子串行 可以不連續,但是要保證出現的順序與原字串相同 比如字串abcdefg abc既是子串又是子串行 acd只是子串行 一 最大連續子串行和 給定乙個整數序列a1,a2 an。求最大的連續的子串行的和。比如的最大連續子串行的和為5 3 1 1 2 萬能列舉?每次...