单调队列与DP

单调队列 & 单调队列优化DP & 题目推荐 来看一道题 扫描 题目描述 有一个 1 ∗ n 的矩阵,有 n 个正整数。 现在给你一个可以盖住连续的 k 的数的木板。 一开始木板盖住了矩阵的第 1 ∼ k 个数,每次将木板向右移动一个单位,直到右端与第 n 个数重合。 每次移动前输出被覆盖住的最大的数是多少。 输入格式 第一行两个数,n,k,表示共有 n 个数,木板可以盖住 k 个数。 第二行 n 个数,表示矩阵中的元素。 输出格式 共 n ... 阅读详情

      以前以为单调队列只能求解区间最值的问题,没想到还能用于dp的优化,使循环的维度直接减去1维,将原先O(n³)复杂度的方程优化成O(n²)。最近几天刷了几道相关的题目,遂小小的总结一下以防以后忘掉。

  首先,单调队列是一种单调递增或者单调递减的队列(如1、3、5、7、9是单调递增的队列)。对于单调队列的操作主要有三种:插入(从队尾插入一个新元素)、取最值(取队头的元素)、删除(从队头开始删除已经失效的元素)。由于操作较简单,一般采用一维数组的方式来存储队列,用head、tail分别表示队头和队尾。操作的代码如下(以单调递增队列为例,即队首为最小值)。

    int q[100];
    int head = 0, tail = 0, x;
    //插入
    while(head < tail && q[tail-1] > x) tail--;
    q[tail++] = x;
    //取值
    printf("%d\n", q[head]);
        但是,单调队列不是对所有的状态方程都有效,只有形如dp[i] = max{f[k]}+g[i]或dp[i] = min{f[k]}+g[i],其中(k < i && g[i]与k无关)的方程才能进行优化,优化对象为f[k]。

          例如,方程dp[i] = max{dp[j]}+i+j,其中(0<=i < 100, 0 <= j < i)。变换方程式,得dp[i] = max{dp[j]+j}+i。当i=1时,j只可取0,即dp[1] = dp[0]+1+0;当i=n时,j可取0-(n-1)。即i每增加1,j的范围也增加1。当i=n时,向队列中加入元素(dp[n-1]+n-1),这样每次取出的队头元素再加上i,即为dp[i]的值。代码如下

    dp[0] = 0;
    for(int i = 1; i <= 100; i++) {
       int x = dp[i-1] + (i-1);
       while(head < tail && q[tail-1] < x) tail--;
       q[tail++] = x;
       dp[i] = q[head]+i;
    }


      下面是几个题目

    1.https://vjudge.net/problem/UESTC-594

     

     方程不难想到,用dp(i, j)表示第i个孩子身高为j时的最小花费。则转移如下。dp(i, j) = min{ dp(i-1, k) + C*abs(j-k) + (x[i]-j)²}, 其中,x[i]为第i个孩子原来的高度。

    当k<=j时, 变换方程得dp(i,j) = min{dp(i-1,k)+C*k}+(x[i]-j)²。另f(i-1, k) = dp(i-1,k)+C*k,g(i, j) = (x[i]-j)²,则dp(i, j) = min{f(i-1, k)}+g(i, j)。这样就可以用单调队列优化了。

    当k>j时,同理得之。

   

