多重背包问题

动态规划——多重背包问题 聪明人都会点进来学习!!动态规划看这一篇就够了!!!本文详细讲解了动态规划的核心思路(配有图片),还以经典问题——多重背包问题为例,先从基础解法开始讲解思路,再一步步优化(特别是二进制优化),带有完整代码(有详细注释),对于难以理解的地方还会举例讲解,还会和01背包问题对比思考,赶紧点进来学习吧!!! 阅读详情

前言

没有理解01背包之前不推荐先玩别的背包,01背包的概念是一定要弄清楚的,完全背包的 顺推 和01背包的 逆推 思路也要理解清楚。
可参考以我发表的两篇博文:
01背包
完全背包

问题

题目链接:多重背包
大概就是这样了,输入的时候注意字母的区分,不要和核心代码部分混在一起。

字母含义表:

n n n m m m
希望购买的奖品的种数拨款金额

续表:

v i v_i vi​ w i w_i wi​ s i s_i si​
价格价值能购买的最大数量(买 0 0 0件到 s s s件均可)

数据范围

v ≤ 100 , w ≤ 1000 , s ≤ 10 v\le 100,w\le 1000,s\le 10 v≤100,w≤1000,s≤10


朴素 · 思路

输入部分注意区分字母,在此就不多作赘述了。

好了,这里开始讲思路。
最容易想到的算法是把每件物品的数量拆成循环然后当成01背包做。
但是这个算法和多重背包差不多,都要注意几点:

  1. 在状态转移方程内,要把物品的重量和价值扩大 k k k 倍(不明确指出时, k k k 默认为物品件数的循环变量)。
  2. 注意下限为 0 0 0。
  3. 注意上线的判断终止条件。

上限

上限现在具有两个判断条件:

  1. k × w i ≤ j k\times w_i\le j k×wi​≤j
  2. k ≤ s i k\le s_i k≤si​

此处 s i s_i si​ 为该物品的数量。

其实还可以这么写:
k ≤ m i n ( j ÷ w i , s [ i ] ) k\le min(j\div w_i,s[i]) k≤min(j÷wi​,s[i])
PS(整型变量相除默认向下舍入)
这么写可以节省亿——点点时间,创建一个变量维护这个值就可以了。

循环结构

大概长这样:

// 外面的东西
int x/*这里你随便搞个什么都行*/ = min(j / v[i], s[i])
for (int k = 0; k <= x; ++k) { // 或 k * v[i] <= j && k <= s[i]
	dp[j] = max(dp[j], dp[j - k * v[i]] + k * w[i]);
}
// ∞ 个 '}'
// ┬┴┬┴┤・ω・)ノ
// 输出答案

朴素 · 代码

#include <iostream>
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    const int N = n + 1, M = m + 1;
    int v[N], w[N], s[N], dp[M];
    // price value  number
    for (int i = 1; i < N; ++i) cin >> v[i] >> w[i] >> s[i];
    for (int i = 0; i < M; ++i) dp[i] = 0;
    for (int i = 1; i < N; ++i) {
        for (int j = m; j >= 0; --j) {
            int x = min(s[i], j / v[i]);
            for (int k = 0; k <= x; ++k)
                dp[j] = max(dp[j], dp[j - k * v[i]] + k * w[i]);
        }
    }
    cout << dp[m] << endl;
}

小贴士

小贴士:其实你完全没有必要按照题目的格式定义数组名,你完全可以把 a a a改成 b b b(仅举一例,请参照题目里的名字)

名称改变参考表:

v v v w w w s s s
w w w c c c v v v(其实这个可以不用改)

优化 · 思路

本节我们将深入讨论多重背包的二进制优化。(这里 “二” 的一般字体貌似跟它的粗体没啥区别,并不是我忘记在它前面加**了)

好了,继续讲。
首先看看这道题:
有 2 n − 1 2^n-1 2n−1个苹果,给你 n n n个箱子(假设它们都足够大或者苹果足够小),怎么放苹果才能使你不管要几个苹果( 0 ≤ 0\le 0≤ 几个 ≤ 2 n − 1 \le2^n-1 ≤2n−1),都能成箱成箱地拿(一个箱子都不拿也算成箱成箱地拿)?
对于熟悉二进制的小伙伴们来说,肯定很容易。(好吧,你也许不熟悉但也想出了答案或者你很熟悉但没有想出答案,但这都没关系)

