DP背包小结(2)(结构完整,内容简洁,状态方程篇)

来源:互联网 发布:php 3des加密 编辑:程序博客网 时间:2024/04/30 06:50


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)

HDU2602 裸01背包




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)

HDU4508 裸完全背包



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]);}}}

(POJ1276找零钱)



//防止给出的该类物品个数大于实际上能存储的该类物品最大数量(有些用母函数的代码,就是在这里判断一步,如果给出的数量大于背包总容量除以该类物品体积得到的数量,那么就认为该类物品是取不完的 可以转化为完全背包来做,否则就转化为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]);        }    }}

HDU2159 裸二维背包


5混合背包

看情况组合,部分多重背包在数量足够的情况下可以转化为完全背包,而完全背包又可以转为01背包处理

HDU3591混合背包-经典



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)(注意里面有小错误)




0 0