輸入:

n=3

(w,v)={(3,4),(4,5),(2,3)}

W=7

輸出:

10(0號物品選1個,2號物品選2個)

和01揹包的區別是物品可以任意選擇.

令dp[i+1][j]=從前i種物品中挑選任意總重量不超過j時總價值的最大值.那麼遞推關係為:

dp[0][j]=0

dp[i+1][j]=max{dp[i-k*w[i]]+k*v[i]|0<=k}

=max{dp[i][j-k*w[i]]+k*v[i]|0<=k}

 int dp[MAX][MAX];  //DP陣列

 void solve()
{
for(int i=; i<n; i++){
for(int j=; j<=W; j++){
for(int k=; k*w[i]<=j; k++){
dp[i+][j]=max(dp[i+][j],dp[i][j-k*w[i]]+k*v[i]);
}
}
}
printf("%d\n",dp[n][m]);
}

複雜度為:O(nW2)

修改後:

 void solve()
{
for(int i=; i<n; i++){
for(int j=; j<=W; j++){
if(j<w[i]){
dp[i+][j]=dp[i][j];
}
else{
dp[i+][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
}
}
}
printf("%d\n",dp[n][W]);
}

複雜度:O(nW)

當然,01揹包和完全揹包可以通過不斷重複利用一個數組來實現.

01揹包問題的情況:

 int dp[MAX];  //DP陣列

 void solve()
{
for(int i=; i<n; i++){
for(int j=W; j>=w[i]; j--){
dp[i]=max(dp[j],dp[j-w[i]+v[i]]);
}
}
printf("%d\n",dp[W]);
}

完全揹包問題的情況:

 int dp[MAX];  //DP陣列

 void solve()
{
for(int i=; i<n; i++){
for(int j=w[i]; j<=W; j++){
dp[i]=max(dp[j],dp[j-w[i]+v[i]]);
}
}
printf("%d\n",dp[W]);
}

兩者的差異就變成只有迴圈方向.重複利用陣列雖然可以節省記憶體空間,但是得不好將有可能留下bug,所以要格外小心.不過出於節約記憶體的考慮,有時候必須要重讀利用陣列.也存在通過重複利用能夠進一步降低複雜度的問題.

DP陣列的再利用:

可以通過講兩個陣列滾動使用來實現重複利用.

dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i])

dp[i+1]計算時只需要dp[i]和dp[i+1],所以可以結合奇偶性寫成如下形式:

 int dp[][MAX];  //DP陣列

 void solve()
{
for(int i=; i<n; i++){
for(int j=; j<=W; j++){
if(j<w[i]){
dp[(i+)&][j]=dp[i&][j];
}
else{
dp[(i+)&][j]=max(dp[i&][j],dp[(i+)&][j-w[i]]+v[i]);
}
}
}
printf("%d\n",dp[n&[W]);
}

<<挑戰程式設計競賽>>讀後感