答案

第 i i i 个箱子123 ⋯ \cdots ⋯ n n n
装 2 i − 1 2^{i-1} 2i−1个苹果124 ⋯ \cdots ⋯ 2 n − 1 2^{n-1} 2n−1

好了,现在来个升级版:
有 α \alpha α 个苹果(注意现在 0 ≤ α ≤ ∞ 0\le\alpha\le\infty 0≤α≤∞, α \alpha α 不一定正好等于 2 x − 1 2^x-1 2x−1),给你 n n n个箱子(假设它们都足够大或者苹果足够小,反正就是不管几个苹果都装得下),怎么放苹果才能使你不管要几个苹果( 0 ≤ 0\le 0≤ 几个 ≤ α \le\alpha ≤α),都能成箱成箱地拿(一个箱子都不拿也算成箱成箱地拿)?
现在对于熟悉二进制的小伙伴们来说,肯定很 “容易”。真的吗?反正别问我,我又不知道。

答案

本题答案:二进制凑不够够?自然数来了!
打几个比方(注意最后不是 2 x 2^x 2x的数):

α \alpha α 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8
分配方案:共 n n n 个箱子(其实这一行无关紧要) 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4
第 i i i 个箱子放 β \beta β个放 β \beta β个放 β \beta β个放 β \beta β个放 β \beta β个放 β \beta β个放 β \beta β个放 β \beta β个
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
2 2 2无 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
3 3 3无无无 1 1 1 2 2 2 3 3 3 4 4 4 4 4 4
4 4 4无无无无无无无 1 1 1

找到算法规律了吗?找不到也没关系。


解释:

对于任意一个自然数 n n n,都有如下的(创建物品)操作:
接着,我们定义操作 f ( n ) f(n) f(n) 为:

  1. 初始化权重为 2 0 2^0 20,也就是 1 1 1。
  2. 每次判断权重是否大于 n n n,若为假则执行第 6 6 6 步。
  3. 创建一件物品,重量为 权重 × w i \times w_i ×wi​,价值为 权重 × c i \times c_i ×ci​。
  4. 将权重翻倍(即升一级),然后使 n − n- n− 权重。
  5. 回到第 2 2 2 步。
  6. 若 n > 0 n>0 n>0,则创建一件物品,令其重量为 n × w i n\times w_i n×wi​,价值为 n × c i n\times c_i n×ci​。

问题来了(对,还有一个):为什么要这么做?
原因是:
在二进制下(其实应该叫补二进制),无论你想要几件这种物品,你都能从该种物品衍变出来的 “堆” 里面凑足。
事实上,在遍历这些物品的过程中,这个算法就会自动筛选出最合适的数量!


核心代码思路(二进制优化)

维护一个权重 t t t,每次循环之前将它初始化为 2 0 2^0 20,也就是 1 1 1。
每输入一种物品和它的个数 s s s,就执行一遍 f ( s ) f(s) f(s)操作,并将操作过程中产生的新物品存入动态数组内,并持续追踪物品总件数。

优化 · 代码

#include <iostream>
#include <vector> // 动态数组
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    vector<int> v{0}, w{0}, dp(m + 1, 0);
    // 这里使用动态数组是因为数据大小的不确定性和对空间的合理利用,以及考虑到初始化方便一点
    /*
        v:price
        w:value
    */
    int vi, wi, si; // 输入变量
    int t; // 维护权重
    int sizen = 0; // 追踪动态数组大小
    for (int i = 0; i < n; ++i) {
        cin >> vi >> wi >> si;
        t = 1; // 初始化权重值
        while (t <= si) { //二进制优化
            v.push_back(vi * t); // 价格乘以权重
            w.push_back(wi * t); // 价值也乘以权重
            si -= t; // 不要忘记去掉权重个数
            t *= 2; // 权重增加一级
            ++sizen; // 增加追踪器
        }
        if (si) { // 该种物品若还有剩余
            v.push_back(vi * si); // 价格乘以剩下物品数
            w.push_back(wi * si); // 价值乘以剩下物品数
            ++sizen; // 增加追踪器
        }
    }
    for (int i = 1; i <= sizen; ++i) // 遍历每一件物品
        for (int j = m; j >= v[i]; --j) // 逆序
            dp[j] = max(dp[j], dp[j - v[i]] + w[i]); // 01背包
    cout << dp[m] << endl; // 输出答案
}