#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 50000 + 5;
const int INF = 1e9 + 10;
int h[maxn];
int dp[50005][105];
int q[500];
int head, tail;
int cur, pre;
int main()
{
	int n, c;
	while(scanf("%d%d", &n, &c) == 2) {
		for(int i = 1; i <= n; i++) scanf("%d", &h[i]);
		
		for(int i = 0; i <= 100; i++) {
            if(i < h[1]) dp[1][i] = INF;
            else dp[1][i] = (i-h[1])*(i-h[1]);
		}
		
		for(int i = 2; i <= n; i++) {
            //当k<=j时
            head = tail = 0;
            for(int j = 0; j <= 100; j++) {
                dp[i][j] = INF;
                int now = dp[i-1][j] - c * j;
                while(head < tail && q[tail-1] > now) tail--;
                q[tail++] = now;
                if(j >= h[i])dp[i][j] = q[head] + c*j + (h[i]-j)*(h[i]-j);
            }
            
            //当k>=j时
            head = tail = 0;
            for(int j = 100; j >= 0; j--) {
                int now = dp[i-1][j] + c * j;
                while(head < tail && q[tail-1] > now) tail--;
                q[tail++] = now;
                if(j >= h[i]) dp[i][j] = min(dp[i][j], q[head] - c*j + (h[i]-j)*(h[i]-j));
            }
            
		}
		
		int mi = 1e9 + 10;
		for(int i = h[n]; i <= 100; i++) mi = min(mi, dp[n][i]);
		printf("%d\n", mi);
	}
	return 0;
}

    2、HDU3401 https://vjudge.net/problem/HDU-3401

    一个人知道了接下来T天的股市行情,问他T天后最多能赚多少钱。

    第i天买一股的价格为APi,卖一股的价格为BPi;每天最多买ASi股,卖BSi股。手里最多持MaxP股。两次交易之间相差W+1天。最初有0股。

   设dp(i, j)表示第i天持有j股时最多能赚的钱数。则这j股要么是原来就有,要么是通过购买得到,要么是通过卖掉多余的得到,由此得到以下方程。

   不买时: dp[i][j] = dp[i-1][j]。

   从第i-W-1天又买进一些: dp[i][j] = max( dp[i-W-1][k] - (j-k)*ASi )。

   从第i-W-1天卖出一些:  dp[i][j] = max( dp[i-W-1][k] +(k-j)*BSi )。

   如同上面题目那样将方程式进行适当的变形即可。

   但这个题有一个注意的地方,因为每天的买卖数量是有限制的,所以在取队头元素前应先把数量不合法的元素去掉。

#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 2000 + 5;
const int INF = 1e8 + 10;

struct Node
{
    int pos, val;
    Node(int pos = 0, int f = 0) : pos(pos), val(f){}
};

int dp[maxn][maxn];
int BP[maxn], AP[maxn], AS[maxn], BS[maxn];
Node que[maxn*10];
int head, tail;

int main()
{
   int t;
   scanf("%d", &t);
   while(t--) {
       int T, MaxP, W;
       scanf("%d%d%d", &T, &MaxP, &W);
       for(int i = 1; i <= T; i++) scanf("%d%d%d%d", &AP[i], &BP[i], &AS[i], &BS[i]);
       for(int i = 0; i <= MaxP; i++) {
          if(i > AS[1]) dp[1][i] = -INF;
          else dp[1][i] = -AP[1]*i;
       }
       for(int i = 2; i <= T; i++) {
            
          for(int j = 0; j <= MaxP; j++)
             dp[i][j] = dp[i-1][j];
             
          if(i - W - 1 <= 0) {
              for(int j = 0; j <= AS[i]; j++)
                 dp[i][j] = max(dp[i][j], -AP[i]*j);
              continue;
          }
          
          int pre = i - W - 1;
          //买股票
          head = tail = 0;
          for(int j = 0; j <= MaxP; j++) {
             int now = dp[pre][j] + j * AP[i];
             while(head < tail && que[tail-1].val < now) tail--;
             que[tail++] = Node(j, now);
             while(head < tail && que[head].pos < j-AS[i]) head++; //去掉不合法的元素
             dp[i][j] = max(dp[i][j], que[head].val-AP[i]*j);
          }
          //卖股票
          head = tail = 0;
          for(int j = MaxP; j >= 0; j--) {
             int now = dp[pre][j] + j * BP[i];
             while(head < tail && que[tail-1].val < now) tail--;
             que[tail++] = Node(j, now);
             while(head < tail && que[head].pos > j + BS[i]) head++; //去掉不合法的元素
             dp[i][j] = max(dp[i][j], que[head].val-BP[i]*j);
          }
          
       }
       int ans = -INF;
       for(int i = 0; i <= MaxP; i++)
          ans = max(ans, dp[T][i]);
       printf("%d\n", ans);
   }
}


     3、多重背包问题(HDU2191)

    现有n种物品,每件体积为Vi,价值为Wi,数量为Ci,装入体积为m的背包中,求最大价值。

    设第i件物品体积为V,则可按照余数,将当前体积分为V-1组(0,1,2,....,V-1)。

    设d是任意一组。则dp[i][d+j*V] = max( dp[i-1][d+k*V] + (j-k)*W)。其中  j-Ci<=k<=j。第一个小于等于号是怎么来的呢。因为物品i有Ci件,设当前要算dp[i][d+j*V], 则单调队列最多要取dp[i-1][d+j*V], dp[i-1][d+(j-1)*V]...dp[i-1][d+(j-Ci)*V]中的最大值,所以单调队列队头元素的下标k应满足j-Ci >= k,否则这个元素应该出列。

   

