每周一算法:背包问题(三)多重背包

多重背包

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

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

输出最大价值。

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

10

提示

0 < N ≤ 1000 , 0 < M ≤ 2000 0<N≤1000,0<M\le2000 0<N1000,0<M2000

0 < v i , w i , s i ≤ 2000 0<v_i,w_i,s_i≤2000 0<vi,wi,si2000

算法思想

状态表示

多重背包的特点是第 i i i种物品最多有 s i s_i si件。仍可以采用01背包的思想,将处理每种物品作为一个阶段,考虑在不同背包容量情况下的最大价值,将其状态定义为 f [ i ] [ j ] f[i][j] f[i][j],表示对于 i i i种物品,在背包容量为 j j j的情况下,背包获得的最大价值。

状态计算

在当前阶段,对于第 i i i种物品来说,有多种情况可以选择:

  • 放入 0 0 0件,此时的最大价值为前 i − 1 i-1 i1种物品,在背包容量为 j j j的情况下的最大价值 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j]
  • 放入 1 1 1件,此时背包的最大价值为前 i − 1 i-1 i1种物品,在背包容量为 j − v i j-v_i jvi的情况下的最大价值 f [ i − 1 ] [ j − v i ] + w i f[i-1][j-v_i]+w_i f[i1][jvi]+wi
  • 放入 2 2 2件,此时背包的最大价值为前 i − 1 i-1 i1种物品,在背包容量为 j − 2 × v i j-2\times v_i j2×vi的情况下的最大价值 f [ i − 1 ] [ j − 2 × v i ] + 2 × w i f[i-1][j-2\times v_i]+2\times w_i f[i1][j2×vi]+2×wi
  • 放入 k k k件,此时背包的最大价值为前 i − 1 i-1 i1种物品,在背包容量为 j − k × v i j-k\times v_i jk×vi的情况下的最大价值 f [ i − 1 ] [ j − k × v i ] + k × w i f[i-1][j-k\times v_i]+k\times w_i f[i1][jk×vi]+k×wi
  • 放入 s i s_i si件,此时背包的最大价值为前 i − 1 i-1 i1种物品,在背包容量为 j − s i × v i j-s_i\times v_i jsi×vi的情况下的最大价值 f [ i − 1 ] [ j − s i × v i ] + k × w i f[i-1][j-s_i\times v_i]+k\times w_i f[i1][jsi×vi]+k×wi

以上情况的前提是背包能够装得下 k k k件第 i i i种物品,也就是背包容量 j ≥ k × v i j\ge k\times v_i jk×vi。那么, f [ i ] [ j ] f[i][j] f[i][j]应该选择所有情况的最大值,即 f [ i ] [ j ] = max ⁡ { f [ i − 1 ] [ j − k × v i ] + k × w i } f[i][j] = \max\{f[i-1][j-k\times v_i]+k\times w_i\} f[i][j]=max{f[i1][jk×vi]+k×wi},其中 0 ≤ k ≤ s i 0\le k\le s_i 0ksi,并且 k × v i ≤ j k\times v_i \le j k×vij

初始状态

f [ 0 ] [ 0 ] f[0][0] f[0][0]表示将前 0 0 0种物品装入容量为 0 0 0的背包中的产生的最大价值为 0 0 0

时间复杂度

  • 状态数 n × m n\times m n×m
  • 状态计算时需要枚举第 i i i件物品的数量 s i s_i si,时间复杂度为 O ( s i ) O(s_i) O(si)

总的时间复杂的为 O ( n × m × s ) O(n\times m\times s) O(n×m×s)

代码实现

