当前位置: 首页 > news >正文

【算法】动态规划专题⑦ —— 多重背包问题 + 二进制分解优化 python

目录

  • 前置知识
  • 进入正题
  • 优化方法:二进制分解
  • 实战演练


前置知识


【算法】动态规划专题⑤ —— 0-1背包问题 + 滚动数组优化 python
【算法】动态规划专题⑥ —— 完全背包问题 python


进入正题


多重背包问题I https://www.acwing.com/problem/content/4/


题目描述

N N N 种物品和一个容量是 V V V 的背包。
i i i 种物品最多有 s _ i s\_i s_i 件,每件体积是 v _ i v\_i v_i,价值是 w _ i w\_i w_i

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

输入格式

第一行两个整数, N , V N,V NV,用空格隔开,分别表示物品种数和背包容积。
接下来 N N N 行,每行三个整数 v i , w i , s i v_i, w_i, s_i vi,wi,si,用空格隔开,分别表示第 i i i 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0 < N , V ≤ 100 0 \lt N, V \le 100 0<N,V100
0 < v i , w i , s i ≤ 100 0 \lt v_i, w_i, s_i \le 100 0<vi,wi,si100

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

在多重背包问题中,对于每种物品,你不仅可以选择是否放入背包,而且对于每种物品还有一个限制,即该物品最多可以放入的数量。
与0/1背包和完全背包不同:我们需要对每个物品的数量限制进行处理。

基本实现:

n, v = map(int, i
nput().split())
dp = [[0] * (v + 1) for _ in range(n + 1)]
for i in range(1, n + 1):vi, wi, si = map(int, input().split())for j in range(1, v + 1):for k in range(min(si, j // vi) + 1):dp[i][j] = max(dp[i][j], dp[i - 1][j - k * vi] + k * wi)
print(dp[n][v])

时间复杂度为O(n*v*s),有没有优化的方法呢?


优化方法:二进制分解


为了提高效率,我们可以采用二进制优化的方法来处理物品的数量限制。
具体来说,对于第i种物品,我们将其数量mi拆分为若干组,每一组的数量分别为(1, 2, 4, …, res)
这样做的目的是让这些组合能够通过不同的组合方式表示从1到mi的所有数字。

例如,假设某物品的数量限制为13,那么我们可以将其拆分为:

  • 1 即(2^0)
  • 2 即(2^1)
  • 4 即(2^2)
  • 6 (13 - (1+2+4) = 6)

这样,我们就可以用这四个新的“物品”组合出任何从1到13的数量。比如:

  • 5 = 1 + 4
  • 7 = 1 + 2 + 4
  • 13 = 1 + 2 + 4 + 6

完成上述转换后,我们将问题转化为0-1背包问题,其中每组作为一个单独的“物品”。


实现步骤

  1. 对于每种物品,根据其数量mi进行二进制拆分,生成新的“物品”列表。
  2. 初始化dp数组,dp[0]通常初始化为0,因为当容量为0时无法放入任何东西。
  3. 遍历新生成的每个“物品”,然后从该物品的重量开始遍历到背包的容量C,更新dp数组。
    dp[j] = max(dp[j], dp[j-w[i]] + v[i]) 对于所有 j >= w[i]
    
  4. 最终,dp[C]将包含能够放入容量为C的背包中的最大价值。

注意

  • 使用二进制优化可以显著减少计算量,特别是在物品的数量限制较大的情况下。
  • 在实际实现时,需要注意边界情况的处理,如物品数量mi为0的情况。

通过这种方法,我们可以有效地解决多重背包问题,并找到在给定背包容量下可获得的最大价值。



好了,实际运用的时候到了

实战演练


多重背包问题II https://www.acwing.com/problem/content/5/

题目描述

N N N 种物品和一个容量是 V V V 的背包。
i i i 种物品最多有 s i s_i si 件,每件体积是 v i v_i vi,价值是 w i w_i wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

输入格式

第一行两个整数, N , V N,V NV,用空格隔开,分别表示物品种数和背包容积。

接下来有 N N N 行,每行三个整数 v i , w i , s i v_i, w_i, s_i vi,wi,si,用空格隔开,分别表示第 i i i 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0 < N ≤ 1000 0 \lt N \le 1000 0<N1000
0 < V ≤ 2000 0 \lt V \le 2000 0<V2000
0 < v i , w i , s i ≤ 2000 0 \lt v_i, w_i, s_i \le 2000 0<vi,wi,si2000

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

本题数据范围增强了,考查多重背包的二进制优化方法。


code:

n, v = map(int, input().split())
v_w = []  # 存储物品的体积和价值的二进制分解
for i in range(1, n + 1):vi, wi, si = map(int, input().split())k = 1while si >= k:v_w.append((vi * k, wi * k))  # 二进制分解si -= kk *= 2# 处理剩余的部分if si > 0:v_w.append((vi * si, wi * si))dp = [0] * (v + 1)  # 滚动数组优化
for i, (vi, wi) in enumerate(v_w):for j in range(v, vi - 1, -1):dp[j] = max(dp[j], dp[j - vi] + wi)
print(dp[v])

时间复杂度优化为O(n*v*logs)


再进一步,利用单调队列维护窗口内最大值,避免重复计算
可以将时间复杂度优化为O(n*v)
在此就不赘述了,属于进阶技巧,后续会更新


END
如果有更多问题或需要进一步的帮助,可以在评论区留言讨论哦!
如果喜欢的话,请给博主点个关注 谢谢


http://www.mrgr.cn/news/89909.html

相关文章:

  • #渗透测试#批量漏洞挖掘#微商城系统 goods SQL注入漏洞
  • C++ Primer 成员访问运算符
  • 快速在wsl上部署学习使用c++轻量化服务器-学习笔记
  • 【自开发工具介绍】SQLSERVER的ImpDp和ExpDp工具演示04
  • 私有化部署DeepSeek并SpringBoot集成使用(附UI界面使用教程-支持语音、图片)
  • C++ Primer 递增和递减运算符
  • 120,【4】 攻防世界 web Confusion1(jinja2)
  • 介绍10个比较优秀好用的Qt相关的开源库
  • 音频进阶学习十二——Z变换一(Z变换、收敛域、性质与定理)
  • [论文阅读] Knowledge Fusion of Large Language Models
  • 图解BWT(Burrows-Wheeler Transform) 算法
  • DeepSeek与人工智能的结合:探索搜索技术的未来
  • Linux之安装docker
  • 学习数据结构(6)单链表OJ
  • 机器学习中常用的评价指标
  • Vue(4)
  • springboot配置https
  • 开放式TCP/IP通信
  • webpack系统学习
  • 【Linux网络编程】之守护进程