#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 200 + 5;
const int INF = 1e8 + 10;
int dp[maxn][maxn];
int head, tail;
int qa[maxn], qb[maxn];

int main()
{
    int T;
    scanf("%d", &T);
    while(T--) {
        int n, m;
        scanf("%d%d", &m, &n);
        for(int i = 1; i <= n; i++) {
            int v, w, c;
            scanf("%d%d%d", &v, &w, &c);
            for(int d = 0; d < v; d++) {
               head = tail = 0;
               for(int j = 0; j <= (m-d)/v; j++) {
                  int now = dp[i-1][j*v+d] - j*w;
                  while(head < tail && qa[tail-1] < now) tail--;
                  qa[tail] = now;
                  qb[tail++] = j;
                  while(qb[head] + c < j) head++; //因为第i件物品只有c个,所以,单调队列的容量为c。对j来说,取的应该是dp[j],dp[j-V],...dp[j-c*V]这些中的最大值
                  dp[i][j*v+d] = qa[head] + j*w;
               }
            }
        }
		printf("%d\n", dp[n][m]);
    }
    return 0;
}


npm报错 npm WARN deprecated....的问题解决方式 报错 :npm WARN deprecated … 现象:无法进行有效的vue项目的部署以及npm install 命令的执行。 原因一:版本问题 说明:由于npm的版本过低或者过高导致的报错 检查版本: npm -v 如果版本是比较旧的,或者是最新的,这里可以进行对npm的更新和回退。 更新npm版本: npm updated -d 查看可以更新的版本 npm install -g npm 默认安装最新版本 安装回退指定的版本: npm updated -d 查看所有的版本 npm -g i. 阅读详情

相关推荐

手机流畅的秘密:拆解LPDDR5X在骁龙8 Gen2平台上的电源与信号完整性设计

本文深入解析了LPDDR5X内存与骁龙8 Gen2处理器在旗舰手机中的协同设计,重点探讨了0.5V超低电压下的电源完整性设计以及8.5Gbps高速传输中的信号完整性挑战。通过详细的电源树架构、关键器件选型和布线策略分析,揭示了如何实现高性能与低功耗的完美平衡,为手机流畅体验提供硬件保障。

weixin_30235225的博客 484

单调队列优化的DP问题

单调队列就是通过排除求最值时候的冗余,从而是队列具有性质,可以方便求解问题。

xjsc01的博客 1135

【客户案例】母婴行业头部品牌是如何做私域的

