动态规划法求解背包问题原理思路

一、什么是背包问题

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

 

二、动态规划法求解0/1背包问题思路

  每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是O(2^n),这里的n表示物品数量,这是指数级别的复杂度,显然是不可接受的。利用动态规划法求解能大大降低时间复杂度。

       物品从0开始编号,用i表示,背包的容量用j表示,确定动态规划的数组为dp[i][j], 表示从下标为0~i的物品里任意取,放进容量为j的背包,价值总和最大是多少。那么在每次遍历到当前物品i的时候,无非就是两种情况,一种就是放了物品i后价值最大,一种是不放物品i价值最大,所以放不放物品i就看哪个价值最大即可,如下所示:

 三、案例解析

    假设有3个物品,分别编号为0,1,2,求解的目标是在背包容量为4的情况下,所能达到的最大价值是多少?如下图:

整体思路:先遍历物品从0~2,再遍历背包容量从0~4,整个遍历完就知道在背包容量为4的情况下的,最大价值是多少了。

二维数组表示的代码:

def test_2_wei_bag_problem1(bag_size, weight, value) -> int: 
    rows, cols = len(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 = weight[0], value[0]
    for j in range(1, cols):     
        if first_item_weight <= j: 
            dp[0][j] = first_item_value

    # 更新dp数组: 先遍历物品, 再遍历背包. 
    for i in range(1, len(weight)): 
        cur_weight, cur_val = weight[i], value[i]
        for j in range(1, cols): 
            if cur_weight > j: # 说明背包装不下当前物品. 
                dp[i][j] = dp[i - 1][j] # 所以不装当前物品. 
            else: 
                # 定义dp数组: dp[i][j] 前i个物品里,放进容量为j的背包,价值总和最大是多少。
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cur_weight]+ cur_val)

    print(dp)

一维数组表示的代码

def test_1_wei_bag_problem():
    weight = [1, 3, 4]
    value = [15, 20, 30]
    bag_weight = 4
    # 初始化: 全为0
    dp = [0] * (bag_weight + 1)

    # 先遍历物品, 再遍历背包容量
    for i in range(len(weight)):
        #倒序遍历,避免使用本轮的刚刚更新的j-weight[i],倒序的时候就只能使用上一轮的。容量j倒序只需要遍历与当前物品i一样的重量即可,
再小也没有必要遍历了,肯定放不下i
for j in range(bag_weight, weight[i] - 1, -1): # 递归公式 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) print(dp)

 

四、可以选择用贪心算法来解决0/1背包问题吗?

    既然每一种物品都有价格和重量,我们优先挑选那些单位价格最高的是否可行呢?比如在下图中,背包的容量是50kg,我们有3种物品,他们的重量和价格分别是10, 20, 30 kg和60, 100, 120。

      那么按照单位价格来算的话,我们最先应该挑选的是价格为60的元素,选择它之后,背包还剩下50 - 10 = 40kg。再继续前面的选择,我们应该挑选价格为100的元素,这样背包里的总价值为60 + 100 = 160。所占用的重量为30, 剩下20kg。因为后面需要挑选的物品为30kg已经超出背包的容量了。我们按照这种思路能选择到的最多就是前面两个物品,总价值160。

       按照我们前面的期望,这样选择得到的价值应该是最大的。可是由于有一个背包重量的限制,这里只用了30kg,还有剩下20kg浪费了。这会是最优的选择吗?我们看看所有的选择情况:

 

  对于0-1背包问题,贪心选择之所以不能得到最优解是因为它无法保证最终能将背包装满,部分闲置的背包空间使每公斤背包空间的价值降低了

       但是对于背包问题是可以选择贪心算法的背包问题:与0-1背包问题类似,所不同的是在选择物品i装入背包时,可以选择物品的一部分,而不一定要全部装入背包对于背包问题而言,执行尽量装入最大单位重量价值的贪心选择即可

       事实上,在考虑0-1背包问题时,应比较选择该物品和不选择该物品所导致的最终方案,然后再作出最好选择。由此就导出许多互相重叠的子问题。这正是该问题可用动态规划算法求解的另一重要特征。

 

参考链接:代码随想录

posted @ 2022-01-09 20:13  光彩照人  阅读(931)  评论(0)    收藏  举报