首页 > 编程语言 >[算法学习笔记][刷题笔记] 单调队列优化 dp

[算法学习笔记][刷题笔记] 单调队列优化 dp

时间:2023-08-27 17:12:11浏览次数:51  
标签:int sum back 笔记 队列 单调 include dp 刷题

前置知识 · 单调队列

单调队列顾名思义,一般用于解决 滑动RMQ问题。它的原理非常简单。我们维护一个双端队列,这个双端队列 只维护可能成为区间最值的元素。

最基础的单调队列,例如滑动窗口。直接依据题意维护即可。

这里提供单调队列模板(STL deque 版)

单调队列模板(STL deque 版)
    for(int i=1;i<=n;i++)
    {
        if(!q1.empty()&&i-q1.front() >= k) q1.pop_front(); //越界“退役”
        while(!q1.empty()&&a[q1.back()] > a[i]) //比当前元素大, 不可能成为区间最小值。直接pop
        {
            q1.pop_back();
        }
        q1.push_back(i); 
        if(i>=k) cout<<a[q1.front()]<<" "; //依据题意输出答案即可,不同题目处理不同。
    }

单调队列具体内容见:SXqwq的单调队列学习笔记

单调队列优化 dp

单调队列优化 dp,往往是将朴素 dp 方程转换成可以用单调队列维护的形式。这里给出几个例题。

例题1:Luogu P1714 切蛋糕

在数列 \(\{p_n\}\) 中,找出一个子段 \([l,r](r-l+1\le m)\),最大化 \(\sum\limits_{i=l}^rp_i\)。

Solution

此类问题属于最大不定长字段和问题

朴素做法是对于每一个 \(l\),枚举长度,时间复杂度 \(O(n^2)\),无法接受。

如果我们预处理 \(sum_i\) 表示数组前 \(i\) 项的前缀和,则\(max(\sum\limits_{i=l}^rp_i)=max(sum_r)-min(sum_l)(r > l)\)

对于每次处理 \(sum_r\) 固定,也就是求 \(r\) 前面最小的 \(sum_l\)。我们知道单调队列用于位于区间最值,排除无用决策。所以我们可以用单调队列维护区间长为 \(k\) 的最小 \(sum_l\)。

至此,我们就可以将本题使用单调队列维护,由于每个元素只会入队出队一次,时间复杂度 \(O(n)\) 。

实现
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <deque>
using namespace std;
const int N = 1000010;
int sum[N];
int n,m;
deque <int> q;
int maxn = -1;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int a;
        cin>>a;
        sum[i] = sum[i-1] + a;
    }
    q.push_back(0);
    for(int i=1;i<=n;i++)
    {
        if(q.size() && i - q.front() > m) q.pop_front();
        while(q.size()&&sum[q.back()] > sum[i]) q.pop_back();
        maxn = max(maxn,sum[i]-sum[q.front()]);
        q.push_back(i);
    }
    cout<<maxn<<endl;
    return 0;
}

例题2:Luogu P2629 好消息,坏消息

题目链接:Luogu P2629

Solution

首先,本题存在环,直接断环成链。处理更加方便。

因为在任何时候老板的心情不能小于0,初始心情为0,所以任何时候位置 \(i\) 的前缀和不能小于0(断环为链后)。同理设 \(sum_i\) 表示前 \(i\) 个 数的前缀和。

接下来,我们就将题目转化为:求有多少个 \(k\) 满足 \(\forall sum_i \geq sum_k(i \in \{1,n\times 2\})\) (显然这里已经断环为链)

那么如何处理呢?我们需要对于每个 \(i\) 都判断一遍吗?显然不需要,因为中间哪怕出现一个小于 \(0\) 的数,就不合法。所以我们使用单调队列维护一个最小的 \(sum_i\) 即可。

需要注意我们每次需要维护区间 \([k,n+k-1]\) 的最小值,然后判断 \(sum_i-sum_k\) 是否小于 \(0\) 即可。

实现
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <deque>
using namespace std;
const int N = 100000010;
int n,m;
int sum[N];
int ans[N];
int a[N];
deque <int> q;
int cnt = 0;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>a[i];
        a[i+n] = a[i];
        sum[i] = sum[i-1] + a[i];
    }
    for(int i=n+1;i<=n*2-1;i++) sum[i] = sum[i-1] + a[i];
    q.push_back(1);
    for(int i=2;i<=n*2-1;i++)
    {
        while(q.size() && sum[i] < sum[q.back()]) q.pop_back();
        q.push_back(i);
        while(q.size() && i - q.front() > n) q.pop_front(); //超过 n,即开始统计答案,越界。
        if(i >= n) {ans[i] = sum[q.front()] - sum[i-n];}
    }
    for(int i=n;i<=n*2-1;i++) if(ans[i] >= 0) cnt++;
    cout<<cnt<<endl;
    return 0;
}