各品牌争相跑马圈地,想方设法圈住流量,建立品牌资产;私域运营是一项“重人力”的工作,在传统的品牌运营过程中,私域操盘手经常会由于一对多导致无法兼顾,只能通过提高触达次数的方式加大漏斗,这样难以让每个社群都发挥出应有的价值,甚至还会导致一定程度的用户流失。除了宝妈身份外激发的用户认同感、亲密度外,KOC们作为产品的使用者,自由选择的推品和自由创作的推广文案都更具有真实性、代表性,能够极大程度激发群友们的信任感,从而愿意为产品买单,转化复购水到渠成。不同阶段的宝妈,母婴知识的储存量和经验是不同的,K品牌还会。.

圈量SCRM系统-企微营销工具-私域流量运营软件 947

单调队列/单调栈优化dp

单调队列/单调栈优化dp

qq_33969563的博客 1448

11 CO配置-控制-成本中心会计-定义分割结构(OKES-Define Splitting Structure)

业务背景:定义分割结构 事务码: OKES SPRO路径:SPRO->控制->成本中心会计->实际过账->期末结算->作业分配->划分->定义分割结构 第1步,SPRO路径进入 第2步,选择“新的条目”创建 分割结构 第3步,选择分割结构代码 点击“分割规则”进行下一个界面,点击“新条目”创建 分割结构规则 第4步,点击“为规则选择” 为分割结构规则创建版本并保存 第5步,选择“分配”,点击“新的条目” 输入分割结构分配

4371

从零构建:Simulink与PX4的快速原型开发实战

本文详细介绍了如何利用Simulink与PX4进行四旋翼无人机的快速原型开发,涵盖环境搭建、动力学建模、控制器设计、仿真验证及自动代码生成全流程。通过实战案例展示姿态控制器的实现与调试,帮助开发者高效完成从算法设计到硬件部署,显著提升开发效率并降低试错成本。

plant的博客 323

单调队列优化DP

单调队列优化DP最大子序和修剪草坪旅行问题烽火传递绿色通道理想的正方形 在多重背包问题3中,我们用到了单调队列优化DP,实际上,不止这个问题,很多问题我们仔细观察,就能发现它有类似的性质,我们就能用单调队列优化,我们通过几道题的练习来观察观察其中的规律。 最大子序和 题目链接:最大子序和 修剪草坪 题目链接:修剪草坪 旅行问题 题目链接:旅行问题 烽火传递 题目链接:烽火传递 绿色通道 题目链接:绿色通道 理想的正方形 题目链接:理想的正方形 ...

csdnwqy030429的博客 2706

记一次Redis出问题及解决方案

Q1: redis服务关闭前确认数据还是在的,重启后发现一条数据都没了,key* 查也是空的 A1: 经仔细检查发现redis.conf配置文件配置的数据存储目录配置的是dir ./ 也就是相对目录,也就是说在哪个目录执行redis-server redis.config启动命令 redis数据就会存到哪个目录,同样就会加载哪个目录下的数据文件(dump.rdb),前后两次启动命令的位置不一样导致第二次启动没有加载到(dump.rdb)文件,从而导致查不出数据 Q2:springboot 集成r

u010013928的博客 3058

算法学习:单调队列(dp优化)

单调队列,顾名思义,一个是单调一个是队列。首先是单调,可以是单调递增单调递减,也可以是某种特殊数据的组合的比较方式,是可以自定义>号的这种(我想应该可以写成运算符重载,或者函数引用,不过我还不会qwq)。其次是队列,队列其实就是封装好的,只可以从队首队尾进行操作的数据结构。不过由于队首队尾都需要推入和弹出操作,所以用std::deque很适合。

2301_80738165的博客 1930

ADS负载牵引仿真避坑指南:从零开始搞定Loadpull模板设置

本文是一份针对ADS负载牵引仿真的深度避坑指南,详细解析了Loadpull模板从初始化配置到结果解读的全过程。文章重点阐述了如何根据设计目标灵活设置扫描圆、采样点等关键参数,以解决仿真不收敛、结果不完整等常见问题,并指导读者在功率与效率之间做出理性权衡,最终提取出用于匹配网络设计的最优负载阻抗。

