牛客[HJ16 购物单]

0. 题目

该题目为有依赖的背包问题,题目描述如下:https://www.nowcoder.com/practice/f9c6f980eeec43ef85be20755ddbeaf4?tpId=37&tqId=21239&rp=1&ru=/exam/oj/ta&qru=/exam/oj/ta&sourceUrl=%2Fexam%2Foj%2Fta%3Fdifficulty%3D3%26judgeStatus%3D3%26page%3D1%26pageSize%3D50%26search%3D%26tpId%3D37%26type%3D37&difficulty=3&judgeStatus=3&tags=&title=

1. 背包问题

我们首先从最基本的01背包问题开始,01 knapsack problem:一共有N件物品,第 i( i 从1开始)件物品的重量为w[ i ],价值为v[ i ]。在总重量不超过背包承载上限W的情况下,能够装入背包的最大价值是多少?

1.1. 基础解法

该问题为经典的动态规划问题,我们可以开一个二维数组dp[ m+1 ][ n+1 ],
其中dp[ i ][ j ]表示考虑前 i 个物品,背包容量为 j 时背包能放入的最大价值。
考虑前0个物品或者背包容量为0时,dp[ 0 ][ : ]=dp[ : ][ 0 ] = 0
对于第 i 个物体,有放入与不放入两种可能:
a. 若 j >= w[ i ],则表示背包可以容纳该物品,此时有放入与不放入两种选择,dp[ i ][ j ]分别为 dp[ i ][ j ]=dp[ i - 1 ][ j- w[ i ] ] + v[ i ],dp[ i ][ j ]=dp[ i - 1 ][ j ],我们选择两者中的较大值即可,即:
dp[ i ][ j ] = max( dp[ i - 1 ][ j- w[ i ] ] + v[ i ], dp[ i - 1 ][ j ] )
b. 若 j < w[ i ],则背包无法放入该物品,dp[ i ][ j ]=dp[ i - 1 ][ j ]


c=15 #背包容量
w=[4,5,6,4,2,1] #物品重量
v=[6,5,4,3,2,1] #物品价值

def bag_1(c,w,v):
    n=len(w)
    '''
    注意python中列表的定义:
    定义一维数组时,可以直接使用a=[0]*n
    在定义二维数组时,若使用a=[[0]*n]*m,则会出现问题,
    此时id(a[0])=id(a[:]),
    在赋值时候,若令a[1][1]=1,则a[:][1]=1(a[1][1]所在列全为1)
    在定义二维数组的时候,应该使用如下表达式
    a = [[0]*(n) for _ in range(m)] (m行n列)    
    '''
    dp = [[0]*(c+1) for _ in range(n+1)]    
    for i in range(1,n+1):
        for j in range(1,c+1):
            if w[i-1]<=j:
                dp[i][j]=max(dp[i-1][j-w[i-1]]+v[i-1],dp[i-1][j])
            else:
                dp[i][j]=dp[i-1][j]
    return dp[n][c]

该算法时间复杂度为O(mn),空间复杂度为O(mn) (m为物品的总数量,n为背包容量)

1.2. 空间优化

有dp的表达式可知,在求解dp[ i ][ : ]时只用到了dp[ i-1 ][ : ],因此我们没有必要把dp整个存储下来,存储两行即可,因此,我们只开两行数组,利用取模操作不断更新两行数组即可。

def bag_2(c,w,v):
    n=len(w)
    dp = [[0]*(c+1) for _ in range(2)]    
    for i in range(1,n+1):
        for j in range(1,c+1):
            if w[i-1]<=j:
                dp[i%2][j]=max(dp[(i-1)%2][j-w[i-1]]+v[i-1],dp[(i-1)%2][j])
            else:
                dp[i%2][j]=dp[(i-1)%2][j]
    return dp[n%2][c]

该算法时间复杂度为O(mn),空间复杂度为O(2n) (m为物品的总数量,n为背包容量)

1.3. 空间优化pro

求解dp[ i ][ : ]时只用到了dp[ i-1 ][ : ],更进一步,求解dp[ i ][ j ]时,只用到了dp[ i-1 ][ : j ](即只用到了
第 j 列之前的元素)。如果我们正向求解,即从 j=1 开始的话,就必须存储两行数据,但是如果我们倒叙求解,从 j=c 开始,我们可以用dp[ i ][ j ]将dp[ i-1 ][ j ]覆盖掉,因为求解dp[ i ][ : j ]时并不会用到dp[ i-1 ][ j ],因此,我们存储一行数据即可。

def bag_3(c,w,v):
    n=len(w)
    dp = [0]*(c+1)   
    for i in range(1,n+1):
        for j in range(c,0,-1):
            if w[i-1]<=j:
                dp[j]=max(dp[j-w[i-1]]+v[i-1],dp[j])
    return dp[c]

该算法时间复杂度为O(mn),空间复杂度为O(n) (m为物品的总数量,n为背包容量)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值