背包问题

背包问题是一种动态规划问题

问题是给你背包容量和物品的花费、价值,不一定要将背包装满,问怎样装才可以使价值最大

背包问题可分为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是由上一个状态得到。

初始条件:
不一定装满的初始状态:f[0-v]=0(要求最大值,初始化最小)
恰好装满的初始状态:f[0]=0,f[1~v]=-∞(状态不合法,0件物品无法恰好装满任意非0容量)
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  
       

 
 
 
更新A时,要么从C点,要么从D或B点更新是吧,
状态转移方程是:f[j]=max(f[j],f[j-w[i]]+c[i])//放或者不放的情况比较
就是即使是从B点更新过来也无所谓了,因为你物品有无限个嘛!
初始条件:f[0]=0;
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

无愧于心,不困于情。
原文地址:https://www.cnblogs.com/SUMMER20020929/p/9764216.html