[算法]0/1背包问题(python)

0/1背包问题(python)

思路可见这篇博客

问题分析
  1. 是否需要打印最佳路径?
  2. 状态转移矩阵是怎样的?
  3. 若1不要需要且2只与之前的前一次的状态有关(i-1),尤其是这种用列来表示每个物品的时候,可以考虑压缩矩阵
  4. 是否有其他约束如必须选择到几个物品,如果有,则dp的维度会增加,在dp维度2维以上时,优先考虑压缩矩阵,压缩某个维度的时候一定要保存上一次的状态(逆序)
  5. dp数组中的值是否会越界,比如物品的价值特别大?
步骤
  1. 构建dp数组
  2. 寻找最佳路径
dp数组
  1. dp数组结构: shape(len(背包容量),(len(物品数量)))
  2. dp[x][y]代表当前的容量y下,只考虑前x个物品所能获得的最大价值
  3. 初始化数组可以选择增加全0的一行一列,
数组压缩
  1. 压缩后一定要倒着更新数组,正着更新会覆盖掉上一次的状态
  2. dp[y] 等于前y个物品的最大价值,dp[x][y]在未更新之前记录的是上一次的状态,可以理解成dp[x-1][y]
  3. 打印循环中压缩数组可以发现其组成了压缩前的数组,换言之,数组压缩通过倒着更新数组的方式巧妙的利用了之前的状态,并且完成了更新.
  4. 压缩后无法找最佳路径.
  5. 更新前dp[i][j],更新后dp[i’][j’],当满足i’>i,j’>j即可逆序压缩,
    在本例中dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - arr[i].w] + arr[i].v),i>i-1 j>[j - arr[i].w],这意味着当前值只跟他正上边,或者左上方的值有关
python代码实现
def find(i, j, dp, arr):
    if i < 0:
        return
    if dp[i][j] == dp[i - 1][j]:
        find(i - 1, j, dp, arr)
    else:
        print(i)
        find(i - 1, j - arr[i].w, dp, arr)


def get_dp(cap, arr):
    dp = [[0] * (cap + 1) for _ in range(len(arr))]  # 行数: 物品数+1 列数:背包重量+1
    # print(np.array(dp))
    for i in range(1, len(dp)):  # 行
        for j in range(1, len(dp[i])):  # 列
            if arr[i].w > j:  # 如果当前物品的重量大于了背包容量
                # 不能放该物品,那就跟不放该物品的时候一样
                dp[i][j] = dp[i - 1][j]
            else:  # 可以放入该物品,那就得比较一下
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - arr[i].w] + arr[i].v)  # 不放入该物品,放入该物品
    return dp


# 优化空间后不能去找路径了
def get_dp2(cap, arr):
    dp = [0] * (cap + 1)
    # 一维压缩
    for i in range(1, len(arr)):  # 行
        for j in range(cap, 0, -1):  # 列
            if j - arr[i].w < 0:
                break
            dp[j] = max(dp[j], dp[j - arr[i].w] + arr[i].v)  # dp2[j] 等于第i个物品时的最大价值
        print(dp)
    return dp


if __name__ == "__main__":
    import numpy as np
    from collections import namedtuple

    bag = namedtuple("bag", ["w", "v"])
    arr1 = [bag(0, 0), bag(2, 3), bag(3, 4), bag(4, 5), bag(5, 6)]
    cap = 8
    print(arr1)
    dp1 = get_dp(cap, arr1)
    find(len(arr1) - 1, cap, dp1, arr1)
    print(np.array(dp1))
    dp2 = get_dp2(cap, arr1)
    print("=" * 10)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值