动态规划 | 01背包问题理论 | 代码随想录

文章目录

  • 01背包问题
    • 画图说明
    • 代码
  • 滚动数组 —— 一维dp数组
  • 多重背包

跟随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)

滚动数组 —— 一维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()

多重背包

多重背包的意思就是:每个物品有多件,求当背包容量一定时,能装入的最大价值是多少。我们可以把多件相同的物品摊开来看,其实就是01背包问题。可以转化为01背包问题来做。多重背包问题了解到这儿就可以了,没有对应的题目练习。

def test_multi_pack1():
    '''版本一:改变物品数量为01背包格式'''
    weight = [1, 3, 4]
    value = [15, 20, 30]
    nums = [2, 3, 2]
    bag_weight = 10
    for i in range(len(nums)):
        # 将物品展开数量为1
        while nums[i] > 1:
            weight.append(weight[i])
            value.append(value[i])
            nums[i] -= 1
    
    dp = [0]*(bag_weight + 1)
    # 遍历物品
    for i in range(len(weight)):
        # 遍历背包
        for j in range(bag_weight, weight[i] - 1, -1):
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
    
    print(" ".join(map(str, dp)))

你可能感兴趣的:(算法与数据结构,动态规划,算法)