背包问题
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和 最大。
基本思路
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。 用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便 是:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
−
1
]
[
j
−
w
v
[
i
−
1
]
]
+
w
t
[
i
−
1
]
)
dp[i][j] = max(dp[i-1][j],dp[i-1][j-wv[i-1]]+wt[i-1])
dp[i][j]=max(dp[i−1][j],dp[i−1][j−wv[i−1]]+wt[i−1])这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一 下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转 化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包 中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”, 此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
01背包
class soultion():
def o_i(self):
v = 5 #背包容量
wv = [3,1,2,2]# 体积
wt = [8,5,4,3]# 价值
# 背包不用装满,物品价值为0时,初始化为dp[0][0]为0,dp[0][1~v] = 0
dp = [[0]*(v+1) for _ in range(len(wt)+1)]
# 背包装满,物品价值为0时,初始化为dp[0][0]为0,dp[0][1~v] = -inf
#for i in range(1,v+1):
# dp[0][i] = float('-inf')
for i in range(1,len(wt)+1):#体积选择
for j in range(1,v+1):#背包体积选择
if j - wv[i-1] < 0:
#大于当前背包体积,装不了
dp[i][j] = dp[i-1][j]
else:
# max(不装这个dp[i-1][j],装这个dp[i-1][j-wv[i-1]]+wt[i-1])
dp[i][j] = max(dp[i-1][j],dp[i-1][j-wv[i-1]]+wt[i-1])
return dp[-1][-1]
有的题目要求“恰好装满背包”时的最 优解,有的题目则并没有要求必须把背包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。 如果是第一种问法,要求恰好装满背包,那么在初始化时当物品为0时,无论背包体积多大,dp均设为-∞,这样就可以保证最终 得到的是一种恰好装满背包的最优解。 如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时dp直接全部设为0。 为什么呢?可以这样理解:初始化的数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包 恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的 解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合 法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。 这个技巧完全可以推广到其它类型的背包问题.
完全背包
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些 物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
完全背包的变化只有一点,那就是使用一个物品时,可以建立在之前使用过的基础上 i − 1 变为 i 完全背包的变化只有一点,那就是使用一个物品时,可以建立在之前使用过的基础上i-1变为i 完全背包的变化只有一点,那就是使用一个物品时,可以建立在之前使用过的基础上i−1变为i
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w v [ i − 1 ] ] + w t [ i − 1 ] ) dp[i][j] = max(dp[i-1][j],dp[i-1][j-wv[i-1]]+wt[i-1]) dp[i][j]=max(dp[i−1][j],dp[i−1][j−wv[i−1]]+wt[i−1])
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w v [ i − 1 ] ] + w t [ i − 1 ] ) dp[i][j] = max(dp[i - 1][j], dp[i][j - wv[i - 1]] + wt[i - 1]) dp[i][j]=max(dp[i−1][j],dp[i][j−wv[i−1]]+wt[i−1])
class soultion1():
def wq1(self):
v = 5 # 背包容量
wv = [4,2,2,3]# 体积
wt = [8,5,4,1]# 价值
# 背包不用装满,物品价值为0时,初始化为dp[0][0]为0,dp[0][1~v] = 0
dp = [[0]*(v+1) for _ in range(len(wt)+1)]
# 背包装满,物品价值为0时,初始化为dp[0][0]为0,dp[0][1~v] = -inf
#for i in range(1,v+1):
# dp[0][i] = float('-inf')
for i in range(1, len(wt) + 1):
for j in range(1, v + 1):
if j - wv[i - 1] < 0:
#大于当前背包体积,装不了
dp[i][j] = dp[i - 1][j]
else:
# 不装这个 dp[i-1][j]
# 装这个,可以重复使用当前物品体积选择dp[i][j-wv[i-1]]+wt[i-1])
dp[i][j] = max(dp[i - 1][j], dp[i][j - wv[i - 1]] + wt[i - 1])
#print(dp)
return dp[-1][-1]