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为背包容量)