動態規劃問題的一般形式就是求最值。
求解動態規劃的核心問題是窮舉。
動態規劃的窮舉有點特別,因為這類問題存在「重疊子問題」,如果暴力窮舉的話效率會極其低下,所以需要「備忘錄」或者「dp table」來優化窮舉過程,避免不必要的計算。
而且,動態規劃問題一定會具備「最優子結構」,才能通過子問題的最值得到原問題的最值。
關鍵就是狀態轉移方程,寫出正確的狀態轉移方程,才能正確地列舉。
解決問題步驟:
明確「狀態」 -> 定義 dp 陣列/函式的含義 -> 明確「選擇」(做出選擇改變當前狀態-> 明確 base case。
有n件物品和乙個容量為v的揹包。第i件物品的費用(即體積,下同)是w[i],價值是val[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。
用動態規劃的思路,階段就是「物品的件數」,狀態就是「揹包剩下的容量」,那麼很顯然f [ i , v ] 就設為從前 i 件物品中選擇放入容量為 v 的揹包最大的價值。那麼狀態轉移方程為:
f[i][v]=max。
這個方程可以如下解釋:只考慮子問題「將前 i 個物品放入容量為 v 的揹包中的最大價值」那麼考慮如果不放入 i ,最大價值就和 i 無關,就是 f[ i - 1 ][ v ] , 如果放入第 i 個物品,價值就是 f[ i - 1][ v - w[i] ] + val[ i ],我們只需取最大值即可。
上述狀態表示,我們需要用二維陣列,但事實上我們只需要一維的滾動陣列就可以遞推出最終答案。考慮到用f[ v ]來儲存每層遞迴的值,由於我們求f[ i ][ v ] 的時候需要用到的是f[ i-1 ][ v] 和 f[ i-1 ][v - w[i] ] 於是可以知道,只要我們在求f[ v ]時不覆蓋f[ v - w[i] ],那麼就可以不斷遞推至所求答案。所以我們採取倒序迴圈,即v = m(m為揹包總容積)偽**如下:
for i = 1..n
for v = v..0
f[ v ] = max;
有n種物品和乙個容量為v的揹包,每種物品都有無限件可用。第i種物品的費用是w[i],價值是val[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。
完全揹包問題與0/1揹包問題不同之處在於其每個物品是無限的,從每種物品的角度考慮,與它相關的策略就變成了取0件、1件、2件...。我們可以根據0/1揹包的思路,對狀態轉移方程進行改進,令f[i][v]表示前 i 種物品恰放入乙個容量為 v 的揹包的最大權值。狀態轉移方程就變成了:
f[ i ][ v ] = max。
我們通過對0/1揹包的思路加以改進,就得到了完全揹包的一種解法,這種解法時間複雜度為o(n3),空間複雜度為o(n2)。
根據上述f[ i ][ v ]的定義,其為前 i 種物品恰好放入容量為 v 的揹包的最大權值。根據上述狀態轉移方程可知,我們假設的是子結果f[ i-1 ][ v-k*w[i] ]中並沒有選入第 i 種物品,所以我們需要逆序遍歷(像0/1揹包一樣)來確保該前提;但是我們現在考慮「加選一件第 i 種物品」這種策略時,正需要乙個可能已經選入第 i 種物品的子結果f[ i ][ v-w[i] ],於是當我們順序遍歷時,就剛好達到該要求。這種做法,使我們省去了一層迴圈,即第 i 種物品放入的件數k,從而時間複雜度優化為o(n^2)。
正如0/1揹包的空間優化,上述狀態轉移方程已經優化為:
f[i][v]=max
將這個方程用一維陣列實現,便得到了如下偽**:
for i = 1..n
for v = 0..v
f[v] = max;
int coinchange(vector& coins, int amount)
} return dp[amount] == amount + 1 ? -1 : dp[amount];
}
狀態:揹包的容量和可選擇的物品
選擇:裝入揹包和不裝入揹包
for 狀態1 in狀態1的所有取值:
for 狀態2 in 狀態2的所有取值:
dp[狀態1][狀態2]=擇優(選擇1,選擇2)
dp陣列表示狀態。
dp[i][w]的定義:對於前i個物品,當前揹包的容量為w,這種情況下可以裝的最大價值為dp[i][w]。
這樣做是為了便於狀態轉移。
我們想求的最終答案是dp[n][w]。base case就是dp[0][..]=dp[..][0]=0,因為沒有物品或者沒有揹包空間時,能裝的最大價值為0.
int dp[n+1][w+1]
dp[0][..]=dp[..][0]=0
for i in[1..n]:
for j in [1,w]:
dp[i][j]=max(i裝入揹包,不把i裝入揹包)
return dp[n][w]
要結合dp陣列的定義和演算法邏輯
如果沒有把第i個物品裝入,那麼dp[i][w]=dp[i-1][w](不裝,繼承之前的結果)
如果把第i個物品裝入,那麼dp[i][w]=dp[i-1][w-wt[i-1]]+val[i-1];(i從1開始,val和wt取值為i-1.
dp[i-1][w-wt[i-1]]:在裝入第i個物品的前提下,揹包能裝的最大價值是多少?
顯然應該尋求重量為w-wt[i-1]限制下能裝入的最大價值,再加上第i個個物品的價值val[i-1]。
for i in[1..n]:
for j in [1,w]:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-wt[i-1]+val[i-1])
return dp[n][w]
int _01(int w, int n, vector& wt, vector& val)
} return dp[n][w];
}
簡單揹包問題
#include #include using namespace std;
int main()
dp[0][0] = 1;
dp[0][1] = 0;
dp[1][0] = 1;
for(int j=1;j <= s;j++)
for(int i = 1;i <= n;i++)
for (int i = 1; i <= n; i++)
else}}
if (dp[n][s] == 1)
else
return 0;
}
動態規劃 01揹包 完全揹包
有一類動態規劃可解的問題,它可以描述稱為若干有序的階段,且每個階段的狀態只和上乙個階段的狀態有關,一般把這類問題稱為多階段規劃問題。01揹包問題描述如下 有 n 件物品,每件物品的重量為 w i 價值為 c i 現有乙個容量為 v 的揹包,問如何選取物品放入揹包,使得揹包內物品的總價值最大。其中每種...
動態規劃 揹包問題(01揹包 完全揹包)
揹包問題 多種物品 重量不同 價值不同 你可以取最多重量不超過w的物品,問最大價值為多少?01揹包 指的是 有n個物品 每個物品的重量為w i 價值為v i 每個物品只有乙個 所有面臨這些物品只有兩種結果 1 拿這件物品,揹包容量減去w i 此時的價值增加v i 2 不拿這件物品,揹包容量不變,最大...
總結 動態規劃 01揹包 完全揹包
前一段時間我學習了一下動態規劃裡面的01揹包和完全揹包,自己做了一些總結與大家分享一下,希望對你們有幫助 首先來說一下什麼是動態規劃 動態規劃就是把大問題拆分成小問題,通過尋找大問題與小問題的遞推關係,解決乙個個小問題,最終達到解決原問題的效果。通過填寫表把所有已經解決的子問題答案紀錄下來,動態規劃...