有 “亿” 点点长,但是时间复杂度大大减低至 O ( l o g 2   n ) O(log2\, n) O(log2n),可以应对 s ≤ 2 30 s\le2^{30} s≤230 的数据范围。


若有任何建议或问题欢迎在评论区指出。

动态规划解二维多重背包问题 背包问题 背包问题是一个很经典的算法问题,根据其复杂程度不同又可分为01背包问题、完全背包问题、多重背包问题、二维背包问题等等。本文讲一讲二维多重背包问题的动态规划解法。 01背包问题 有N件物品和一个容量为V的背包。第i件物品的体积是a[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大 完全背包问题 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品... 阅读详情

相关推荐

Spark Graphx 项目实战(航班飞行网图分析)

航班飞行网图分析一.需要的技能点二.任务描述三.数据探索1.下载数据2.数据格式四.航班飞行网图问题分析1.构建航班飞行网图2.统计航班飞行网图中机场与航线的数量3.计算最长的飞行航线4.找出最繁忙的机场5.找出最重要的飞行航线6.找出最便宜的飞行航线五.代码实现 一.需要的技能点 Spark GraphX API Spark GraphX PageRank Spark GraphX Pregel 二.任务描述 需求概述: 探索航班飞行网图数据 构建航班飞行网图 使用Spark GraphX完成下列任

May_J_Oldhu的博客 1538

背包九讲——多重背包问题

多重背包问题是背包问题的一种扩展,与0/1背包问题和分数背包问题有些不同。在多重背包问题中,每种物品都有限定的数量,不再是仅有一个,而是有多个。问题的描述如下: 给定一个背包容量为C,有n种物品,每种物品有重量w[i]、价值v[i]和数量s[i]。目标是选择物品放入背包,使得它们的总重量不超过背包容量,并且总价值最大。

闻道有先后,术业有专攻 2990

3.ESP32S3 AP 配网开发实战:HTTP 与 WebSocket 协议实现

本文介绍了基于ESP32S3的AP配网实现方案,通过HTTP协议提供配网网页,利用WebSocket实现双向通信。详细解析了HTTP和WebSocket协议特点,阐述了SPIFFS文件系统存储HTML网页的方法,包括分区表配置、CMakeLists设置和代码实现。工程采用模块化设计,包含WiFi管理、Web服务器、IO扩展等组件,支持手机浏览器直接配网。测试验证了从AP热点连接到路由器切换的全流程,并总结了SPIFFS操作、WebSocket通信等核心优化点。该方案无需专用APP,适合物联网设备快速部署场景

u011697185的专栏 960

多重背包问题详解

多重背包问题详解

缪语博的博客 1775

自学ROS完成智能小车路径规划(一)--配置功能包

此系列文章记录本人学习ros的过程,将会尽可能详细以便自己能够看懂 参考系列:古月居老师,优快云,ROS tutorials,科大讯飞智能车竞赛线上赛 本文章的目标: 比较全局路径规划算法中A*算法与Dijkstra算法在该地图场景下的规划与运行时间 比较局部路径规划算法中DWA算法与TEB算法在该地图场景下的规划与运行时间 在同等参数条件下选择出各自适合的算法进行融合,经过比较,我将选择dijkstra算法作为全局路径规划算法,TEB算法为局部路径规划算法 第一步,按照科大讯飞的教程配置文件 科大讯

weixin_42621524的博客 9365

杰理SDK入门教程(一):SDK的四种编译方式的使用

杰理SDK提供了四种主要编译方式:1) JL Studio官方IDE,适合初学者;2) VS Code轻量级编辑器,需在SDK根目录打开;3) 命令行/脚本编译,灵活支持自动化;4) Code::Blocks开源IDE。每种方式都有详细操作指南,开发者可根据需求选择,初学者推荐从JL Studio开始熟悉编译流程。环境配置注意事项和编译成功标识均有明确说明,确保开发效率。

qq_42014563的博客 1148

多维多重背包问题_多重背包问题

题目有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。基本算法这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值...

weixin_35618325的博客 705

ICML-2025《Learning from Sample Stability for Deep Clustering》

