輸入:
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]);
}
<<挑戰程式設計競賽>>讀後感