多重背包问题——单调队列优化

news2024/11/15 21:53:59

一、多重背包问题

我们在之前的文章中曾经讲解过多重背包问题,当时我们讲解了两种方法,一种方法就是三重循环,这种方法最为朴素好想。但是这种方法的时间复杂度非常高,后来我们想到了二进制优化的方式。那么今天我们将再介绍一种更好的优化方式——单调队列优化。

在讲解这种优化方式之前,建议大家先去看看作者之前对另外两种方法的讲解,传送门:多重背包问题(详解二进制优化原理)

二、单调队列优化

1、思路

我们观察一下多重背包问题中的转移方程,如下图所示:
在这里插入图片描述
我们发现j,j-v等等都是同余于v的。也就是说,我们的转移方程只会用到余数相等的状态。基于这个观点,我们将容量从0–m按照余数分类。
就像图中的黑色笔记。

那么对于任意一个状态,在容量允许的条件下,我们会向前数s个,(s是物品的个数,这里最多数s个,如果容量装不下了就会小于s个)。然后我们在这s个余数相同的状态中选出一个最大值。如图中的红色框所示。我们可以将这个框看作一个滑动窗口,因为随着图中系数y的增加,这个窗口也在向右移动,即滑动。

那么我们如何构造这个滑动窗口呢?

我们刚刚将0到m之间的数字按照余数分为了若干组,那么现在我们拿出一组写成转移方程,观察一下如何构造滑动窗口。如下图所示:
在这里插入图片描述
我们的单调队列存储的是下标,也就是说下标对应着一个固定的状态值。但我们看图中的状态,下标为x的状态所对应的值都不一样。也就是说下标所对的状态都变,既然在变,我们的窗口中的值就很难比较大小。

因此,我们需要经过一个变化,让窗口内下标所对的状态值不变。那么怎么变呢?很简单,我们将后面加的几w提出来。

在这里插入图片描述
此时我们就发现,我们max中比较的对象都一致了,这样的话,我们就可以将这些数构造成一个单调减的滑动窗口。

我们还可以观察到,窗口内的状态在不断地-w,但是系数都不同,那么这个系数有什么规律呢?
在这里插入图片描述知道这个规律的目的在于滑动窗口内元素的比较。比如我们碰到一个新的状态值,我们需要计算出其相对于余数的偏移量然后对这个状态进行减几个w的操作,之后这个值才能放到窗口内。

此时我们将x+sv换成j,因为我们循环中肯定是用j来枚举的,所以换成j来表达易于后面写代码。
在这里插入图片描述
但是我们还还有不选这个物品的情况,需要将其加入到队列中,即下图所示,
在这里插入图片描述
我们需要利用这个式子判断f[i-1][j]是否有可能是最大值,即将这个数加入队列中。然后我们就可以根据最值更新f[i][j]了,即:

 f[i][j]=f[i-1][q[hh]]+((j-q[hh])/v[i])*w[i];

接下来我们就可以写代码了,按照余数分组,按照偏移量构造队列,选出最值得到当前状态。

