背包问题是一种动态规划问题
问题是给你背包容量和物品的花费、价值,不一定要将背包装满,问怎样装才可以使价值最大
背包问题可分为01,完全,多重,混合,分组(互斥),二维费用,依赖
一:01背包:
!!!每个物品只有一件,而且v的列举需要倒序
将n个物品放入容量为v的背包,每件物品花费w、价值c都已给出。
每个物品只有一件,可以选择放或者不放
用f[j]表示物品放入j的容量时可获得的最大价值
f[j]=max(f[j],f[j-w[i]]+c[i])//放或者不放的情况比较
有个大大的注意点:第二层循环体积时应该从v-0!!
为什么?我们画个图来解决
横行是物品,纵列是容量
w[i] j-w[i] j v
E | D | C | F |
B | A | ||
这是一个一维数组
根据状态转移方程
若-->更新,在更新A时,来源于B或者C,B是这一轮更新的值,C是上一轮更新的值,反正就是不能保证A是由上一个状态得到。
若<--更新,在更新A时,来源于B或者C,B由于还没有被更新,所以相当于B=D,也就是B还是上一轮的值,那就保证了A是由上一个状态得到。
初始条件:
1 for(int i=1;i<=n;i++){ 2 for(int j=v;j>=w[i];j--){ 3 f[j]=max(f[j],f[j-w[i]]+c[i]); 4 } 5 }
二.完全背包:
大致和01背包一样,不同点在于每件物品有无限件
第二层循环要顺序0-v了
为啥子?咱又来画个图
w[i] j-w[i] j v
E | D | C | F |
B | A | ||
1 for(int i=1;i<=n;i++){ 2 for(int j=w[i];j<=v;j++){//重量是必须大于这个物品重量的 3 f[j]=max(f[j],f[j-w[i]]+c[i]); 4 } 5 }
三.多重背包:
大致和01一样,只是每种物品取的数量有限
转化为01背包即可:把一个物品里的取n件物品看成,取若干件不同的物品
若干件?总不能把他看成n件吧!那这个数据量好大哦。
其实可以应用二进制,就是说:
13=1+2+4+6
这4个数字可以构成1-13中每个数字,所以问题也没有太复杂,而且还转化成功了
1 //比如 13=1+2+4+6 2 //nu是最多取几件 3 t=1; 4 while(nu>=t){ 5 w[++n]=pr*t;//这一堆组成有几个物品 6 //这个物品的花费和价值要乘几 7 //相当于捆绑销售 8 c[n]=va*t; 9 nu-=t; 10 t=t*2; 11 } 12 //13中的6是在这里分 13 if(nu!=0){ 14 w[++n]=pr*nu; 15 c[n]=va*nu; 16 }
四.混合背包
01+完全+多重
分类讨论即可
第二层for循环时,完全是0-v,01+多重是v-0
所以第二层循环时分类讨论就好
就加一个check数组判断他是01多重还是完全
1 for(int i=1;i<=n;i++){ 2 if(check[i]==0) for(int j=w[i];j<=v;j++){ 3 f[j]=max(f[j],f[j-w[i]]+c[i]); 4 } 5 else for(int j=v;j>=w[i];j--){ 6 f[j]=max(f[j],f[j-w[i]]+c[i]); 7 } 8 }
五.分组背包
物品被划分为若干组,每一组内物品互斥,最多选一件
在最内层加一个循环,循环的是这个组内的物品。
在最内层加可以保证一个组内只取一件物品
1 for(int i=1;i<=t;i++){//组数 2 for(int j=v;j>=0;j--){//容量 3 for(int k=1;k<=a[i][0];k++){//a[i][0]表示这个组内的物品个数 4 if(j>=w[a[i][k]]){//容量肯定要大于这个物品才行 5 f[j]=max(f[j],f[j-w[a[i][k]]]+c[a[i][k]]); 6 } 7 } 8 } 9 }
六.二维费用的背包问题
每件物品不止一个花费
就像修房子需要花费人力和财力
那两个花费的最大值就是两个背包容量
类比前面,花费是v时有一层循环(不算循环物品个数/组数),dp数组为一维。
那两个花费就有两层循环,dp数组为二维
有时候物品总个数也是个限制,这个是隐含条件,也算作一个背包容量,每件物品个数费用为1