背包问题归纳【2024蓝桥杯0基础】-学习笔记

题目描述

题目

01背包

在这里插入图片描述

n,V = map(int,input().split())
#这里的I表示的是第几个商品,j表示的是物品所占的体积
dp = [[0]*(V+1) for i in range(n+1)]
# 这里一般都是从1开始,防止越界
for i in range(1,n+1):
    w,v = map(int,input().split())
    for j in range(V + 1):
        #如果现在的书包容量装不下,那么就用前一个商品的状态去更新
        if j < w:
            dp[i][j] = dp[i -1][j]
        else :
            #用上一个物品的状态和现在加入了这一个物品的状态价值进行比较,选取最大的
            dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - w]+v)
print(dp[n][V])

完全背包

基本思想:

区别于01的地方是这里的商品数量是无限的
在这里插入图片描述
这样写的时间复杂度为O(NVV)
改进如下:
在这里插入图片描述
代码:

n,V = map(int,input().split())
#这里的I表示的是第几个商品,j表示的是物品所占的体积
dp = [[0]*(V+1) for i in range(n+1)]
# 这里一般都是从1开始,防止越界
for i in range(1,n+1):
    w,v = map(int,input().split())
    for j in range(V + 1):
        #如果现在的书包容量装不下,那么就用前一个商品的状态去更新
        if j < w:
            dp[i][j] = dp[i -1][j]
        else :
            #用上一个物品的状态和现在加入了这一个物品的状态价值进行比较,选取最大的
            dp[i][j] = max(dp[i - 1][j],dp[i][j - w]+v)
print(dp[n][V])

时间复杂度变为O(N*V)

在这里插入图片描述

这里可以用记忆化搜索(记录先前搜索的信息):

N,V = map(int,input().split())
dp = [0]*(V+1)
for i in range(1,N+1):
    w,v = map(int,input().split())
    for j in range(w,V+1):
        dp[j] = max(dp[j],dp[j-w]+v)
print(dp[V])

多重背包

区别:

是01背包和完全背包的结合体,表示商品的数量是k个的(k大于等于1)
基础代码思路:

N,V = map(int,input().split())
#这里的I表示的是第几个商品,j表示的是物品所占的体积
dp = [[0]*(V+1) for i in range(N+1)]
# 这里一般都是从1开始,防止越界
for i in range(1,N+1):
    w,v,s = map(int,input().split())
    for j in range(V + 1):
        # 这里必须保证j-k*w>=0
        for k in range(min(s,j//w)+1):
            dp[i][j] = max(dp[i][j], dp[i-1][j-k*w]+k*w)
print(dp[N][V])

在这里插入图片描述
数量变为Nlog(S),体积为V,时间复杂度为O(Nlog(S)V)

N, V = map(int, input().split())
new_w_v= []
for i in range(1,N+1):
    w,v,s = map(int,input().split())
    k = 1
    while s >= k:
        new_w_v.append((k*w,k*v))
        s -= k
        k *= 2
    if s != 0:
        new_w_v.append((s*w, s*v))
#01背包的代码
dp = [0]*(V+1)
for i,(w,v) in enumerate(new_w_v,1):
    for j in range(V,w-1,-1):
        dp[j] = max(dp[j],dp[j-w]+v)
print(dp[V])

单调队列优化多重背包

可以参考这篇详细的讲解:
https://blog.csdn.net/jisuanji2606414/article/details/123384333

N, V = map(int, input().split())
dp = [0]*(V+1)
for _ in range(N):
    v, w, s = map(int, input().split())
    for c in range(v):
        queue = []
        hh, tt = 0, -1
        times = (V-c) // v
        for k in range(times+1):
            curr = dp[k*v+c] - k*w
            print("k,curr",k,curr)
            if hh <= tt and queue[hh][0] == k-s-1:
                hh += 1
            while hh <= tt and queue[-1][1] < curr:
                queue.pop()
                print("tt,hh", tt,hh )
                tt -= 1
            queue.append([k, curr])
            tt += 1
            dp[k*v+c] = queue[hh][1] + k*w
print(dp[V])

二维费用背包

题目和思路

在这里插入图片描述
在这里插入图片描述

N,V,M = map(int,input().split())
dp = [[0]*(M+1) for i in range(V+1)]
for i in range(N):
    v,m,w = map(int,input().split())
    #从后往前更新
    for j in range(V,v-1,-1):
        for k in range(M,m-1,-1):
            dp[j][k] = max(dp[j][k],dp[j-v][k-m]+w)
print(dp[V][M])

分组背包

题目和思路

在这里插入图片描述

基本写法(耗费内存)

N,V = map(int,input().split())
dp = [[0]*(V+1) for i in range(N + 1)]
#对于每一组
for i in range(1,N+1):
    each_group = []
    s = int(input())
    #对于每一组数组
    for _ in range(s):
        w,v = map(int,input().split())
        for j in range(V+1):
            if j < w:
            #每组物品都要涉及更新dp[i][j],因此要max
                dp[i][j] = max(dp[i][j],dp[i-1][j])
            else:
                dp[i][j] = max(dp[i][j],dp[i-1][j],dp[i-1][j-w]+v)
print(dp[N][V])

使用滚动数组

#使用滚动数组
N,V = map(int,input().split())
groups= []
for _ in range(N):
    s = int(input())
    each_group = [list(map(int,input().split())) for i in range(s)]
    groups.append(each_group)
dp = [0] * (V+1)

#枚举每一组
for i in range(1,N+1):
    #枚举每一个体积
    for j in range(V,-1,-1):
        #枚举第i组的每一件物品
        for w,v in groups[i-1]:
            if j >= w:
                dp[j] = max(dp[j],dp[j-w]+v)

为什么数组要从大到小遍历

在这里插入图片描述
蓝桥云课学习笔记分享
持续更新中~ 2024蓝桥杯一起冲!

by 闻不多

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值