動(dòng)態(tài)規(guī)劃詳解
動(dòng)態(tài)規(guī)劃 (Dynamic Programming) 是一種算法思想,用于解決一些復(fù)雜的問題。本文將介紹動(dòng)態(tài)規(guī)劃的分類、概念和經(jīng)典例題講解。
動(dòng)態(tài)規(guī)劃的分類
動(dòng)態(tài)規(guī)劃可以分為以下兩種類型:
- 0/1背包問題:該問題是動(dòng)態(tài)規(guī)劃的一種基本類型。在背包問題中,有n個(gè)物品可以放入容量為W的背包中,每個(gè)物品有自己的重量和價(jià)值。需要選擇哪些物品能夠最大化背包的總價(jià)值。
- 最長公共子序列問題:該問題是另一種經(jīng)典的動(dòng)態(tài)規(guī)劃類型,涉及到兩個(gè)字符串,并找到這兩個(gè)字符串之間的最長公共子序列。
動(dòng)態(tài)規(guī)劃的概念
在解決動(dòng)態(tài)規(guī)劃問題時(shí),我們需要定義以下概念:
- 狀態(tài) (State):問題中需要優(yōu)化的變量,如背包問題中的容量,最長公共子序列問題中的字符串長度等。
- 狀態(tài)轉(zhuǎn)移方程 (State Transition Equation):描述狀態(tài)之間的轉(zhuǎn)移過程,即問題的遞推關(guān)系。例如,在背包問題中,每個(gè)物品可以放入背包或不放入背包。因此,狀態(tài)轉(zhuǎn)移方程可以表示為: d p [ i ] [ j ] = max ? ( d p [ i ? 1 ] [ j ] , d p [ i ? 1 ] [ j ? w i ] + v i ) dp[i][j] = \max(dp[i-1][j], dp[i-1][j-w_i]+v_i) dp[i][j]=max(dp[i?1][j],dp[i?1][j?wi?]+vi?) 其中dp[i][j]表示在使用前i個(gè)物品時(shí),填滿j容量的背包的最大價(jià)值。
- 初始狀態(tài) (Initial State):問題的初始條件,通常為問題規(guī)模最小的情況下的答案。在背包問題中,初始狀態(tài)為dp[0][0]=0。
- 邊界狀態(tài) (Boundary State):問題的邊界條件,在狀態(tài)轉(zhuǎn)移過程中需要特別處理的狀態(tài)。在背包問題中,背包的容量不能為負(fù)數(shù),因此需要在狀態(tài)轉(zhuǎn)移方程中特別處理。
經(jīng)典例題講解
下面我們將分別介紹0/1背包問題和最長公共子序列問題的解法。
1. 0/1背包問題
題目描述:有n個(gè)物品和一個(gè)容量為W的背包。第i個(gè)物品的重量為wi,價(jià)值為vi?,F(xiàn)在,需要選擇一些物品放入背包,使得放入的物品的總重量不超過W,且總價(jià)值最大。求最大價(jià)值。
解題思路:定義狀態(tài)dp[i][j]為在使用前i個(gè)物品時(shí),填滿j容量的背包的最大價(jià)值。狀態(tài)轉(zhuǎn)移方程如下所示: d p [ i ] [ j ] = { d p [ i ? 1 ] [ j ] , j < w i max ? ( d p [ i ? 1 ] [ j ] , d p [ i ? 1 ] [ j ? w i ] + v i ) , j ≥ w i dp[i][j] = \begin{cases}dp[i-1][j],&j<w_i\\ \max(dp[i-1][j], dp[i-1][j-w_i]+v_i),&j\ge w_i\end{cases} dp[i][j]={dp[i?1][j],max(dp[i?1][j],dp[i?1][j?wi?]+vi?),?j<wi?j≥wi?? 其中dp[i-1][j]表示不放入第i個(gè)物品的最大價(jià)值,dp[i-1][j-w[i]]+v[i]表示將第i個(gè)物品加入背包的最大價(jià)值。需要注意的是,如果當(dāng)前背包容量小于物品的重量,就不能將該物品放入背包。因此,需要特別處理背包容量小于物品重量的情況。
代碼實(shí)現(xiàn):文章來源:http://www.zghlxwxcb.cn/news/detail-733647.html
int dp[101][1001];
int weight[101], value[101];
int knapSack(int n, int w)
{
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= w; j++) {
if (j < weight[i]) {
dp[i][j] = dp[i-1][j];
} else {
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]);
}
}
}
return dp[n][w];
}
2. 最長公共子序列問題
題目描述:給定兩個(gè)字符串A和B,找到它們的最長公共子序列 (LCS)。
解題思路:定義狀態(tài)dp[i][j]為字符串A的前i個(gè)字符和字符串B的前j個(gè)字符的LCS長度。狀態(tài)轉(zhuǎn)移方程如下所示:
d p [ i ] [ j ] = { 0 , i = 0 或 j = 0 d p [ i ? 1 ] [ j ? 1 ] + 1 , A i = B j max ? ( d p [ i ? 1 ] [ j ] , d p [ i ] [ j ? 1 ] ) , A i ≠ B j dp[i][j] = \begin{cases}0,&i=0\text{或}j=0\\ dp[i-1][j-1]+1,&A_i=B_j\\ \max(dp[i-1][j], dp[i][j-1]),&A_i\neq B_j\end{cases} dp[i][j]=? ? ??0,dp[i?1][j?1]+1,max(dp[i?1][j],dp[i][j?1]),?i=0或j=0Ai?=Bj?Ai?=Bj??
當(dāng)A[i-1]等于B[j-1]時(shí),dp[i][j]等于dp[i-1][j-1]+1,表示A和B中的相同字符加上它們前面的LCS。當(dāng)它們不相等時(shí),LCS為它們前面的LCS的最大值,即dp[i-1][j]和dp[i][j-1]的最大值。
代碼實(shí)現(xiàn):
int dp[1001][1001];
string A, B;
int LCS(int n, int m)
{
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if (i == 0 || j == 0) {
dp[i][j] = 0;
} else if (A[i-1] == B[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[n][m];
}
結(jié)語
動(dòng)態(tài)規(guī)劃是一種非常重要的算法思想,它通常用于解決復(fù)雜的問題。在應(yīng)用動(dòng)態(tài)規(guī)劃解決問題時(shí),需要注意定義狀態(tài)、狀態(tài)轉(zhuǎn)移方程、初始狀態(tài)和邊界狀態(tài)等概念。對于不同類型的動(dòng)態(tài)規(guī)劃問題,需要采用不同的解決方法。希望本文能夠幫助讀者加深對動(dòng)態(tài)規(guī)劃的理解。文章來源地址http://www.zghlxwxcb.cn/news/detail-733647.html
到了這里,關(guān)于「程序員必須掌握的算法」動(dòng)態(tài)規(guī)劃「上篇」的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!