用于指导深度聚类模型的训练,从而提升模型对困难样本的学习能力,最终改善整体聚类性能。首先,利用K-means在原始样本(非增强)的表征上进行聚类,得到初始聚类分配。其目的是鼓励模型对同一稳定样本在连续epoch中产生相近的表征。输出差异大)会产生更大的损失值,从而引导模型优先优化这些困难样本。此损失借鉴了NT-Xent损失的思想,但将正样本对定义为。最后,通过对比学习优化聚类中心,将同一聚类在。为了获得更具代表性的聚类中心,论文提出。为了学习更鲁棒的表征,模型在在线网络。首先,论文定义了样本。

Christo的博客 829

算法:多重背包问题dp

多重背包问题是背包问题的又一变种,它在0-1背包和完全背包问题的基础上增加了一个限制:每种物品。解决多重背包问题的基本思路是利用动态规划,其中最直观的方法是使用二维DP数组。次,而不是只有一次(0-1背包问题)或无限次(完全背包问题)。按照以上思路,并且按照0-1背包一样的思路,进行降维优化。的情况下的最大价值。和0-1背包问题的区别在于物品。由于这种方法会导致较高的时间复杂度,时间复杂度为。的值很大时,常常需要使用其他技巧。外,还有一个最大可用数量。可以被选取的次数最多是。种物品,在不超过重量。

m0_63997099的博客 1870

CMake是什么?为什么学习CMake

CMake的主要作用和重要性,包括其在跨平台构建中的应用、如何支持多种编译器及操作系统,以及它在管理大型和复杂项目中的优势。

小鱼酱的博客 3870

超详细!Linux 服务器 6 大核心性能测试(附自动脚本 + 结果分析秘籍)---Linux 服务器性能测试文档

本文档旨在提供 Linux 服务器的 CPU、内存和磁盘性能测试方案,包括详细的测试大纲、操作步骤以及一键化测试脚本,帮助管理员快速评估服务器性能状况。通过以上测试方案,可以全面评估 Linux 服务器的 CPU、内存和磁盘性能,为系统优化、容量规划和问题排查提供重要参考依据。使用 sysbench 进行 CPU 基准测试。使用 sysbench 测试内存读写性能。使用 fio 工具进行更全面的磁盘测试。使用 dd 命令测试磁盘顺序读写性能。使用 dd 命令测试内存读写速度。记录磁盘 IO 性能。

jundao1997的专栏 380

动态规划—多重背包问题

原文作者:弗兰克的猫 原文地址:【动态规划】多重背包问题 目录 说明 多重背包 递归法 动态规划 自上而下记忆法 自下而上填表法 总结 0|1说明 前面已经介绍完了01背包和完全背包,今天介绍最后一种背包问题——多重背包。这个背包,听起来就很麻烦的样子。别慌,只要你理解了前面的两种背包问题,拿下多重背包简直小菜一碟。如果没有看过前两篇01背包和完全背包的文章,强烈建议先阅读一下,因为本文跟前两篇文章关联性很强。 0|1多重背包 有N种物品和一个容量为T的背包,第i种物品最多有..

sanmi8276的博客 717

构建AI招聘助手:提高人才筛选与匹配效率

人工成本高:传统招聘过程涉及大量的简历筛选和面试安排,需要耗费大量的人力和时间。匹配效率低:简历筛选过程中,人为因素可能导致信息遗漏或误判,影响匹配效果。招聘周期长:从职位发布到最终录用,传统招聘流程往往耗时较长,影响企业业务发展。机器学习(Machine Learning)是一门让计算机通过数据和经验来学习并改进性能的学科。自然语言处理(Natural Language Processing,NLP)是计算机科学领域与人工智能领域中的一个重要方向,旨在让计算机能够理解和解释人类语言。

AI天才研究院 1303

多维多重背包问题_5种好用的背包问题算法模版汇总

一. 背包问题算法模版大全1. 01背包问题朴素做法// f[i][j]表示只看前i个物品,总体积是j的情况下,总价值最大是多少 #include <iostream> #include <algorithm> using namespace std; const int N = 1010; int v[N], w[N], f[N][N]; int main() { ...

weixin_29920047的博客 3324

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

66、多重背包问题II

即通过二进制转化的方式,将物品数量转化为多种物品数量的组合。比如假设物品有10个,即可转化为。接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。,这四种无论如何组合,组合成的状态都在10以内,就可以转化为0-1背包问题。求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。输出一个整数,表示最大价值。

weixin_44010641的博客 742

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

上一篇: 完全背包问题
下一篇: 匹配括号(平衡括号序列)
peter_code
博客等级 码龄5年 41粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值