背包问题

问题:一共有N件物品,第i(i从1开始)件物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限W的情况下,能够装入背包的最大价值是多少?
通常遇到这种问题最容易想到的是暴力递归的方式

暴力递归

如果使用暴利递归的方式一般把该问题拆解成两步,然后递归调用:

既然是有n件物品,我们编上号从0~i,那么对于第i个商品就有如下两种可能:
1、第i件商品被选用了;
2、第i件商品没被选用

//采用暴利递归的方式
 class func beibaoQuestion(v:[Int],w:[Int],maxWeight:Int) -> Int{
      //边界条件判断
        if v.count != w.count || v.count == 0 {
            return 0
        }
        return process(v: v, w: w, index: 0, rest: maxWeight)
    }
    //递归过程
    class func process(v:[Int],w:[Int],index:Int,rest:Int) -> Int{
        //如果超过最大背包容量,那么我就要告诉我的前一步递归者,我已经超出容量了
        if rest < 0 {
            return -1
        }
        if index == v.count {
            return 0
        }
        
        //第index号商品我不要
        let p1 = process(v: v, w: w, index: (index + 1), rest: rest)
        
        //第index号商品我要
        //第index之后的商品最大价值为:
        var p2:Int = 0
        let next = process(v: v, w: w, index: (index + 1), rest: rest - w[index])

      /**
         最大容量如果是15
         编号  0  1  2  3   4
         v    3  1  4  7   3
         w    5  6  3  19  12
         
              不 不 不  要
         
         如上表所示,加入在走到编号3的位置时,如果要这个商品,那么再次递归的时候,剩余的背包重量将会是15-(5+6+3)- 19 < 0
         也就是说,如果选择了该index号商品,就会导致背包容量直接爆表,所以这个商品是无效的
         */
        //对于超出容量的处理
        if next != -1 {
            p2 = v[index] + next
        }
        print(p2)
        return p1 > p2 ? p1 : p2
    }

采用暴利递归的方式可以发现,其时间复杂度为O(2^N),数据量越大,递归时间越长;
下面改为动态规划的方式:

class func beibaoQuestion2(v:[Int],w:[Int],maxWeight:Int) -> Int{
        if v.count != w.count || v.count == 0 {
            return 0
        }
        //由暴力递归的过程可以发现,v和w是固定的参数,只有索引位置index和剩余背包容量rest为变化项,所以构建bp表也是根据这两个来创建;
        /**
         最大容量如果是15
         编号  0  1  2  3   4
         v    3  1  4  7   3
         w    5  6  3  19  12
        |
        改成bp表
         
         重量变化范围    -1       0   1  2   3   4   5   6   7   8   9   10  11  12  13  14  15
         
                            0
                            1
                            2
                            3
                            4
                            5
         
         根据递归过程发现
         if index == v.count {
             return 0
         }
         当index与物体数量+1相等时返回的都是0
         所以第length位置的容量都为0
         **/
        
        
        
        let length = v.count
        var dp:[[Int]] = [[Int]](repeating: [Int](repeating: 0, count: (maxWeight + 1)), count: (length + 1))
        
        for index in (0..<length).reversed() {
            for rest in (0...maxWeight) {

                let p1 = dp[index + 1][rest]
   
                var p2:Int = 0
                let next = (rest - w[index]) < 0 ? -1 : dp[index + 1][(rest - w[index])]
                if next != -1 {
                    p2 = v[index] + next
                }
                print(p2)
                dp[index][rest] = (p1 > p2 ? p1 : p2)
            }
        }
        return dp[0][maxWeight]
    }

这种方式的动态规划时间复杂度为O(n*W),空间复杂度也为O(n*W)

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容