dp基础之背包问题

问题一:有N个物品,重量为A[0]...A[N-1],有一个容量为M(M是一个正整数)。
问:最多能带走多重的物品。
例:A = [2,3,5,7]
    M = 10
输出:10(2,3,5)

问题分析:
要求N个物品能否拼出重量W(W = 0,1...M),需要知道前N-1个物品能否拼出重量W(W = 0,1...M)

考虑最后一个物品(A[N-1])放不放入进入背包
    情况1:如果前N-1个物品能拼出重量W,则前N个物品必然也能拼出重量W
    情况2:如果前N-1个物品能拼出重量W-A[N-1],则前N个物品就能拼出重量W,加上物品A[N-1]即可

子问题:
设:f[i][w]表示物品前i个能拼出重量w(True/False)
        f[i][w] = f[i-1][w] or f[i-1][w-A[i-1]]
                  不放入A[i-1]    or 放入A[i-1]
初始条件:
         0个物品可以拼出重量0
         f[0][0] = True
         0个物品不能拼出大于0的任何重量
         f[0][1...M] = False

边界情况:
         f[i][w-A[i-1]] w>=A[i-1]时使用

计算顺序:
         初始化
         f[0][0..M]
         前1个物品能拼出:f[1][0]...f[1][M]
                             .
                             .
         前N个物品能拼出:f[N][0]...f[N][M]

时间复杂度:O(MN),空间复杂度O(MN),优化后可以达到O(M)

 

代码及注释如下:

def backpack(A,M):
    N = len(A)
    if N == 0:
        return 0
    f = [[False for i in range(M+1)] for j in range(N+1)]
    #初始化,f[0][0] = True ;f[0][1...M] = False
    f[0][0] = True
    for i in range(1,N+1):
        
        for j in range(0,M+1):
            #f[i][w]表示物品前i个能拼出重量w(True/False)
            #f[i][w] = f[i-1][w] or f[i-1][w-A[i-1]](w>=A[i-1])
            f[i][j] = f[i-1][j]
            if j >= A[i-1] :
                f[i][j] = f[i-1][j] or f[i-1][j-A[i-1]]
    #返回前N个物品能拼出最大的重量,肯定不会超过M,因为最大就是M
    for j in range(M+1)[::-1]:
        if f[N][j]:
            return j
            
A = [2,3,7,5]
M = 10
print(backpack(A,M))
#结果:10

 

问题二:假设每个物品只有一个(每个物品只能用一次),问一共有多少种方式正好凑成重量Target?
例:
A = [1,2,3,3,7],Target = 7
输出:2(1,3,3,7)

问题分析:
如果知道这N个物品有多少种方式拼出0...Target,也就得到了答案
确定状态:需要N个物品有多少种方式拼出重量W(W = 0...Target)
最后一步:考虑第N个物品A[N-1](最后一个物品)是否进入背包

    case1: 不进入,用前N-1个物品拼出W
    case2: 进入,前N-1个物品能拼出W-A[N-1],加上最后一个A[N-1],正好拼出W
    现在要求的是方式数,
    case1的方式数+case2的方式数 = 用前N个物品拼出W的方式数
转移方程:设f[i][w]表示用前i个物品能拼出w的方式数
    f[i][w] =

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值