小结论(3)
分组背包的学习结束了,收获颇多,分组背包是个很神奇的东西。
分组背包经常一般是三种情况。
(1)每组物品最多选一个(最常规的)
for(i = 0; i < n; i++) //n是总共种类 for(j = vmax; j >= 0; j--) //vmax是背包最大容量 for(k = 0; k < num[i]; k++) //num[]为该类物品个数 if(j >= v[k]) dp[j] = max(dp[j], dp[j-v[k]+w[k]]);
这是一维的情况,二维很容易扩展,接下来两种题目必须用二维。而且用到用到01背包里面恰好到达的概念,必须通过之前已经得到的状态才能转到现在的状态。
(2)每组物品随便选
memset(dp, -1, sizeof(dp)); dp[0][0] = 0; for(i = 1; i <= n; i++) //n是总共种类 for(k = 0; k < num[i]; k++) //num[]为该类物品个数 { for(j = vmax; j >= v[k]; j--) //vmax是背包最大容量 { if(dp[i][j-v[k]] != -1) dp[i][j] = max(dp[i][j], dp[i][j-v[k]]+w[k]); if(dp[i-1][j-v[k]] != -1) dp[i][j] = max(dp[i][j], dp[i][j-v[k]]+w[k]); } for(j = 0; j <= vmax; j++) dp[i][j] = max(dp[i][j], dp[i-1][j]); }
(3)每组物品至少选一个
memset(dp, -1, sizeof(dp)); dp[0][0] = 0; for(i = 1; i <= n; i++) //n是总共种类 for(k = 0; k < num[i]; k++) //num[]为该类物品个数 { for(j = vmax; j >= v[k]; j--) //vmax是背包最大容量 { if(dp[i][j-v[k]] != -1) dp[i][j] = max(dp[i][j], dp[i][j-v[k]]+w[k]); if(dp[i-1][j-v[k]] != -1) dp[i][j] = max(dp[i][j], dp[i][j-v[k]]+w[k]); } }
(3)的代码与(2)的代码区别只在于少了两行,这是因为(3)必须是有之前得到的状态转移过来,而(2)可以继承之前得到的状态。
注意!!!这里面的一个细节。转换的时候是先判断的同类的物品,再判断之前类物品!!!这是因为如果一个物品v值是0,那么如果你先判断之前类物品,在j为0时肯定会转移一次,那么你再判断同类物品,在j为0时又会转移一次。而如果你先判断同类物品,因为初始化是-1,所以不会转移。