01背包和完全问题

一、01背包问题

有N件物品和一个最多能装重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

解决方法:声明一个 大小为 m[n][w] 的二维数组,m[ i ][ j ] 表示 在面对第 i 件物品,且背包容量为 j 时所能得到的最大价值 ,那么咱们能够很容易分析得出 m[i][j] 的计算方法,

(1) j < w[i] 的状况,这时候背包容量不足以放下第 i 件物品,只能选择不拿m[ i ][ j ] = m[ i-1 ][ j ]

(2)j>=w[i] 的状况,这时背包容量能够放下第 i 件物品,咱们就要考虑拿这件物品是否能获取更大的价值,

若是拿取,m[ i ][ j ]=m[ i-1 ][ j-w[ i ] ] + v[ i ]。 这里的m[ i-1 ][ j-w[ i ] ]指的就是考虑了i-1件物品,背包容量为j-w[i]时的最大价值,也是至关于为第i件物品腾出了w[i]的空间。

若是不拿,m[ i ][ j ] = m[ i-1 ][ j ] , 同(1)t

到底是拿仍是不拿,比较这两种状况哪种价值最大。

由此能够获得状态转移方程:

if(j>=w[i])
    m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
else
    m[i][j]=m[i-1][j];

例:在使用动态规划算法求解0-1背包问题时,使用二维数组m[i][j]存储背包剩余容量为j,可选物品为i、i+1、……、n时0-1背包问题的最优值。

物品价值v = {8, 10, 6, 3, 7, 2},

物品重量数组w = {4, 6, 2, 2, 5, 1},

背包容量C = 12时对应的m[i][j]数组。

第一列为物品序号i,第二列为背包容量j 

如m[2][6],在面对第二件物品,背包容量为6时咱们能够选择不拿,那么得到价值仅为第一件物品的价值8,若是拿,就要把第一件物品拿出来,放第二件物品,价值10,那咱们固然是选择拿。m[2][6]=m[1][0]+10=0+10=10;依次类推,获得m[6][12]就是考虑全部物品,背包容量为C时的最大价值。

for(int i=1;i<=n;i++){
    for(int j=1;j<=bagweight;j++)
    {
        if(j>=w[i])
            m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);


        else
            m[i][j]=m[i-1][j];
    }
}

该公式的空间复杂度为O(nw), 可以用一位数组进行优化,

在使用二维数组的时候,递推公式:m[i][j] = max(m[i - 1][j], m[i - 1][j - weight[i]] + value[i]);

第i层的值由第i-1层的值而来,所以公式可改为

m[j] = max(m[j], m[j - weight[i]] + value[i]);

因为m[j]的值要由m[j-weight[i]]推倒出,所以要进行逆序遍历

 代码如下:

for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
        m[j] = max(m[j], m[j - weight[i]] + value[i]);

    }
}

二、完全背包问题

有N件物品和一个最多能装重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都可以重复使用,求解将哪些物品装入背包里物品价值总和最大。

这个问题非常类似于 01 背包问题,所不同的是每种物品有无限件。也就是从每种物品的角度考虑,与它相关的策略已并非取或不取两种,而是有取 0 件、取 1 件、取 2件……直至取⌊ W / value[i]⌋ 件等许多种。
按照01背包的思路,可得公式f[i,j] = max(f[i-1, j], f[i-1, j-k*value[i]] + k*value[i])

代码如下:

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

时间复杂度太高,可对公式进行优化

f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w ,  f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)
f[i , j-v]= max(            f[i-1,j-v]   ,  f[i-1,j-2*v] + w , f[i-1,j-3*v]+2*w , .....)
由上两式,可得出如下递推关系: 
                        f[i][j]=max(f[i-1][j], f[i,j-v]+w) 

核心代码可优化为

for(int i = 1 ; i <=n ;i++)
for(int j = 0 ; j <=m ;j++)
{
    if(j-v[i]>=0)
        f[i][j]=max(f[i-1][j],f[i][j-v[i]]+w[i]);
}

完全背包的公式与01背包只有下标不同

f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);//01背包

f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]);//完全背包问题

因此也可以优化为一维数组,因此公式可优化为 f[j] = max(f[j],f[j-v[i]]+w[i]);

核心代码可以写为

 for(int i = 1 ; i<=n ;i++)
    for(int j = v[i] ; j<=m ;j++)//注意了,这里的j是从小到大枚举,和01背包不一样
    {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
    }

01背包更新f[i][j] 用的是 f[i-1][j - v] 和 f[i-1][j],完全背包更新用的是f[i][j - v] 和 f[i - 1]f[j] 在一维中 f[i-1][j] 就是 f[j], 而01背包的f[i-1][j-v]是i-1层 , 更新的时候第i层下标大的用第i-1层下标小的 ,需要逆序枚举,完全背包的f[i][j-v] 就是用的第i层 ,更新的时候第i层下标大的用第i层下标小的,需要正序枚举。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值