python 01背包问题(详细注释)

01背包问题是背包问题中的较简单情况。假定一背包和若干物品,物品总体积超过背包容量。已知背包容量,已知各物品的体积和价值,要求选取若干物品放入背包,使放入物品总价值最大。
通常用动态规划解决,列状态转移方程,画状态转移矩阵。

首先定义01背包函数。输入中,p为顺次表示物品价值的列表,w为顺次表示物品体积的列表;v为背包容量。

def knapsack(p, w, v):
    #n来存储物件的总数目
    n = len(p)
    #lists存储应该放入背包的物品,arr为状态转移矩阵,它有v+1列,n+1行。
    #arr[0][0]表示在0件物品可选,选入物品体积总和不超过0的状态下,做出最优选择时放入物品的最大价值。
    #arr[i][j]表示在可选前i个物品,选入物品体积总和不超j的状态下,做出最优选择时放入物品的最大价值。
    lists,arr = [],[[0] * (v + 1) for _ in range(n + 1)]
    #从第1个到第n个物件
    for i in range(1, n + 1):
        #对每一种背包限容可能
        for j in range(1, v + 1):
            #当前物品体积比限容大,则不可选这个物品。
            #若当前物件体积比当前限容小
            if w[i - 1] <= j:  
                # p[i-1]为当前物品价值,w[i-1]为当前物品体积,现对当前物品是否放入进行决策
                #研究第i个物品时,根据循环顺序,上面的行已被填满。也即前i-1件物品可选时,任一限容下的最优价值已知。
                #第i个物品无非有选和不选两种情况。
                #若选第i件物品,说明可能第i件物品价值高,舍弃之前中的某些件物品选上它得到的总价值更高(也有未舍弃的可能)
                #若不选第i件,说明可能以前有某物品价值太高但占一些体积,如果选上第i件就装不下了,舍弃已选物品又得不偿失。
                #选择第i件对应的总价值,
                #是第i件物品价值加上限选前i-1件、以当前研究状态容量减去第i个物品体积所得的数为可容纳最大总体积的那个状态。
                #不选第i件对应的物品总价值显然与相同限容下可选前i-1件得到的最优价值相同。
                #两者取较大值,便是该状态(可选前i件、限容为j)对应的最大价值
                arr[i][j] = max(arr[i - 1][j], p[i - 1] + arr[i - 1][j - w[i - 1]])
            #当前物品超过当前背包限容,就和可选前i-1件物品时的最大价值相等
            else: 
                arr[i][j] = arr[i - 1][j]

现在,矩阵已经建成。但要将所选物品对应下标输出,需要更多操作。


    #输出选择结果
 
    #将背包容量赋给remain
    remain = v
    #从下标为i的最后一个物品开始寻找。从前往后找找到的很可能不是最优解。(思考原因)
    for i in range(n, 0, -1):
        #如果选择了第i个物品,那么第i行某列的最大价值(也就是那个位置存储的元素)一定比同一列上一行对应的最大价值大(思考相等的可能,这里不讨论)
        #最优解中,在原来的排序中下标最大的物品一定锁定在矩阵的最后一列寻找。
        #在最后一列,从下至上,如果第i行元素比第i-1行元素大,说明最后一步选择的是第i件物品。
        #为了找到最优解中下标倒数第二的物品,应该从第i-1行向上寻找。
        #这时,问题缩小为在【可选前i-1件物品,总体积不超过背包原容量减去第i件物品体积】这种条件下寻找最优解
        #所以最优解中下标倒数第二的物品在列数为【总列数减去第i件物品体积所得到的数】,行数为i-1中寻找即可。
        #以此类推,直至从最后一个物品判断到第一个结束
        if arr[i][remain] > arr[i - 1][remain]:
            lists.append(i)  # i为当前物品的编号
            remain -= w[i - 1]  # 容积减去已经找到的物品,再次寻找
    
    #矩阵的右下角的元素就是最大价值
    print(arr[-1][-1])
    #由于从后往前依次加入的最优解,最好倒序输出
    for i in range(len(lists)):
        print(lists[len(lists) - i-1],end = ' ')

测试:

a = int(input())#此参数未用到
v = int(input())#背包容量
w = list(map(int,input().split()))#物品体积
p = list(map(int,input().split()))#物品价值
knapsack(p,w,v)

在这里插入图片描述

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一份Python语言实现的递归回溯法求解0/1背包问题的完整代码,已经加上了注释说明: ```python # 递归回溯法求解0/1背包问题 # 功能:给定物品重量列表和价值列表,背包容量,求能装入背包的最大价值 def knapsack(w, v, c): """ :param w: 一个列表,表示物品的重量 :param v: 一个列表,表示物品的价值 :param c: 背包的容量 :return: 能装入背包的最大价值 """ n = len(w) # 物品个数 memo = [[-1] * (c + 1) for _ in range(n)] # 备忘录,用于记录之前计算过的状态 def dp(i, j): """ :param i: 当前考虑到第i个物品 :param j: 当前背包容量为j :return: 能装入背包的最大价值 """ if i == n: # 物品已经考虑完了 return 0 if memo[i][j] != -1: # 如果之前计算过,直接返回备忘录的值 return memo[i][j] res = dp(i + 1, j) # 不装第i个物品 if j >= w[i]: # 如果当前背包容量能装下第i个物品 res = max(res, v[i] + dp(i + 1, j - w[i])) # 装第i个物品 memo[i][j] = res # 记录备忘录 return res return dp(0, c) # 从第0个物品开始考虑,背包容量为c # 示例 w = [1, 2, 3] # 物品重量列表 v = [6, 10, 12] # 物品价值列表 c = 5 # 背包容量 print(knapsack(w, v, c)) # 输出结果:22 ``` 注释已经比较详细了,主要思路就是用递归回溯法,定义一个dp函数来求解能装入背包的最大价值,同时使用备忘录memo记录之前计算过的状态,避免重复计算。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值