2、代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e3+10,M=2e4+10;
int f[N][M],v[N],w[N],s[N],q[M];
int n,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)scanf("%d%d%d",v+i,w+i,s+i);
    for(int i=1;i<=n;i++)
    {
        for(int x=0;x<v[i];x++)
        {
            int hh=0,tt=-1;
            for(int j=x;j<=m;j+=v[i])
            {
                if(hh<=tt&&q[hh]<j-s[i]*v[i])hh++;
                while(hh<=tt&&f[i-1][q[tt]]-(q[tt]-x)/v[i]*w[i]<f[i-1][j]-(j-x)/v[i]*w[i])tt--;
                q[++tt]=j;
                f[i][j]=f[i-1][q[hh]]+((j-q[hh])/v[i])*w[i];
            }
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java实习------Java基础2

基础语法基本数据类型 熟悉Java有哪些数据类型定义&#xff1a;Java语言是强类型语言&#xff0c;对于每一种数据都定义了明确的具体的数据类型&#xff0c;在内存中分配了不同大小的内存空间。Java语言提供了八种基本类型。六种数字类型&#xff08;四个整数型&#xff0c;两个…

18. 循环语句while,for语句的详解

python 中的循环语句只有 for 和 while两种&#xff0c;没有do…while循环&#xff0c;这与c/c是不同的。 1. while循环 (1) 语法格式 while <condition>&#xff1a;...# demo, 使用while循环累加1-100的和。 sum 0 counter 1 while counter < 100:sum counterco…

Java 23种设计模式(4.创建者模式-建造者模式)

代码分析 结构图 代码 public class Product {//产品类,多个部件构成List <String> parts new ArrayList<>();public void Add(String part){parts.add(part);}public void show(){System.out.println("creat parts");for(String part:parts){System…

第十届蓝桥杯省赛 C++ B/C组 - 等差数列

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;等差数列 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥杯的小伙伴整理常考算法题解&#xff0c;祝大家…

STL分析(九 适配器)

容器适配器&#xff1a;stack&#xff0c;queue stack和queue内含一个deque 函数适配器 binder2nd绑定第二参数 调用的过程中&#xff0c;算法count_if函数读取了迭代器头尾指针后&#xff0c;读取第三个参数为functor object即仿函数。因此在其参数为bind2nd(less< int&g…

LeetCode220123_140、117. 填充每个节点的下一个右侧节点指针 II

给定一个二叉树struct Node {int val;Node *left;Node *right;Node *next;}填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NULL。初始状态下&#xff0c;所有 next 指针都被设置为 NULL。进阶…

双指针思想,c语言

问题导入 对一个给定的自然数M&#xff0c;求出所有的连续的自然数段&#xff0c;这些连续的自然数段中的全部数之和为M。 例如&#xff1a;1998 1999 2000 2001 2002 10000&#xff0c;所以从1998到2002的一个自然数段为M 10000的一个解。 输入格式 第一行&#xff0…

JQuery总结(三)

jQuery 事件 on可以同时绑定多个事件&#xff0c;并且动态添加的元素也会自动添加事件 自动触发事件&#xff1a; 案例1&#xff1a; <style>div{width: 100px;height: 200px;background-color: violet;transition: all .5s;}.current{width: 200px;height: 100px;ba…

ARP渗透与攻防(三)之流量分析

ARP攻击-流量分析 ARP渗透与攻防(一)之ARP原理 ARP渗透与攻防(二)之断网攻击 系列文章 1.环境准备 1.kali作为攻击机 2.win10作为靶机 IP地址&#xff1a;192.168.110.11 3.网关 IP地址&#xff1a;192.168.110.1 2.kali数据包转发 出于安全考虑&#xff0c;Linux系统默…

使用Py6S计算瑞利反射率教程

1、前言那么&#xff0c;首先&#xff0c;什么是瑞利反射率&#xff1f; 好吧&#xff0c;这是由大气中的瑞利散射引起的反射率&#xff08;在大气层顶部测量&#xff09;。 这是大气中气体分子对光的波长依赖性散射——这是光穿过大气时不可避免的结果。所以&#xff0c;关于如…

【2022年度总结与2023展望】---22年故事不长,且听我来讲

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f4e3;系列专栏&#xff1a;故事 文章目录前言一、高考前及高考后的暑假二、大一上的生活2.1 感受校园美景2.2 进入ACM实验室2.3 比赛和比赛经历三、那些帮助过我的人…

AlmaLinux 9 安装Oracle GraalVM Enterprise Edition 22.3.x

今天我们尝试一下在AlmaLinux 9 安装Oracle GraalVM Enterprise Edition 22.3.x。 GraalVM Enterprise 22是包含新功能的最新版本&#xff0c;但不是Long-Term-Support (LTS) 版本。 注意&#xff1a;下载Oracle GraalVM Enterprise Edition需要有Oracle账户&#xff0c;如果没…

六、pyhon操作mysql篇(黑马程序猿-python学习记录)

黑马程序猿的python学习视频&#xff1a;https://www.bilibili.com/video/BV1qW4y1a7fU/ 目录 1. 下载pymysql 2. 新建数据库 3. mysql服务器版本查询 4. 执行非查询性质的SQL 5. 执行查询性质的sql ​​​​​​​6. 执行新增sql 1. 下载pymysql 右下角点击版本 选择解释器设…

基于Markdown文件的个人wiki

节后第一更&#xff0c;老苏祝您兔年大吉&#xff0c;幸福安康&#xff0c;事事顺心&#xff01; 什么是 wikmd &#xff1f; wikmd 是一个基于文件的 wiki&#xff0c;旨在简化。这些文档完全用 Markdown 编写&#xff0c;使用 pandoc 将 Markdown 文件转换为 html5。 &#…

WPF__Viewport3D 学习记录

1、相机的摆放位置和姿态 Position 是指定照相机的位置&#xff0c;注意Z轴的坐标&#xff0c;如果Z轴值大于模型的Z轴值&#xff0c;那么镜头就在物体前面&#xff0c;反之在物体后面。因此&#xff0c;你可以设置 LookDirection 来调整相机的方向&#xff0c;Position是相机的…

NodeJS 之模块化

NodeJS 之模块化参考描述模块化优点分类加载模块作用域优点自定义模块modulemodule.exportsexports 与 module.exports示例示例一示例二示例三建议CommonJS规范CommonJS参考 项目参考搜索引擎Bing哔哩哔哩黑马程序员 描述 项目描述操作系统Windows 10 专业版NodeJS18.13.0 模…

MU-MIMO是什么

欢迎来到东用知识小课堂&#xff01;1.什么是MIMOMIMO&#xff1a;Multiple-Intput Multiple-Output,即多入多出系统&#xff0c;这里的入和出是相对于发射天线和接受天线构成的天线系统来讲。通常的通信系统是单发单收&#xff0c;也就是SISO&#xff1a;Single-Input Single-…

【Bp2Lua】蓝图定义 Struct 和 Enum

【Bp2Lua】蓝图定义 Struct 和 Enum 背景 Enum Struct 结论 enum 可以保持不动&#xff0c;使用枚举值注释&#xff1b;如果C特别需要访问 enum&#xff0c;可以 C 化struct 必须 C 化 坑 首先&#xff0c;从字段访问都有字段名的坑&#xff0c;名字可以有空格&#xff0c…

maven-3-java调用python程序

【Java】使用Java调用Python的四种方法 fastjson的使用——JSON字符串、JSON对象、Java对象的互转 使用idea给Java程序打jar包&#xff08;超简单 超详细&#xff09; 1 环境准备 (1)新建Maven类型的项目&#xff0c;D:\processPython。 右键src/main/java&#xff0c;创建ja…

基于深度学习的文本分类6大算法-原理、结构、论文、源码打包分享

导读&#xff1a;文本分类是NLP领域一项基础工作&#xff0c;在工业界拥有大量且丰富的应用场景。传统的文本分类需要依赖很多词法、句法相关的human-extracted feature&#xff0c;自2012年深度学习技术快速发展之后&#xff0c;尤其是循环神经网络RNN、卷积神经网络CNN在NLP领…