weixin_29234423的博客 595

Halcon异常检测训练避坑指南:如何避免GPU内存溢出和样本不足问题

本文针对Halcon深度学习异常检测训练中常见的GPU内存溢出和样本不足两大难题,提供了从原理分析到实战解决方案的深度指南。通过调整批次大小、图像尺寸、使用梯度累积等技巧有效管理显存,并利用针对性数据增强与人工合成策略破解小样本困境,帮助开发者构建稳定高效的训练流程。

river的博客 215

单调队列优化 DP

的状态转移方程,标清楚 决策 的上下界,然后依据 转移 的性质用 单调队列维护 决策集合 ,进而优化复杂度。,而 决策集合 是从左向右滑动的,这样会不会导致某些后面的有效决策被排除?那么基于 减少无用决策出发:当本层的高度最大时,只保留 最小的 长度即可。位置的决策 排除了在它后面的部分元素,现在它出队了,谁有能是最优呢?整体上看,转移时需要维护到两个信息:当前层的长度,上一层的长度。时,首先让部分决策出队,并从 set 中删除,不需要更改。这是第一步的转移,去除了有空板 / 空人的影响。

qq_56326461的博客 764

【YOLO系列】YOLOv12详解:模型结构、损失函数、训练方法及代码实现

本文提出YOLOv12,一种以注意力为中心的实时目标检测框架,通过创新性改进解决传统YOLO模型在精度与速度上的失衡问题。核心贡献包括:1)轻量混合注意力模块(LHA)结合通道与空间注意力,在不增加计算负担下提升特征提取能力;2)区域注意机制与R-ELAN网络优化特征聚合,显著改善小目标检测(AP_S提升8.3%);3)动态多光谱数据增强策略增强模型鲁棒性。实验表明,YOLOv12在COCO等基准上实现SOTA性能,同时保持2.1 TOPS/W的高能效比,适用于边缘设备部署。该工作为实时目标检测提供了新的注

一碗白开水一 1807

论单调队列优化DP

单调队列优化DP(区间、背包、树形和数位)

2401_90042730的博客 2051

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

DP单调队列优化

