一. 前言
背包问题是一个很经典的问题,包括八个不同类别,但实际面试中,一般知道0-1背包和完全背包就可以应付面试了,本文将从两个基本背包问题进行讨论和实现。
背包问题:
有一个承重为W的背包和N个物品,每个物品重量分别为wt[i],每个物品价值为v[i],求问怎么装背包才能使得包内物品价值最高?注意:包内物品重量总和不能超过W
二. 0-1背包
求最值问题首先想到动态规划来做,背包问题就可以采用dp来做
- 首先弄清楚dp的状态和选择
状态:背包可以承重的重量W和可选择的物品,因此需要采用二维数组dp
选择:将物品装入或不装入背包 - 其次弄清楚dp的含义
dp[i][w]:表示前i个物品在背包承重为w时背包内的最大价值 - 接着写出dp状态转移方程
for i in range(1,N+1):
for w in range(1,W+1):
if w-wt[i-1]<0:
dp[i][w] = dp[i-1][w]
else:
dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - wt[i - 1]] + vt[i - 1])
- 最后初始化dp数组和考虑边界条件
dp = [[0] * (W + 1) for _ in range(N + 1)]
边界条件:当包内不装任何物品的时候,价值为0,即dp[…][0]=0
下面代码值得注意的循环是从1开始的,而物品重量和价值列表是从0开始的,因此i-1表示第i个物品
完成的代码如下:
"""
动态规划
N=3个物品,每个物品重量wi=[2,1,3],价值vi=[4,2,3]
背包最大的容纳重量为W=4
"""
def dp_bagpack():
N = 3
W = 4
wt = [2, 1, 3]
vt = [4, 2, 3]
dp = [[0] * (W + 1) for _ in range(N + 1)]
for i in range(1, N + 1):
for w in range(1, W + 1):
if w - wt[i - 1] < 0: # w < wt[i - 1],当前背包容不下下一个物品的重量,此时选择不放入
dp[i][w] = dp[i - 1][w]
else: # 背包可以容纳下一个物品,i-1和w-1表示当前物品i,因为for循环是i=1开始的
dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - wt[i - 1]] + vt[i - 1])
return dp[N][W]
还可以进一步优化,使用一维数组dp
"""
动态规划
N=3个物品,每个物品重量wi=[2,1,3],价值vi=[4,2,3]
背包最大的容纳重量为W=4
"""
def dp_bagpack2():
N = 3
W = 4
wt = [2, 1, 3]
vt = [4, 2, 3]
dp = [0] * (W + 1) # dp[j]代表的是当包重量达到j的时候,此时包内物品的最大值
dp[0] = vt[0]
for i in range(1, N):
for w in range(W, -1, -1): # 要逆序遍历
if w - wt[i - 1] >= 0: # 如果包内重量还没装满,则判断取最大值
dp[w] = max(dp[w], dp[w - wt[i - 1]] + vt[i - 1])
return dp[W]
两个程序运算结果都为6
三.完全背包
完全背包和0-1背包唯一不同的地方就是在完全背包问题中物品是无限可取的,但不能超过背包承重量W;而0-1背包则是物品只能取一次,不能重复去,也不能超过背包承重量W
完全背包求解思路和0-1背包几乎一致,只是在代码个别地方需要注意一下
"""
动态规划
N=3个物品,每个物品重量wi=[1, 2, 3],价值vi=[2, 4, 3]
背包最大的容纳重量为W=4
物品可重复取
"""
def dp_bagpack():
N = 3
W = 4
wt = [1, 2, 3]
vt = [2, 4, 3]
dp = [[0] * (W + 1) for _ in range(N + 1)]
for i in range(1, N + 1):
for w in range(1, W + 1):
if w - wt[i - 1] < 0:
dp[i][w] = dp[i - 1][w]
else:
# 与01背包不同的就是dp[i][w-wt[i-1]]+vt[i-1]与dp[i-1][w-wt[i-1]]+vt[i-1]
dp[i][w] = max(dp[i - 1][w], dp[i][w - wt[i - 1]] + vt[i - 1])
return dp[N][W]
优化版
"""
动态规划
N=3个物品,每个物品重量wi=[1, 2, 3],价值vi=[2, 4, 3]
背包最大的容纳重量为W=4
物品可重复取
"""
# 优化版
def dp_bagpack2():
N = 3
W = 4
wt = [2, 1, 3]
vt = [4, 2, 3]
dp = [0] * (W + 1)
for i in range(0, N): # 此时不是从1开始,而是从0开始,因为可重复取
for w in range(0, W + 1): # 记住:要正序取,从0开始
if w - wt[i] >= 0: # 只需要判断包未装满的情况
# dp[w - wt[i]] + vt[i]表示取物品i,则当前重量w需要减去物品的重量,并加上该物品的价值
dp[w] = max(dp[w], dp[w - wt[i]] + vt[i])
return dp[W]
三. 总结
背包问题,至此掌握