有n个重量和价值分别为 w i w_i wi, v i v_i vi的物品。从这些物品中挑选出总重不超过W的物品,求所有挑选方案中价值总和的最大值。
方法一:深度优先搜索对于01背包问题,即每个物品有两种选择(选,不选)。那么我们可以依据此性质建立选与不选二叉树。代码如下:
class Solution:
def zeronebag(self, n, W, w, v):
def rec(i, j):
if i == n:
res = 0
elif j < w[i]:
res = rec(i+1, j)
else:
res = max(rec(i+1, j), rec(i+1, j-w[i])+v[i])
return res
return rec(0, W)
if __name__=='__main__':
a = Solution().zeronebag(4, 5, [2, 1, 3, 2], [3, 2, 4, 2])
print(a)
这种方法的搜索深度是n,而且每一层的搜索都需要两次分支,最坏就需要 O( 2 n 2^n 2n) 的时间,当n比较大时就没办法解决。从上述图中可以发现橙色块rec以(3, 2)为参数调用了两次。参数相同返回结果相同,因此我们可以把第一次搜索的结果记录下来,这样下次搜索直接在记录中查找即可,这样就有如下优化方法。
方法二:记忆化搜索创建dp数组,用于存储已经搜索过的rec值。则有如下代码:
class Solution:
def zeronebag(self, n, W, w, v):
def rec(i, j):
if dp[i][j] > 0:
return dp[i][j]
if i == n:
res = 0
elif j < w[i]:
res = rec(i+1, j)
else:
res = max(rec(i+1, j), rec(i+1, j-w[i])+v[i])
dp[i][j] = res
return res
dp = [[0] * (w+1) for _ in range(n+1)]
return rec(0, W)
if __name__=='__main__':
a = Solution().zeronebag(4, 5, [2, 1, 3, 2], [3, 2, 4, 2])
print(a)
上述方法参数的组合为nW种,而函数内只调用2次递归,所以只需要 O(nW) 的复杂度就能解决这个问题。
方法三:动态规划(DP)事实上,观察上述记忆化数组,记 dp[i][j] 为根据rec的定义,从第i个物品开始挑选总重小于j时,总价值的最大值,有如下递推式:
如上所示,不用写递归函数,直接利用递推式将各项的值计算出来,简单地用二重循环也可以解决这一问题。代码如下:
class Solution:
def zeronebag(self, n, W, w, v):
dp = [[0] * (W+1) for _ in range(n+1)]
for i in range(n-1, -1, -1):
for j in range(W+1):
if j < w[i]:
dp[i][j] = dp[i+1][j]
else:
dp[i][j] = max(dp[i+1][j], dp[i+1][j-w[i]]+v[i])
return dp[0][W]
if __name__=='__main__':
a = Solution().zeronebag(4, 5, [2, 1, 3, 2], [3, 2, 4, 2])
print(a)
这个算法的复杂度与前面相同,也是O(nW),但是简洁很多。
上述i的循环是逆向的,可以改变递推式,从0开始,代码如下。
class Solution:
def zeronebag(self, n, W, w, v):
dp = [[0] * (W+1) for _ in range(n+1)]
for i in range(n):
for j in range(W+1):
if j < w[i]:
dp[i+1][j] = dp[i][j]
else:
dp[i+1][j] = max(dp[i][j], dp[i][j-w[i]]+v[i])
return dp[n][W]
if __name__=='__main__':
a = Solution().zeronebag(4, 5, [2, 1, 3, 2], [3, 2, 4, 2])
print(a)
事实上,我们可以只创建一维数组存储 j 时的最大价值和。代码如下:
class Solution:
def zeronebag(self, n, W, w, v):
dp = [0] * (W+1)
for i in range(n):
for j in range(W, w[i]-1, -1):
dp[j] = max(dp[j], dp[j-w[i]]+v[i])
return dp[W]
if __name__=='__main__':
a = Solution().zeronebag(4, 5, [2, 1, 3, 2], [3, 2, 4, 2])
print(a)
二、完全背包问题
完全背包问题是不限制每个物品挑选的数量。类似找零钱问题,找零钱问题刨析。代码如下:
class Solution:
def fullbag(self, n, W, w, v):
dp = [0] * (W+1)
for i in range(n):
for j in range(w[i], W+1):
dp[j] = max(dp[j], dp[j-w[i]]+v[i])
return dp[W]
if __name__=='__main__':
a = Solution().fullbag(4, 5, [2, 1, 3, 2], [3, 2, 4, 2])
print(a)
--------持续更新中--------
例题某次漫展,已知有n个打卡点,每个打卡点的活动需要 m_i 分钟完成,完成后获得奖励点 r_i,已经打卡过的点不能再去。
需要在规定 m 分钟内完成,尽可能多的收获奖励点,求获得最多的奖励点数。
输入描述:
第一行两个整数,打卡点的数量 n 和限制时间 m
第 2 到 1 + n 行,每行两个整数 m_i,r_i
数字以空格分割,其中 0 < n <= 100,1 <= m <= 120,1 <= m_i <= 10,1 <= r_i <= 100
输出描述:
整数, 最大的奖励点数
输入例子1:
4 6
2 4
2 35
1 43
2 10
输出例子1:
88
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)