【蓝桥刷题】——DFS大战DP,谁才是最后的赢家?(01背包模板)

大家好,我是爱学习的小蓝,欢迎交流指正~ 


🔎题目

传送门:蓝桥杯算法提高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          


 读码上万行,下键如有神,撸起袖子加油干!

  • 15
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小蓝刷题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值