0 背包小结2,以下状态转移代码,假设所有相关变量及数组都已初始化,物品对应体积和价值都从下标为1的数组开始存储,dp[]或者dp[][]是各个状态下的背包内所装物品总价值。
对于下面用到完全背包模型的,都可以预先进行一步优化,筛选掉所有满足volume[i]<volume[j]&&value[i]>value[j]的i和j物品,对于随机生成的数据来说,可以删掉很多不符合的物品。
1 01背包(背包总容纳bag,有type种物品,每种物品只有一个选择放还是不放,给出对应体积vlume[i]、对应价值value[i])
1)二维数组(建议充分理解,而不用之做题)(因为容易忘记将因体积原因放不进去的状态赋值,以及最后遍历v:0->bag寻找最大值)
//01背包 二维数组
for(int i=1;i<=type;i++){
for(int j=0;j<=bag;j++){
if(j>=volume[i]){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-volume[i]]+value[i]);
}
else{
dp[i][j]=dp[i-1][j];//注意
}
}
}
int maxx=0;
for(int j=1;j<=bag;j++){
maxx=max(maxx,dp[type][j]);
}
//dp[i][j]指选择完第i个物品放或不放后 背包容量为j时,背包内所有物品的价值
//二维数组因为不能像一维数组一样自动更新,所以如果第i个物品装不进去时,要使dp[i][j]继承上一个物品放置之后的状态dp[i-1][j]而不是维持初始值。
2)一维数组
//01背包 一维数组
for(int i=1;i<=type;i++){
for(int j=bag;j>=volume[i];j--){
dp[j]=max(dp[j],dp[j-volume[i]]+value[i]);
}
}
//01背包,每种物品只有一个,该种物品的值不叠加,所以倒序,详见上一篇博客 DP背包问题小结(01背包,完全背包,需恰好装满或不需,一维DP、二维DP)
2 完全背包(背包总容纳bag,有type种物品,每种物品有无限多,给出对应体积vlume[i]、对应价值value[i])
//完全背包 一维数组
for(int i=1;i<=type;i++){
for(int j=volume[i];j<=bag;j++){
dp[j]=max(dp[j],dp[j-volume[i]]+value[i]);
}
}
//完全背包,每种物品无限多个,该种物品的值可以叠加,所以直接正序,详见上一篇博客 DP背包问题小结(01背包,完全背包,需恰好装满或不需,一维DP、二维DP)
3 多重背包
①耗用更多内存,来换取更快的时间:
(背包总容纳cash,有n种物品,每种物品有给定数量species[i].num,每种物品有给定的价值species[i].deno,这里物品的体积等同于价值。)
(使用的number[j]是指,第i个物品在当前的背包内存储金额j的情况下使用的数量,正是这个数组的使用来做到用空间换时间)
for(int i=0;i<n;i++){
memset(number,0,sizeof(number));
for(int j=species[i].deno;j<=cash;j++){
if(dp[j]<dp[j-species[i].deno]+species[i].deno&&number[j-species[i].deno]+1<=species[i].num){
dp[j]=dp[j-species[i].deno]+species[i].deno;
number[j]=number[j-species[i].deno]+1;
}
}
}
②耗用更多时间,来换取更少内存使用:
(背包总容纳bag,有type种物品,每种物品有给定的数量,给出对应体积vlume[i]、对应价值value[i]、对应数量number[i])
注意!倒序!
以下两者都是利用倒序 每一种物品都只放一个的特性,前者是该类物品放k次每次都放一个,后者是该类物品放一次 一次从1到k 放入个数依次增大
//多重背包,一维数组
for(int i=1;i<=type;i++){
int minn=min(number[i],bag/volume[i]);//这里
for(int k=1;k<=minn;k++){
for(int j=bag;j>=volume[i];j--){//注意是倒序,否则就错了
dp[j]=max(dp[j],dp[j-volume[i]]+value[i]);
}
}
}
//防止给出的该类物品个数大于实际上能存储的该类物品最大数量(有些用母函数的代码,就是在这里判断一步,如果给出的数量大于背包总容量除以该类物品体积得到的数量,那么就认为该类物品是取不完的 可以转化为完全背包来做,否则就转化为01背包)
OR
for(int i=1;i<=type;i++){
for(int j=bag;j>=volume[i];j--){//注意是倒序,否则就错了
for(int k=1;k<=number[i]&&(volume[i]*k<=j);k++){//k=0开始就是不放等同于dp[j],所以不用从0开始
dp[j]=max(dp[j],dp[j-volume[i]*k]+value[i]*k);
}
}
}
HDU2191 裸多重背包
4二维费用的背包问题 (隐晦表示二维费用背包:有时候题目交代背包最多存x件物品,这是隐晦地表示背包能存件数也是一种费用。)(加多重背包的,类似,加数量那一步循环)
//二维费用背包(两种代价) 完全背包(每种物品无限多个)——所以正序
for(int i=1;i<=type;i++){
for(int j=volume[i];i<=bag_volume;i++){
for(int l=weight[i];l<=bag_weight;l++){
dp[j][l]=max(dp[j][l],dp[j-volume[i]][l-weight[i]]+value[i]);
}
}
}
OR
//二维费用背包(两种代价) 01背包(每种物品只有一个)——所以倒序
for(int i=1;i<=type;i++){
for(int j=bag_volume;j>=volume[i];j--){
for(int l=bag_weight;l>=weight[i];l--){
dp[j][l]=max(dp[j][l],dp[j-volume[i]][l-weight[i]]+value[i]);
}
}
}
5混合背包
看情况组合,部分多重背包在数量足够的情况下可以转化为完全背包,而完全背包又可以转为01背包处理
6分组背包
7有依赖背包
8泛化物品
9部分算法优化以及背包问题问法变化
1)二进制优化
//注意,number[i]已被改动,如果不想原来的数组里改动,可以另存一个数组里、参考下面给出例题的解题报告。
for(int i=1;i<=n;i++){
for(int k=1;k<=number[i];k*=2){
for(int j=bag;j>=volume[i]*k;j--){
dp[j]=max(dp[j],dp[j-volume[i]*k]+k);
}
number[i]-=k;//!!!
}
for(int j=bag;j>=volume[i]*number[i];j--){
dp[j]=max(dp[j],dp[j-volume[i]*number[i]+number[i]);
}
}
HDU3591混合背包-经典
剩下的再写、
对应题目,请参考本博客DP一类下有关背包的题目。
(本文内容结构参考了dd大牛《背包九讲》,链接之一,http://www.cnblogs.com/jbelial/articles/2116074.html)(注意里面有小错误)