大家好,我是爱学习的小蓝,欢迎交流指正~
传送门:蓝桥杯算法提高VIP-01背包 - C语言网
给定N个物品,每个物品有一个重量W和一个价值V.你有一个能装M重量的背包.问怎么装使得所装价值最大.每个物品只有一个.
输入
输入的第一行包含两个整数n, m,分别表示物品的个数和背包能装重量。
以后N行每行两个数Wi和Vi,表示物品的重量和价值
输出
输出1行,包含一个整数,表示最大价值。
样例输入
3 5
2 3
3 5
4 7
样例输出
8
难度系数:⭐⭐
考察题型:搜索 动态规划
涉及知识点:DFS 背包DP
很多小伙伴会对DFS和DP有疑问,我先逐一回答下。
Q1:DFS和DP两种方法,考场上我到底要用哪一个呢?
其实很简单,看时间复杂度。DFS:O(2^n)大到爆炸,DP:O(n^2)小到飞速。
所以优选DP,其次DFS。
Q2:那既然有DP这么好用的工具,为啥我们还要学DFS呢?
因为DP相对DFS比较难想,当动态规划实在没思路,写暴力搜索骗骗分也是极好的~
动态规划-五部曲
第一步:明白dp[j]的含义
dp[j] #j:当前背包的容量 #dp[j]:当前背包的价值
第二步:搞懂递推公式
别看递推公式短短一行,其中包含的信息量巨大!这是最为关键的一步。
此时dp[j]有两个选择,一个是dp[j],代表之前算好的最大价值。
另一个是dp[j-weight[i]]+value[i],
代表dp[背包容量-放入当前物品的重量]剩余的价值+放入当前物品的价值。
dp[j]=max(dp[j],dp[j-weight[i]]+value[i])#递推公式
第三步:给dp数组初始化赋值
dp=[0]*(m+1) #数组初始化[0,0,0,0,0,0]
第四步:弄清dp[j]遍历的顺序
倒序遍历主要是保证每个物品只被遍历一次。
因为正序遍历物品会被重复添加两次,就违反了每个物品只能用一次的规则。
for i in range(1,n+1): #先正序遍历物品(1 |2 |3 ) for j in range(m,w[i]-1,-1): #再逆序遍历背包(5 4 3 2 |5 4 3 |5 4)
第五步:打印数组
print(dp[m]) #背包最大价值:8
参考资料:带你学透01背包问题(滚动数组篇) | 从此对背包问题不再迷茫!_哔哩哔哩_bilibili
DFS版
#01背包
n,m=map(int,input().split()) #n=3件物品 #m=5kg
w=[0]*(n+1) #物品重量
v=[0]*(n+1) #物品价值
dp=[0]*(m+1) #背包容量
maxvalue=0
for i in range(1,n+1):
w[i],v[i]=map(int,input().split())
'''
i: 0 1 2 3
w=[0, 2, 3, 4]
v=[0, 3, 5, 7]
'''
def dfs(i,sumW,sumV):
global maxvalue,m
if i==n: #遍历到最后一件物品
if sumW<=m: #总重量<=背包容量
maxvalue=max(maxvalue,sumV) #更新最大价值
return
#对下一个物品只有两种状态
dfs(i+1,sumW,sumV) #不选下一个物品
if sumW+w[i]<=m: #超出背包上限剪枝
dfs(i+1,sumW+w[i],sumV+v[i]) #选择下一个物品
dfs(0,0,0) #从0开始搜索
print(maxvalue) #最大价值是8
DFS会超时在意料之内,骗一两分还是可以滴~想要满分则需要动态规划上场了。
DP版
#01背包
n,m=map(int,input().split()) #n=3件物品 #m=5kg
w=[0]*(n+1) #物品重量
v=[0]*(n+1) #物品价值
dp=[0]*(m+1) #背包容量
for i in range(1,n+1):
w[i],v[i]=map(int,input().split())
'''
i: 0 1 2 3
w=[0, 2, 3, 4]
v=[0, 3, 5, 7]
'''
for i in range(1,n+1): #先正序遍历物品(1 |2 |3 )
for j in range(m,w[i]-1,-1): #再逆序遍历背包(5 4 3 2 |5 4 3 |5 4)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]) #递推公式
'''
j 0 1 2 3 4 5
[0, 0, 0, 0, 0, 3]
[0, 0, 0, 0, 3, 3]
[0, 0, 0, 3, 3, 3]
[0, 0, 3, 3, 3, 3]
[0, 0, 3, 3, 3, 8]
[0, 0, 3, 3, 5, 8]
[0, 0, 3, 5, 5, 8]
[0, 0, 3, 5, 5, 8]
[0, 0, 3, 5, 7, 8]
'''
print(dp[m])#8
读码上万行,下键如有神,撸起袖子加油干!