動態規劃dp與三種揹包問題
動態規劃dp
這個演算法以普通的揹包問題引入。
它其實是將大塊問題分解成小問題,並使得每一個小問題的解決都可依賴其它小問題解決後的結果來進行。
首先描述一下揹包問題:
容量為10的揹包,有5種物品,每種物品只有一個,其重量分別為5,4,3,2,1,其價值分別為1,2,3,4,5。設計演算法,實現揹包內物品價值最大。
對於這個大問題,把它分解為:設物品標號為[1],[2],[3],[4],[5]。然後計算當存在物品分別為[1],[1][2],[1][2][3]…時、揹包容量分別為1、2、3、4…9、10時揹包內價值的最大值。下面展示一下思維過程:
當只有物品[1]時:([1]的重量為5,價值為1,下面以此類推)
size: 1 2 3 4 5 6 7 8 9 10
price: 0 0 0 0 1 1 1 1 1 1
當只有物品[1]、[2]時:
size: 1 2 3 4 5 6 7 8 9 10
price: 0 0 0 2 2 2 2 2 3 3
當只有物品[1]、[2]、[3]時:
size: 1 2 3 4 5 6 7 8 9 10
price: 0 0 3 3 3 3 5 5 5 5
當只有物品[1]、[2]、[3]、[4]時:
size: 1 2 3 4 5 6 7 8 9 10
price: 0 4 4 4 7 7 7 7 9 9
當只有物品[1]、[2]、[3]、[4]、[5],即全部都有時:
size: 1 2 3 4 5 6 7 8 9 10
price: 5 5 9 9 9 12 12 12 12 12
但是每一個最大價格的計算是怎麼依賴前面問題的結果的呢?
假設上面的矩陣為:
那麼在計算dp[i][j]時,進行如下計算和判斷:(j >= w[i] )
就是比較來確定在增加了一個物體後,揹包內能否再在這基礎上放入物品。而這個揹包價值的最大值就是這個矩陣中元素的最大值。
程式碼如下:
int dp[6][11] = {0};
int v[6] = {0,1,2,3,4,5};
int w[6] = {0,5,4,3,2,1};
int n = 5, m = 10;
for(int i = 1;i <= n;i++)
{
for(int j = m;j >= w[i];j--)
{
dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);//減去當前產品的質量之後剩餘多少
}
}
對於完全揹包問題
容量為10的揹包,有5種物品,每種物品數量無限,其重量分別為5,4,3,2,1,其價值分別為1,2,3,4,5。 設計演算法,實現揹包內物品價值最大。
區別就是限制不再是物品個數了,而只是揹包容量。修改的也只是矩陣列的迴圈方式。
程式碼如下:
int dp[6][11] = {0};
int v[6] = {0,1,2,3,4,5};
int w[6] = {0,5,4,3,2,1};
int n = 5, m = 10;
for(int i = 1;i <= n;i++)
{
for(int j = w[i];j <= m;j++)
{
dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);
}
}
對於多重揹包問題
容量為10的揹包,有5種物品,每種物品數量分別為1,2,1,2,1,其重量分別為5,4,3,2,1,其價值分別為1,2,3,4,5。設計演算法,實現揹包內物品價值最大。
按照揹包的思路,每一個物品的限制不同,所以再增加一個迴圈限制即可。
int dp[6][11] = {0};
int v[6] = {0,1,2,3,4,5};
int w[6] = {0,5,4,3,2,1};
int n[6] = {0,1,2,1,2,1};
int n = 5, m = 10;
for(int i = 1;i <= n;i++)
{
for(int k = 1;k <= n[i];k++)
{
for(int j = m;j >= w[i];j--)
{
dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);
}
}
}
理解的不深入的地方還請大家指點。