混合背包,分组背包,二维费用的背包问题

混合背包,二维费用的背包问题,分组背包

混合背包

混合背包就是将01背包,完全背包,多重背包混合起来的背包问题。也就是说,有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解呢?

首先考虑01背包与完全背包的混合

考虑到在01背包和完全背包中的代码只有一处不同,故如果只有两类物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个物品应用转移方程时,根据物品的类别选用顺序或逆序的循环即可,复杂度是O(VN)。伪代码如下:

for(i=1;i<=n;i++){
    if 第i件物品只能取一次
    for(int v=V;v>=w[i];v--)
      dp[v]=max(dp[v],dp[v-w[i]]+v[i]);
  else if 第i件物品可以取无限次
    for(int v=w[i];v<=V;v++)
      dp[v]=max(dp[v],dp[v-w[i]]+v[i]);
}
再加上多重背包

多重背包可以拆成01背包,我们通常用二进制来拆,将每种物品分成 l o g 2 n [ i ] log_2n[i] log2n[i]个物品,再套用上面的代码。复杂度O(V* l o g 2 N log_2N log2N)

具体代码如下:

//先将多重背包预处理
int cnt=1;
cin>>n>>V;
for(int i=1;i<=n;i++){
    int m=1;
    cin>>a>>b>>s;//a表示当前物品的代价,b表示价值,s表示数量(-1表示无穷)
    if(s!=-1){
        while(m<=s){
            s-=m;
		    num[cnt]=1,w[cnt]=m*a,v[cnt++]=m*b;//分成用每次乘二表示的数打包成一件物品成一项加入数组
		    m*=2;
	    }
        if(s>0){
		    num[cnt]=1,w[cnt]=s*a,v[cnt++]=s*b;//剩下的单成以项,加入数组
	    }
     }
    else
        num[cnt]=s,w[cnt]=a,v[cnt++]=b
}
//再判断背包类型分别进行dp
for(i=1;i<=cnt;i++){
  if(num[i]==1)
    for(int j=V;j>=w[i];j--)
      dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
  else
    for(int j=w[i];j<=V;j++)
      dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}

https://www.acwing.com/problem/content/7/ 混合背包模板题

二维费用的背包问题

二维费用的背包问题是指:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为a[i]和b[i]。两种代价可付出的最大值(两种背包容量)分别为W和U。物品的价值为v[i]。

解题思路:

费用加了一维,只需状态也加一维即可。设dp[i][w][u]表示前i件物品付出两种代价分别为w和u时可获得的最大价值。

状态转移方程:

dp[i][w][u]=max(dp[i-1][w][u],dp[i-1][w-a[i]][u-b[i]]+v[i]) (01背包)

可以发现f[i][][]只与f[i-1][][]有关,如01背包所讲的降维优化,这里可以将三维转化为二维。

for(int i=1;i<=n;i++){
    for(int w=V;w>=a[i];w--)
        for(int u=U;u>=b[i];u--)
            dp[w][u]=max(dp[w][u],dp[w-a[i]][u-b[i]]+v[i]);
}

以上是01背包的思路,同理可以推出完全背包和多重背包。

//完全背包
for(int i=1;i<=n;i++){
    for(int w=a[i];w<=W;w++)
        for(int u=b[i];u<=U;u++)
            dp[w][u]=max(dp[w][u],dp[w-a[i]][u-b[i]]+v[i]);
}

//多重背包
//先将多重背包预处理
int cnt=1;
cin>>n>>W>>U;
for(int i=1;i<=n;i++){
    int m=1;
    cin>>w>>u>>v>>s;//w表示当前物品的代价1,u表示当前物品的代价2,v表示价值,s表示数量
    while(m<=s){
        s-=m;
		a[cnt]=m*w,b[cnt]=m*u,c[cnt++]=m*v;//分成用每次乘二表示的数打包成一件物品成一项加入数组
		m*=2;
	}
    if(s>0){
		a[cnt]=s*w,b[cnt]=s*u,c[cnt++]=s*v;//剩下的单成以项,加入数组
	}
}
//再用01背包进行dp
for(int i=1;i<=cnt;i++){
    for(int v=V;v>=a[i];v--)
        for(int u=U;u>=b[i];u--)
            dp[w][u]=max(dp[w][u],dp[w-a[i]][u-b[i]]+v[i]);
}
物品总个数的限制

有时,“二维费用”的条件是以这样一种隐含的方式给出的:最多只能取M件物品。这事实上相当于每件物品多了一种“件数”的费用,每个物品的件数费用均为1,可以付出的最大件数费用为M。换句话说,设dp[w][m]表示付出费用w、最多选m件时可得到的最大价值,则根据物品的类型(01、完全、多重)用不同的方法循环更新,最后得到的dp[W][M]即为答案。

https://www.acwing.com/problem/content/8/ 二维费用的背包问题模板题

分组背包

问题:

有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是v[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

思路:

这个问题变成了每组物品有两种策略:是选择本组的某一件,还是一件都不选。也就是说设dp[k][w]表示前k组物品花费费用w能取得的最大价值,则有:

dp[k][w]=max(dp[k-1][w],dp[k-1][w-c[i]]+v[i]|物品i属于第k组)

同样利用降维思想将二维转化为一维,使用一维数组的代码如下:

vector<int>num[N];//num[i]表示第i组有哪些物品

for(int i=1;i<=m;i++)//m为分组个数
    for(int j=W;j>=0;j--)
        for(int k=0;k<num[i].size();k++)
            if(j>=c[num[i][k]])
                dp[j]=max(dp[j],dp[j-c[num[i][k]]]+v[num[i][k]]);

注意这里的三层循环的顺序。 '‘for(int j=W;j>=w[i];j–)’'这一层循环必须在“for(int k=0;k<num[i].size();k++)”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。

分组的背包问题将彼此互斥的若干物品称为一个组,这建立了一个很好的模型。不少背包问题的变形都可以转化为分组的背包问题(比如下次会讲到的有依赖的背包问题),由分组的背包问题进一步可定义“泛化物品”的概念,十分有利于解题。

https://www.acwing.com/problem/content/9/ 分组背包模板题

小结

混合背包就是将01背包,完全背包和多重背包混合在一起的背包问题。解题思路就是先将多重背包二进制拆分01背包,之后再遍历每个物品。根据物品类型选择正序dp(完全背包)或逆序dp(01背包)。伪代码如下:

for(i=1;i<=n;i++){
  if 第i件物品只能取一次
      逆序dp
  else if 第i件物品可以取无穷次
      顺序dp
  else
      先二进制分解再逆序dp
}

二维费用的背包问题就是增加物品的另一种费用,也就是在原来dp的基础上增加了一维,所以称为二维费用。解题思路就是在dp的时候多一重循环。实现代码如下:

for(int i=1;i<=n;i++){
    for(int w=V;w>=a[i];w--)
        for(int u=U;u>=b[i];u--)
            dp[w][u]=max(dp[w][u],dp[w-a[i]][u-b[i]]+v[i]);
}

分组背包就是将物品分成多组,每组最多只能取一个的背包问题。其实分组背包可以看成是01背包进阶版,就是将01背包中的每个物品当成一个背包。在01背包中,我们以每一件物品作为动态规划的每一阶段,但是在分组背包中我们要以每一组作为每一阶段。
其实很简单,伪代码如下。

for(int i=1;i<=k;i++)
    for(int c=v;c>=0;c--)
        for( each 物品j in 第i组 )
            if(c>=w[j])
                f[c]=max(f[c],f[c-w[j]+v[j]]);

先枚举每一组,再枚举体积,最后枚举每组中的物品。记住顺序一定不能乱!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值