• 动态规划 | 01背包问题 | leecode刷题笔记


    跟随carl代码随想录刷题
    语言:python


    01背包问题

    动态规划从小问题着手,逐步解决大问题。解决的子问题(小问题)会帮助我们解决大问题。
    动态规划五部曲

    1. 确定dp数组(dp table)以及下标的含义

      • dp[i][j]表示从下标为(0, i)的物品里面任意取,放进容量为j的背包,价值总和是d[i][j]
    2. ⭐️确定状态转移公式(递推公式)

      • dp[i][j] = max(dp[i-1][j], dp[i-1][j - weight[i] + value[i]]) # 包括两个方向
    3. dp数组如何初始化

      • dp[i][0] = 0 # 如果背包重量为0,那价值永远为0
      • for j in range(bagweight): dp[0][j] = value[0] # 当背包容量第一个物体的重量时,第一行价值均为第一个物品的重量
    4. 确定遍历顺序

      • 从左到右一层一层遍历
      • 先遍历物品
    5. 举例推导dp数组

    画图说明

    • 初始化
      在这里插入图片描述

    • 动态规划与遍历
      在这里插入图片描述

    代码

    def test_2D_bag_problem1(bag_size, item_weight, item_value) -> int:
        rows, cols = len(item_weight), bag_size + 1
        dp = [[0 for _ in range(cols)] for _ in range(rows)]
    
        # 初始化dp数组
        # 初始化第一列
        for i in range(rows):
            dp[i][0] = 0
        first_item_weight, first_item_value = item_weight[0], item_value[0]
        # 初始化第一行
        for j in range(1, cols):
            if first_item_weight <= j:  # 如果物品的重量比背包容量小,那么就添加其价值
                d[0][j] = first_item_value
    
        # 更新dp数组:先遍历物品,再遍历背包
        for i in range(1, len(item_weight)):
            cur_weight, cur_val = item_weight[i], item_value[i]
            for j in range(1, cols):
                if cur_weight > j:  # 如果当前物品的重量大于背包容量
                    dp[i][j] = dp[i - 1][j]  # 不装,保持原价值
                else:
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cur_weight] + cur_val)
                    # dp[i - 1][j - cur_weight]表示:当容量等于[j - cur_weight]时的价值
                    # dp[i - 1][j - cur_weight] + cur_val表示:装入现有物品之后的价值
        print(dp)
    
    if __name__ == "__main__":
        bag_size = 4
        item_weight = [1, 3, 4]  # 各个物品的重量
        item_value = [15, 20, 30]
        test_2D_bag_problem1(bag_size, wight, value)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    滚动数组 —— 一维dp数组

    • 上述过程也可以用一维数组来做,也就是:

      • 如果值没有变,那就直接拷贝,
      • 如果值变了,那就更改为新值。
    • 遍历顺序:背包容量从大到小,保证物品i只被放入一次。

      • 因为一维数组涉及到覆盖问题,所以只能背包容量从大到小遍历。
        • 两层遍历,第一层是对各个物品的遍历,第二层是对背包容量的逆序遍历。
          • 注意:这两层不能反!如果写反了意味着每个dp[j]时,背包只放入一件物品。
          • 但我们其实是想放入多个物品的。
        • 遍历范围是(背包容量, 当前物品的重量, -1(逆序)),也就是说,如果背包容量<当前物品的重量,就不遍历(不做更改),保留原值
        • 【下图中画×表示不遍历】
      • 而二维数组不涉及覆盖问题,所以可以背包容量从小到大遍历

    在这里插入图片描述

    def test_2D_bag_problem1() -> int:
        bag_size = 4
        item_weight = [1, 3, 4]  # 各个物品的重量
        item_value = [15, 20, 30]
        
        # 初始化:全为0
        dp = [0] * (bag_size + 1)
    
        # 先遍历物品,再遍历背包容量
        for i in range(len(item_weight)):
            for j in range(bag_weight, item_weight[i] - 1, -1):
                # 递归公式
                dp[j] = max(dp[j], dp[j - item_weight[i]] + item_value[i])
    
        print(dp)
    
    test_1_wei_bag_problem()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    创建对象在堆区如何分配内存
    如何把thinkphp5的项目迁移到阿里云函数计算来应对流量洪峰?
    Word第一课
    18 矩阵置0
    vue3【计算属性与监听-详】
    怎样优雅地增删查改(五):按组织架构查询
    数据库备份
    数据库连接池
    安卓页面绘制流程(3)Window注册
    小程序商城如何成为商家的标配?
  • 原文地址:https://blog.csdn.net/qq_44250700/article/details/126373623