内,那么,通过下图可以发现,其实整个DP的过程类似单调队列滑动窗口。,所以前区间的最小值一定能维护到,方案可行。所以,我们可以考虑用单调队列来优化枚举。由于一段的连续长度不能超过。这个点已经加入单调队列了,不必再加)。是定值,不用枚举,而对于剩下的。可以考虑用单调队列维护最大值。那么,对于当前点的最大值就是。加入单调队列中,维护最大值(的循环,优化后时间复杂度为。,所以答案通过队头就可以。,单调队列循环次数一共是。,先按照题目计算出价值。,然后放入单调队列中。由于普通思想需要枚举。

STY_fish_2012的博客 814

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

单调队列优化dp小结

拿手拆一拆易得:f[i][j]=c[i]*j+min(f[i-1][k]+(x[i]-x[i-1])*k*k-c[i]*k)(max(0,j-c[i])<=k<=j)这题和单调队列毫无关系,设f[i][j]表示后两步为i,j的最大值,转移枚举j,再枚举k,用minn=max(f[i][j])转移一下即可,可以用上一个k的延续过来。拿脚推易得:f[i][j]=min(f[i-1][k]+(x[i]-x[i-1])*k*k+c[i]*(j-k))(max(0,j-c[i])<=k<=j)

2301_81913843的博客 1154

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

dp单调队列优化

本文介绍了单调栈和队列的基本概念及其实现方法,并通过洛谷P2034题目展示了单调队列在动态规划中的应用。单调栈通过手动实现或STL库保持元素单调性,而队列则遵循先进先出原则。在解决P2034问题时,通过将问题转化为删除最小和的方式,利用单调队列优化比大小过程,显著提升了效率。文章提供了手搓和STL两种实现代码,帮助读者理解并应用该算法。

xiaowu114514的博客 1214

DP 优化一:单调队列优化 DP

本文介绍了动态规划中的单调队列优化方法。首先解释了单调队列的概念及其在线性时间内求区间最值的优势,通过滑动窗口例题展示了具体实现过程。然后重点讲解了如何利用单调队列优化特定形式的DP转移方程,即形如f[i]=min{f[j]+F(j)+C}的情况。最后以一道粉刷围栏的题目为例,展示了如何将暴力DP解法转化为单调队列优化形式,通过数学变形将转移方程改写为可优化结构,从而显著提升算法效率。文章通过具体例题和代码示例,系统性地阐述了单调队列优化的原理和应用场景。

cwplh的博客 657

单调队列 例子:hdu3401 trade(dp加单调队列)

单调队列指的是指队列中的元素是单调的。如:{a1,a2,a3,a4……an}满足a1<=a2<=a3……<=an,a序列便是单调递增序列。同理递减队列也是存在的。单调队列出现的机会不多,而且最常和dp一起出现。单调队列是一种工具而不是解题的方法。 单调队列的出现可以简化问题,队首元素便是最大(小)值,这样,选取最大(小)值的复杂度便为o(1),由于队列的性质,每个元素入队一次,出队一次,维护队列

Kblacksmith的博客 1461

单调队列优化DP——习题收集

前言 感觉可以用单调队列优化dp的模型还是挺活的,开个随笔记录一些遇到的比较有代表性的模型,断续更新。主要做一个收集整理总结工作。 记录 0x01 POJ - 1821 Fence,比较适合入门的题,写出转移方程后可以比较容易的看出决策变量的取值范围的界是单调变化的,以及价值拆开之后也是单调的 #include<iostream> #include<cstdio> #in...

weixin_30508309的博客 228

HDU 5945 单调队列优化DP

比赛时一直以为是个数学题,一直没有想到用优先队列优化DP 看来需要好好学一学

qq_29904713的博客 260

记单调队列DP

单调队列DP 原理 比如说有这么几个数,每次划定一个长度为5的区间,求每次区间内的最大值 2 4 0 3 7 1 -4 6 第一次把2加进队列{2} 第二次{4},因为在有4的情况下,2是没用的 第三次{4,0} 第四次{4,3} 第五次{7} 第六次和以后{}。。。懒得写了 ...

7_26的博客 410

单调队列优化DP动态规划(1)

目录 一.前言 二.例题1:单调队列本身的灵活应用——测量温度 1.题目 题目描述 输入 输出 输入样例 输出样例 2.思路 三.例题2:单调队列优化DP:猴子 1.题目 题目描述 输入 输出 输入样例   输出样例 2.思路  四.总结 一.前言 学好了单调队列不仅可以单独使用,他还可以有更多的广泛用途。这里我主要讲其对动态规划的优化,较简单。 二.例题...

weixin_43908980的博客 797

DP问题之单调队列优化

DP问题之单调队列优化

ljyoi的博客 412

单调队列优化dp

如上图,可以跳到 3−7 这个区间,跳到 3 和 4 的时候,都是消耗劳累度的,但是我们选择哪个更好呢,当然是越大的越好,因为高度越高,下一次越有利,如上图,当我们跳到 4 ,下一次跳到 3 的时候就不增加劳累度。如果暴力枚举那个区间,一般会超时,于是我们可以选择将最大值放入队头的最大值或者最小值,通过这样维护我们可以减去暴力枚举的过程,优化了时间复杂度。维护这样一个队列的好处是,当我们想求最大值的时候,我们只需要保证队列是单调递增的,于是每次取出队头就是我们的最大值。第一个元素下标进队列,此时队列为。

m0_74176921的博客 639
上一篇: POJ 3268 Silver Cow Party (Dijkstra算法)
下一篇: UVa 10905 Children's Game (贪心)
Interstellar_
博客等级 码龄10年 8粉丝 121原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值