标签:int,sum,back,笔记,队列,单调,include,dp,刷题
From: https://www.cnblogs.com/SXqwq/p/17660493.html

相关文章

  • 操作系统学习笔记(一)——硬件
    一、冯诺依曼模型定义计算机基本结构为5个部分:存储器、运算器、控制器、输入设备、输出设备。运算器和控制器在中央处理器(CPU)里,存储器就是常见的内存,输入输出设备就是计算机外接的设备,比如键盘是输入设备,显示器是输出设备。1、内存 程序和数据存储在内存里,存储数据的基本单......
  • openGauss学习笔记-52 openGauss 高级特性-LLVM
    openGauss学习笔记-52openGauss高级特性-LLVMopenGauss借助LLVM(LowLevelVirtualMachine)提供的库函数,依据查询执行计划树,将原本在执行器阶段才会确定查询实际执行路径的过程提前到执行初始化阶段,从而规避原本查询执行时候伴随的函数调用、逻辑条件分支判断以及大量的数据读取......
  • Docker方式安装wordpress
    准备拉取wordpress,mysql镜像dockerpullwordpressdockerpullmysql启动wordpress,mysql容器启动wordpress容器,将容器80端口映射到主机端口8080dockerrun-d-p8080:80--namewordpress01wordpress启动mysql容器,映射数据库端口到主机的3306,设置root密......
  • go 进阶训练营 微服务可用性(下)笔记
    降级:减少工作量,丢弃不重要的请求。确定具体采用哪个指标作为流量评估和优雅降级的决定性指标:如CPU、延迟、队列长度、线程数量、错误等当服务进入降级时,需要执行什么动作?流量抛弃或者优雅降级应该在服务的哪一层实现?是否需要在整个服务的每一层都实现,还是可以选择某个高层面......
  • [刷题记录Day22]Leetcode二叉树
    No.1题目二叉搜索树的最近公共祖先思路递归法BST特性如何利用?在BST中,公共祖先一定在p、q数值范围的中间利用BST特性定向搜索注意递归遍历一条边和遍历整棵树的写法不同递归分析返回值:节点,参数:当前节点,p,q终止逻辑:发现当前节点为空,则直接返回当前节点;为什么不用判断p......
  • Pytest+Jenkins 学习笔记
    Pytest+Jenkins学习笔记在软件测试工作中,单元测试通常是由开发人员执行的、针对最小单元粒度的组件测试,在完成了单元粒度的测试任务之后,通常就需要交由专职的测试人员将这些单元级的组件放到粒度更大的功能组件或子系统中来进行整合性的测试了。在专业术语中,粒度介于单元测试与......
  • mormot2 笔记(四) Services的使用
    constructorTMyRestServer.Create(Port:Word);begininheritedCreate;FRestServerDB:=TRestServerDB.Create(TOrmModelFactory.ModelInstance,SQLITE_MEMORY_DATABASE_NAME);FRestServerDB.DB.Synchronous:=smOff;FRestServerDB.DB.LockingMode:=lmExc......
  • 学习笔记413—python实现BP神经网络进行预测和误差分析(附源代码)
    python实现BP神经网络进行预测和误差分析(附源代码)反向传播算法也称为BP神经网络,是一种带有反馈的神经网络反向学习方法,它可以对神经网络的各层上的各个神经元的各个神经元之间的连接权重进行不断迭代修改,使神经网络将输入数据转换成期望的输出数据 BP神经网络的学习过程由正向......
  • Mongodb 笔记
    MongoDb:非关系型数据库,基于分布式文件存储的开源数据库系统,在高负载的情况下,添加更多的节点,可以保证服务器的性能MongoDB操作 文档的数据结构和JSON基本一样。所有存储在集合中的数据都是BSON格式。BSON是一种类似JSON的二进制形式的存储格式,是BinaryJSON的......
  • C/C++百日刷题第三天
    一、选择题1.1、如下代码输出的是什么()chara=101;intsum=200;a+=27;sum+=a;printf("%d\n",sum);A:327B:99C:328D:72题解:这题考察对常见数据结构存储的理解,容易出错在a+=27这个地方,char类型的数据存储范围为-128--127,当a+27之后会超过数据存储范围,a就变为-128,sum加......