#include <iostream>
using namespace std;
const int N = 1010, M = 2010;
int f[N][N];
int main(){
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        int v, w, s;
        cin >> v >> w >> s;
        for(int j = 0; j <= m; j++)
        {
            for(int k = 0; k <= s && k * v <= j; k++)
            {
                f[i][j] = max(f[i][j], f[i - 1][j - k * v] + k * w);
            }
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

算法优化

根据上述状态转移方程,考虑能否像完全背包一样的思路进行优化呢?

f [ i ] [ j ] = max ⁡ { f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − v ] + w , f [ i − 1 ] [ j − 2 × v ] + 2 × w + . . . + f [ i − 1 ] [ j − s × v ] + s × w } f[i][j] = \max\{f[i-1][j], f[i-1][j-v]+w, f[i-1][j-2\times v]+2\times w+...+f[i-1][j-s\times v]+s\times w\} f[i][j]=max{f[i1][j],f[i1][jv]+w,f[i1][j2×v]+2×w+...+f[i1][js×v]+s×w}

可得, f [ i ] [ j − v ] = max ⁡ { f [ i − 1 ] [ j − v ] , f [ i − 1 ] [ j − 2 × v ] + w , f [ i − 1 ] [ j − 3 × v ] + 2 × w + . . . + f [ i − 1 ] [ j − ( s + 1 ) × v ] + ( s + 1 ) × w } f[i][j - v] = \max\{f[i-1][j - v], f[i-1][j-2\times v]+w, f[i-1][j-3\times v]+2\times w+...+f[i-1][j-(s+1)\times v]+(s+1)\times w\} f[i][jv]=max{f[i1][jv],f[i1][j2×v]+w,f[i1][j3×v]+2×w+...+f[i1][j(s+1)×v]+(s+1)×w}

f [ i ] [ j − v ] f[i][j - v] f[i][jv] f [ i ] [ j ] f[i][j] f[i][j]和对比可以发现,多了一项 f [ i − 1 ] [ j − ( s + 1 ) × v ] + ( s + 1 ) × w f[i-1][j-(s+1)\times v]+(s+1)\times w f[i1][j(s+1)×v]+(s+1)×w。如果计算出 f [ i ] [ j − v ] f[i][j - v] f[i][jv],那么是否能得到 f [ i ] [ j ] f[i][j] f[i][j]呢?举个栗子:

在这里插入图片描述

也就是说,知道前 s + 1 s+1 s+1项的最大值并不能计算出前 s s s项的最大值,因此不能采用完全背包的思想来优化多重背包。

二进制枚举

在计算状态的过程中,需要枚举第 i i i种物品的数量 [ 0 , s i ] [0,s_i] [0,si],这里采用一种更高效的枚举方式——二进制枚举。例如当 s i = 1023 s_i=1023 si=1023时,可以将第 i i i种物品“打包”为:

  • 0 0 0件一组
  • 1 1 1件一组
  • 2 2 2件一组
  • 4 4 4件一组
  • 512 512 512件一组

通过上述组与组之间的组合,可以表示出 [ 0 , 1023 ] [0,1023] [0,1023]之间的任意一个数。如果把每组物品看成是01背包中的一种物品(仅能选择一次),那么就相当于用 10 10 10个新物品来表示原来的第 i i i个物品,通过组合这 10 10 10个新物品就可以枚举出第 i i i个物品的全部方案。

时间复杂度

  • 状态数 n × m n\times m n×m
  • 通过上述思想,原来要枚举 s s s次,现在只需要枚举 l o g s logs logs

总的时间复杂的为 ( n × m × l o g s ) (n\times m\times logs) (n×m×logs)

代码实现

#include <iostream>
using namespace std;
const int N = 1010 * 12, M = 2010;
int v[N], w[N];
int f[M];
int main()
{
    int n, m, k = 0;
    cin >> n >> m;
    for(int i = 1; i <= n; i ++)
    {
        int a, b, s;
        cin >> a >> b >> s;
        //二进制拆分
        for(int j = 1; j <= s; j *= 2)
        {
            v[++ k] = j * a;
            w[k] = j * b;
            s -= j;
        }
        //拆分后还有剩余
        if(s) v[++ k] = s * a, w[k] = s * b;
    }
    n = k; //拆分后实际的物品数量
    //01背包
    for(int i = 1; i <= n; i ++)
        for(int j = m; j >= v[i]; j --)
            f[j] = max(f[j], f[j - v[i]] + w[i]);
    cout << f[m];
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/215163.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt 如何操作SQLite3数据库?数据库创建和表格的增删改查?

# 前言 项目源码下载 https://gitcode.com/m0_45463480/QSQLite3/tree/main # 第一步 项目配置 平台:windows10 Qt版本:Qt 5.14.2 在.pro添加 QT += sql 需要的头文件 #include <QSqlDatabase>#include <QSqlError>#include <QSqlQuery>#include &…

Dockerfile脚本编写流程及示例

学习dockerfile指令 Dockerfile 指令 说明 FROM 指定基础镜像 MAINTAINER 声明镜像的维护者 LABEL 添加元数据标签 RUN 在容器中执行命令 CMD 容器启动后默认执行的命令 EXPOSE 暴露容器的端口 ENV 设置环境变量 ADD 将文件、目录或远程文件添加到容器中 COP…

以用户为中心的前端性能

1. 简介 前端性能跟用户体验息息相关。举个栗子&#xff0c;当你打开乘车码扫码进站&#xff0c;网页白屏了很久才加载出来&#xff0c;延误了乘车时间&#xff1b;当你在微信抢红包时&#xff0c;点击按钮后延迟了一会才开始转圈圈&#xff0c;最终没抢到红包。当出现这样的情…

医疗器械设备模组的具体应用

直线模组是一种高精度、高速度的精密传动元件&#xff0c;目前被广泛应用在各种工业自动化领域&#xff1b;尤其是在激光加工、电子制造、医疗设备、物流设备和机器人等行业中&#xff0c;都发挥着重要作用&#xff0c;接下来我们看看医疗器械设备模组的具体应用吧&#xff01;…

echarts笔记-GeoJSON河北数据下并裁剪为冀北地图并使用echarts加载

首先找个网站把河北的GeoJSON数据下载下来&#xff0c;我用的是这个&#xff0c;理论上任意一个都可以 DataV.GeoAtlas地理小工具系列 将json数据下载后&#xff0c;进行裁剪&#xff0c;仅保留冀北数据。 如下&#xff0c;我裁剪的数据&#xff1a; {"type": &qu…

【备忘干货】c/c++ (wasm)和js互相调用记录

c/c&#xff08;wasm&#xff09;和js互相调用记录 废话 :)准备工作&#xff1a;安装Emscripten初探&#xff1a;C(wasm)之hello world进一步探究&#xff1a;接口调用1.js调用c&#xff0c;一些基本类型的传递&#xff08;char*&#xff0c;int&#xff0c;float&#xff09;以…

企业安全生产管理系统功能介绍

安全生产管理系统通过借助信息化手段和技术算法&#xff0c;建立了一个集安全风险监测预警、安全分区管理、隐患排查治理、特殊作业、人员定位管控于一体的“一张图”平台。平台可以实现企业安全生产管控的全面监管&#xff0c;推动公司生产安全业务的动态管理、集中管理和协同…

014 OpenCV canny边缘检测

一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、canny原理 OpenCV中的Canny边缘检测算法是一种基于图像处理的计算机视觉技术&#xff0c;主要用于检测图像中的边缘。Canny边缘检测算法的原理是通过计算图像中像素点之间的梯度值来…

GPT4-Turbo技术原理研发现状及未来应用潜力分析报告

今天分享的是GPT4-Turb系列深度研究报告&#xff1a;《GPT4-Turbo技术原理研发现状及未来应用潜力分析报告》。 &#xff08;报告出品方&#xff1a;深度行业分析研究&#xff09; 报告共计&#xff1a;46页 图像理解能力提升&#xff1a;三大视觉学习方法  为打造视觉大模…

53.redis分布式缓存

目录 一、单机安装Redis。 二、Redis主从集群。 2.1.集群结构 2.2.准备实例和配置 2.3.启动 2.4.开启主从关系 2.5.测试 三、搭建哨兵集群。 3.1.集群结构 3.2.准备实例和配置 3.3.启动 3.4.测试 四、搭建分片集群。 4.1.集群结构 4.2.准备实例和配置 4.3.启动…

网络层之SDN基本概念、路由算法和路由协议

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

淘宝API接口申请指南

一、申请条件数据接口 已注册淘宝账号并完成实名认证&#xff1b;拥有良好的淘宝信用记录&#xff1b;符合淘宝API接口的相关规定。 二、申请流程 登录淘宝账号&#xff0c;进入“卖家中心”页面&#xff1b;点击“我要开店”-“申请应用”&#xff0c;选择“淘宝API”&…

LangChain(0.0.340)官方文档五:Model

LangChain官网、LangChain官方文档 、langchain Github、langchain API文档、llm-universe 文章目录 一、Chat models1.1 Chat models简介1.2 Chat models的调用方式1.2.1 环境配置1.2.2 使用LCEL方式调用Chat models1.2.3 使用内置Chain调用Chat models 1.3 缓存1.3.1 内存缓存…

【Linux】:线程(一)概念

线程概念 一.线程1.简单理解2.一些疑问3.简单说一下优缺点&#xff0c;异常和用途 二.进程VS线程1.进程和线程的联系和区别2.phread线程库(创建)3.线程的LWP4.线程等待5.线程终止 三.C11里的线程四.创建线程最底层接口 一般教程里定义线程&#xff1a;是进程内的一个执行分支。…

PicoScope 7 软件报警功能可实现自动保存和循环捕捉

最近很多用户提到&#xff0c;怎么让虹科Pico示波器采集信号到缓冲区满了之后自动保存在电脑里&#xff0c;然后清出缓存空间继续采集&#xff0c;如此循环工作。这里不得不向大家介绍一下PicoScope软件的强大功能之一&#xff1a;报警功能&#xff01; 报警在软件的工具菜单下…

盘点最近两个世纪那些搞怪又精彩的专利

人类的创新是无止境的&#xff0c;下面收集的就是最近两个世纪全球那些奇怪搞笑、精彩的6项专利。 小胡子卫士 (1876) 这款“胡须防护罩”由 VA.Gates 于 1876 年获得专利&#xff0c;是在节日盛宴期间保护胡须的巧妙解决方案。“弯曲和凹形的护罩&#xff0c;可以由硫化橡胶、…

visual c++ 2019 redistributable package

直接安装下面包只有24M Microsoft Visual C Redistributable 2019 x86: https://aka.ms/vs/16/release/VC_redist.x86.exe x64: https://aka.ms/vs/16/release/VC_redist.x64.exe ———————————————— 版权声明&#xff1a;本文为CSDN博主「kpacnB_Z」的原创文章…

堆排序(C语言)

前言 在上一篇内容&#xff1a;大小堆的实现&#xff08;C语言&#xff09;&#xff0c;我们实现了关于创建大小堆的各函数与实现。但是如果突然要使用一个堆排序但是此时并没有一个现成的堆&#xff0c;这就需要花费时间去新建实现堆的插入删除这些操作从而实现一个堆&#xf…

Oracle-CDB容器数据库修改service_names踩坑

前言: 最近在对一套Oracle容器数据库进行迁移测试时&#xff0c;为了保持新环境与旧环境的服务名一致&#xff0c;需要在新环境添加旧环境的服务名&#xff0c;在CDB的根容器通过service_name参数添加旧环境的服务名之后&#xff0c;发现数据库PDB的服务名全部被注销&#xff0…

今日思考 -- 创新领导力(CIO)读后感

收获3个观点&#xff1a; 1 &#xff0c;IT DT 商业&#xff0c;才是未来IT人的出路之一 &#xff01; 2 &#xff0c;在CXO中&#xff0c;CIO像CEO一样&#xff0c;具备了整个企业的业务全视角 &#xff0c;同时也更具解决 ‘’系统性‘’问题的能力 &#xff01; 3 &…