01背包问题推广———分组法(多重背包问题)(超详细解释)

目录

(1)不能用完全背包问题的理由

(2)基础代码

(3)优化思路

(1)证明用2分组的合理性

(2)最后使用01背包优化删去一个维度

总结


原题

(1)不能用完全背包问题的理由

首先我们来题干

乍一看很像完全背包问题,但是这里出现了一个限制,就是物品的数量是给定的

如果现在用完全背包问题的优化思路就会发现

f[i][j]=max(f[i-1][j],f[i-1][j-v]+w ...f[i-1][j-k*v+k*w)

f[i][j-v]=max(        f[i-1][j-v] .... f[i-1][j-k*v+k*w) f[i-1][j-(k+1)*v+(k+1)*w)

在这个时候由于数量k是给定的,所以在每次比较的时候都要保证有k项,而如果数量k并未给定,
就有这个问题,现在f[i][j-v]比f[i][j]有一项不相同,所以无法用完全背包问题

(2)基础代码

那我们就只能用01背包去解决 现在对集合的划分就变成了

对于第i类物品是否取k个 那么这时候我们就要加入三重循环

第一层循环i类物品 第二层循环j体积 第三次循环k的个数


int n, m;
int v[N], w[N], s[N];//分别表示体积 价值 数量
int f[N][N];

int main()
{
    cin >> n >> m; //表示n类 最大体积m

    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i] >> s[i];

    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ )
            for (int k = 0; k <= s[i] && k * v[i] <= j; k ++ )
                f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k);
}

(3)优化思路

现在虽然解决了问题,但用上了三重循环太慢了

我们现在来考虑如何优化

(1)证明用2分组的合理性

我们现在要用一种跟快的方式去表示出k个数

对于一个整数而言 以13为例

它的二进制就是1101

那么它就可以表示为

1000 加上     0100 加上  0000 加上  0001

就是2的三次 加2的二次                加二的0次 

所以任何一个正整数就可以表示为

s=1+2+4+.....+2^k +c  

1~2的k次 就可以表示1~2^(k+1) -1之间的任何一个数

以200为例

它可以拆分为 200=1+2+4+8+16+32+64+63

1+2+4+8+16+32+64可以表示1~127 

63与前者相加以后就可以表示64~200之间任意一个数

两个区域合并以后就可以表示1~200之间任意一个数

所以我们写出如下进行分组的代码

 int cnt=0;//标记分到第几组了
    for(int i=1;i<=n;i++)
    {
        int a,b,s;//分别表示第i个物品的体积,价值,数量
        scanf("%d%d%d",&a,&b,&s);

        int k=1;//k用来表示数量  每次乘上2 表示一组
        while(k<=s)
        {
            cnt++;
            v[cnt]=a*k;//第cnt组新的体积与价值
            w[cnt]=b*k;
            s-=k;//有k数量的i物品已经被分走了
            k*=2;
        }
//这就是我们举例里面的情况
//200里面127的数量已经被分走了 还剩下63 把剩下的63作为一组
        if(s>0) 
        {
            cnt++;
            v[cnt]=a*s;
            w[cnt]=b*s;
        }
        
        
    }

(2)最后使用01背包优化删去一个维度

         n=cnt;//注意现在的n n种物品都已经被分好组了 现在用总组数去更新n
       for (int i = 1; i <= n; i++)//n类物品
        for (int j = m; j >= v[i]; j--)//最大体积m
            f[j] = max(f[j], f[j - v[i]] + w[i]);

总结

由此我们可以看出所谓的01背包问题,就是在更新方式上去做两种判断:选或者不选

所以多重背包就也理解为 对我给了那么多给定数量的i类物品,要求我去得到一个表达式

使其可以表达任意数量的物品,

      这时候更新方式就变成了 : 我是否选择用这个表达式去计算数量 是或者否

题目来源acwing 

模板来源:闫学灿
链接:https://www.acwing.com/activity/content/code/content/57785/
来源:AcWing

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值