【Algorithm】彻底解释三种背包问题

背包问题

背包问题是一类经典的动态规划问题,本文先对背包问题的几种常见类型作一个总结,期望可以用一套框架解决背包问题。

这里先简单说说动态规划。动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果。但不同的是,分治法在子问题和子子问题等上被重复计算了很多次,而动态规划则具有记忆性,通过填写表把所

有已经解决的子问题答案纪录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到了。

最优性原理是动态规划的基础,最优性原理是指“多阶段决策过程的最优决策序列具有这样的性质:不论初始状态和初始决策如何,对于前面决策所造成的某一状态而言,其后各阶段的决策序列必须构成最优策略”。

常见背包问题可分为:

  • 01背包问题:有N件物品和一个容量为V的背包,第i件物品消耗的容量为Wi,价值为Vi,求解放入哪些物品可以使得背包中总价值最大。特点:每个物品只有一件供你选择放还是不放。
  • 完全背包问题:有N种物品和一个容量为V的背包,每种物品都有无限件可用,第i件物品消耗的容量为Wi,价值为Vi,求解放入哪些物品可以使得背包中总价值最大。特点:每个物品可以无限选用。
  • 多重背包问题:有N种物品和一个容量为V的背包,第i种物品最多有Mi件可用,每件物品消耗的容量为Wi,价值为Vi,求解入哪些物品可以使得背包中总价值最大。特点 :它与完全背包有类似,每个物品都有了一定的数量。

三种背包问题都有一个共同的限制,那就是背包容量,背包的容量是有限的,这便限制了物品的选择,而三种背包问题的共同目的,就是让背包中的物品价值最大。

不同的地方在于物品数量的限制,01背包问题中,每种物品只有一个,对于每种物品而言,便只有选和不选两个选择。完全背包问题中,每种物品有无限多个,所以可选的范围要大很多。在多重背包问题中,每种物品都有各自的数量限制。

三种背包问题虽然对于物品数量的限制不一样,但都可以转化为01背包问题来进行思考。

在完全背包问题中,虽然每种物品都可以选择无限个,但由于背包容量有限,实际上每种物品可以选择的数量也是有限的,那么将每种物品都看做是 V/Wi 种只有一件的不同物品,就转换成了01背包问题。对于多重背包也是如此,只是每种物品的膨胀数量变成了min{Mi, V/Wi}。

所以说,01背包问题是所有背包问题的基础,弄懂了01背包问题后,完全背包和多重背包问题也可以迎刃而解。

下面给出三种背包问题的状态转移方程,便于更好的理解它们之间的联系:

首先给出一些基本概念:有N件物品和一个容量为V的背包。第i件物品的容量是w[i],价值是v[i],求将哪些物品装入背包可使价值总和最大。

  • 01背包的状态转移方程:

二维方程:

f[i][j] = max{f[i-1][j], f[i-1][j-w[i]]+v[i]}

// 伪代码 
for(int i=1; i<=n; i++){
    for(int j=m; j>0; j--){
        if(a[i]<=j)
            f[i][j]=max(f[i-1][j],f[i-1][j-a[i]]+b[i]);
        else
            f[i][j]=f[i-1][j];
    }
}

一维方程:设f[j]表示重量不超过j公斤的最大价值,状态转移方程为:

f[j] = max{f[j], f[j−a[i]]+b[i]}

// 伪代码
for(int i=1; i<=n; i++){       
    for(int j=V; j>=w[i]; j--){
        f[j] = max(f[j], f[j-w[i]]+v[i]);  
    }
}
  • 完全背包的状态转移方程:

设f[j]表示重量不超过j公斤的最大价值,状态转移方程为:

f[j] = maxj{f[j], f[j−a[i]]+b[i]}
// 伪代码:
for(int i=1; i<=n; i++){
    for(int j=a[i]; j<=m; j++){
        f[j] = max(f[j], f[j-a[i]]+b[i]);
    }
}
  • 多重背包的状态转移方程:

设f[j]表示重量不超过j公斤的最大价值,状态转移方程为:

f[j] = max{f[j], f[j−k∗a[i]]+k∗b[i]}
// 伪代码
for(int i=1; i<= n; i++){
    for(int j=m;j>=a[i]; j--){
        for(int k=0; k<=c[i]; k++){
            if(j-k*a[i]<0)
                break;
            f[j] = max(f[j], f[j-k*a[i]]+k*b[i]);
        }
    }
}

1. 01背包问题

01背包问题是最基本的背包问题:有N件物品和一个容量为V的背包,第i件物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限V的情况下,求解放入哪些物品可以使得背包中的总价值最大。

01背包问题的特点是:每种物品仅有一件,可以选择放或不放。
用子问题定义状态:即f[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。则其状态转移方程便是:

f[i][j] = max{f[i-1][j], f[i-1][j-w[i]]+v[i]}

上面的方程是背包问题最基本的方程,这里对它进行一个简单的解释:

“将前 件物品放入容量为 的背包中”这个子问题,若只考虑第 件物品的策略(放或不放),那么就可以转化为一个只牵扯前 i−1 件物品的问题。如果不放第 件物品,那么问题就转化为“前 i−1 件物品放入容量为 的背包中”,价值为 f[i−1][j];如果放第 件物品,那么问题就转化为“前 i−1 件物品放入剩下的容量为 j-w[i] 的背包中”,此时能获得的最大价值就是 f[i−1][j−w[i]] 再加上通过放入第i件物品获得的价值v[i]

以上方法的时间和空间复杂度均为O(V*N),其中时间复杂度已经不能再优化了,但使用一位数组代替二维数组时,空间复杂度可以优化到O(N)。

二维方程:

f[i][j] = max{f[i-1][j], f[i-1][j-w[i]]+v[i]}

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

一维方程: 

f[j] = max{f[j], f[j−w[i]]+v[i]}

// 伪代码
for(int i=1; i<=n; i++){       
    for(int j=V; j>=w[i]; j--){
        f[j] = max(f[j], f[j-w[i]]+v[i]);  
    }
}

 

 

2. 完全背包问题

完全背包与 01 背包不同之处在于每种物品可以有无限多个:有N种物品,每种物品有无限多个,第 i种物品的重量为w[i](i从1开始),价值为v[i]。在总重量不超过背包承载上限W的情况下,求解放入哪些物品可以使得背包中的总价值最大。
01背包问题与完全背包问题的主要区别就是物品是否可以重复选取

 

3. 多重背包

有N种物品和一个容量为V的背包,第i种物品最多有Mi件可用,每件物品消耗的容量为Ci,价值为Wi,求解入哪些物品可以使得背包中总价值最大。 

特点 :它与完全背包有类似点 特点是每个物品都有了一定的数量

背包问题具备的特征:
是否可以根据一个 target(直接给出或间接求出),target 可以是数字也可以是字符串,再给定一个数组 arrs,问:能否使用 arrs 中的元素做各种排列组合得到 target。

 

4. 背包问题的解法

01 背包问题:
如果是 01 背包,即数组中的元素不可重复使用,外循环遍历 arrs,内循环遍历 target,且内循环倒序:

完全背包问题:
(1)如果是完全背包,即数组中的元素可重复使用并且不考虑元素之间顺序,arrs 放在外循环(保证 arrs 按顺序),target在内循环。且内循环正序。
(2)如果组合问题需考虑元素之间的顺序,需将 target 放在外循环,将 arrs 放在内循环,且内循环正序。

 

 

【leetcode】一套框架解决「背包问题」

【简书】三种基本背包问题

【CSDN】背包九讲——全篇详细理解与代码实现

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值