单调栈原理+练习

首先用一道题引出单调栈

码蹄集 (matiji.net)

首先画一个图演示山的情况:

最暴力的做法自然是O(n方)的双循环遍历,这么做的思想是求出当前山右侧有多少座比它小的山,遇见第一个高度大于等于它的就停止。

但是对于我们所求的答案数,可以换一个想法,即对于一座山,求出它左侧有多少座山可以看到它。

对于5;7可以看到它。

对于3;7,5可以看到它。

对于4;7,5可以看到它。

可以发现,即求左侧有多少比当前值大的山的个数,那么我们可以维护一个数据结构,使得每次加进来一个数,都保持递减的顺序,然后每次加入前,都计算它的size大小,进行累加。比如:

一开始7进入;

然后5准备进入,进入前检查是否进入后会破坏单调性,发现不会,那么将当前的size累加到sum里,sum当前为1。5进入,现在数据结构为7,5;

然后3准备进入,检查发现不会破坏单调性,sum累加当前size(2),sum现在为(3)。3进入,现在数据结构为7,5,3;

然后4准备进入,检查发现会破坏单调性,就把比4小的全部扔出,于是3出去了。这时候sum累加当前size(2),sum现在为(5),然后4进入,数据结构现在为7,5,4;

结果也符合手算结果。

发现此过程进和出都是在同一侧进行,那么最适合的数据结构自然是栈了。这样的思想就是单调栈。

以下是代码:

#include<iostream>
#include<stack>
#define int long long
using namespace std;
signed main() {
    int n, num, sum=0;//n为山脉个数;num为每次读入的山脉高度,sum为总和
    stack<int> stk;//栈,我们要使它单调
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> num;
        //下面的while循环即用来维护单调性,本题要严格单调递减
        //每次检查栈顶元素是否小于等于当前高度,如果是则弹出,直到为空或者栈顶高度大于当前高度
        while (!stk.empty() && stk.top() <= num)
            stk.pop();
        //总和累加,stk.size即左侧比当前山脉高的山脉,也即能看到当前山的个数
        sum += stk.size();
        //入栈,栈此时满足单调性
        stk.push(num);
    }
    cout << sum;
}

(因为数据范围的要求,得用long long)

下面还有几道练习题

码蹄集 (matiji.net)

这道题需要我们脑补出究竟是哪种情况可以使海报数变少。

最多的情况自然是墙的个数n,考虑下面的情况:

如果出现这种情况,即低-高-低,并且左侧和右侧低的墙高度相同,那么就可以减少一张海报了。

注意中间高的墙可以不止一道,只要是被两个同高且相对低的墙夹住,整体就可以减少一张海报。

同样,可以没有所谓的高墙,即两道相连的墙同高,那自然也可以减少一张海报数。

而如果是这种情况:(涂橙色的代表墙)

那么即使左右两侧高度相同,还是需要用三张海报,因为题目要求海报不能超出边界。

我们同样维护一个数据结构,如果新输入的墙高于当前墙,那么就可以加入;而如果小于当前墙,那么就需要检查是否存在低-高-低的情况了。低的墙的高度即取决于新输入的墙的高度,将数据结构里所有比低墙要高的墙都弹出;若遇到同高的墙,则代表海报数可以减1,同时也弹出在数据结构里的那个同高的墙;若遇到比我们选定的低墙还低的墙,就结束弹出操作,并把新输入的墙加入。

以下面的数据为例,数字代表墙的高度:

2,2,3,4,2;以下是数据结构内容的变化

  • 2(第一个2加入)
  • 2(读入第二个2,发现同高,把前一个弹出,同时记录可以减1个海报)
  • 2-3(读入3,直接加入)
  • 2-3-4(读入4,直接加入)
  • 2(读入最后一个2,发现比顶部的墙小,开始弹出,发现4,3,2都要弹出,并且记录可以减1个海报数)

最后发现可以减少两个海报,那么就需要5-2=3个海报,就可以覆盖上述例子。

最终我们发现,维护的数据结构最终依然是单调的,并且操作在同侧进行,所以依然是单调栈的做法。

代码如下,代码量是很少的,但是需要知道其中的思想

#include<bits/stdc++.h> 
using namespace std;
int n,ans,d,w;
stack<int> st;
int main( )
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>d>>w;
        //只要栈顶的墙的高度大于等于墙的高度,都需要弹出
        while(!st.empty()&&w<=st.top()){
            if(st.top()==w)//如果遇见同高的墙,记录可以减少一个海报数
                ans++;
            st.pop();//弹出
        }
        st.push(w);//此时栈里的墙都比当前墙矮(或者栈为空),此时可以加入,就不会破坏单调性
    }
    cout<<n-ans;//最终结果为n-ans
    return 0;
}

下一道题

码蹄集 (matiji.net)

以样例为例,这道题也可用单调栈完成

维护一个单调递增的栈,一旦新增系数不满足单调性,即代表它是第一个小于栈顶元素系数的值。

按步骤拆解:

一开始1,3入栈

之后读入-3,开始检查,发现-3小于栈顶元素,于是x三次方的系数就变成-3,然后弹出3;之后-3与栈顶1对比,发现-3小于栈顶元素,于是x四次方的系数就是-3,然后弹出1 。-3入栈。

6入栈

读入1,比6小,所以x的系数是1 。但是比下一个栈顶元素-3大,于是入栈,现在栈里有-3,1;

最后x的平方和x的0次方系数为0.

这里有个问题,就是如果栈里存放系数的话,经过弹栈入栈,就判断不了这个系数是属于哪个x次幂的了,所以我们栈里改为存放下标i,通过下标读入存放在数组的系数。

以下是代码:

#include<bits/stdc++.h> 
#define ll long long
const int MOD=99887765;
const int N=5e6+7;
using namespace std;
stack<int> st;
//a是原系数数组,b是新的系数数组
int n,x,a[N],b[N];
ll ans;
int main( )
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>x;
    for(int i=1;i<=n+1;i++){
        cin>>a[i];
        //如果读入的系数小于栈顶的系数(注意栈里存放的是下标,于是通过a[st.top()]来获得栈顶的系数)
        while(!st.empty()&&a[i]<a[st.top()]){
            b[st.top()]=a[i];//栈顶位置的x次幂的系数设置为当前读入的系数(是第一个小于原系数的值)
            //弹栈
            st.pop();
        }
        //注意如入栈的是下标
        st.push(i);
    }
    //其实最好是显式初始化一下b数组,把初始值都设为0,虽然不这样做默认初值也是0.这样没处理的下标的保存的新系数自动设为0
    //这里就是计算多项式了
    for(int i=1;i<=n+1;i++){
        ans=ans*x+b[i];
        ans=(ans%MOD+MOD)%MOD;
    }
    cout<<ans;
    return 0;
}

以上就是一些单调栈的简单示例了。

似乎可以总结出一个模板了:

1.循环读入新元素

2.用一个while循环,通过对比栈顶和新元素,来把不满足单调性的栈顶弹出

3.新元素压栈,保持单调性

至于题目有什么要求,就要在模板里进行一些其他操作了

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

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

相关文章

能不能接受这些坑?买电车前一定要看

图片来源&#xff1a;汽车之家 文 | Auto芯球 作者 | 雷慢 刚有个朋友告诉我&#xff0c;买了电车后感觉被骗了&#xff0c; 很多“坑”都是他买车后才知道的。 不提前研究&#xff0c;不做功课&#xff0c;放着我这个老司机不请教&#xff0c; 这个大冤种他不当谁当&…

C# WinForm —— 25 ProgressBar 介绍与使用

1. 简介 用于显示某个操作的进度 2. 常用属性 属性解释(Name)控件ID&#xff0c;在代码里引用的时候会用到,一般以 pbar 开头ContextMenuStrip右键菜单Enabled控件是否可用ForeColor用于显示进度的颜色MarqueeAnimationSpeed进度条动画更新的速度&#xff0c;以毫秒为单位M…

【Python】解决Python错误报错:IndexError: tuple index out of range

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

什么是PLAB?

接上文PLAB---》 可以看到和TLAB很像&#xff0c;PLAB即 Promotion Local Allocation Buffers。用在年轻代对象晋升到老年代时。 在多线程并行执行YGC时&#xff0c;可能有很多对象需要晋升到老年代&#xff0c;此时老年代的指针就"热"起来了&#xff0c;于是搞了个…

新游启航 失落的方舟台服注册指南 一文教会你方舟台服注册

新游启航&#xff01;失落的方舟台服注册指南&#xff01;一文教会你方舟台服注册 失落的方舟作为本月最受期待游戏之一&#xff0c;在上线之际许多玩家已经有点急不可待了。这款游戏是由开发商Smile gate开发的一款MMORPG类型游戏&#xff0c;这款游戏的基本玩法与其他MMORPG…

44-1 waf绕过 - WAF的分类

一、云 WAF 通常包含在 CDN 中的 WAF。在配置云 WAF 时&#xff0c;DNS 需要解析到 CDN 的 IP 上。请求 URL 时&#xff0c;数据包会先经过云 WAF 进行检测&#xff0c;如果通过检测&#xff0c;再将数据包流向主机。 二、硬件IPS/IDS防护、硬件WAF 硬件IPS/IDS防护&#xff…

归并排序C++代码详解,思路流程+代码注释,带你完全学会归并排序

归并排序 归并排序是一种经典的排序算法&#xff0c;属于分治算法的一种。其核心思想是将一个大问题分解成若干个较小的子问题来解决&#xff0c;然后根据子问题的解构建原问题的解。在排序问题中&#xff0c;归并排序将数组分成两半&#xff0c;分别对这两半进行排序&#xf…

0基础认识C语言(理论+实操3)

所有籍籍无名的日子里 我从未看轻自己半分 小伙伴们&#xff0c;一起开始我们今天的话题吧 一、算法操作符 1.双目操作符 为何叫双目操作符呢&#xff1f;其实是因为我们进行加减乘除的时候&#xff0c;至少得需要两个数字进行这些运算&#xff0c;而这个数字就被称为操作数…

Java对sqlserver表的image字段图片读取和输出本地

Java代码实现对sqlserver数据库表的image字段图片的读取&#xff0c;和输出存储到本地 由于表image字段图片存的内容是二进制值&#xff0c;如何输出保存到本地&#xff1a; 代码示例&#xff1a;&#xff08;注&#xff1a;连接sqlserver数据库需配置其驱动文件&#xff09; …

基础—SQL—DCL(数据控制语言)之用户管理

一、引言 分类全称描述DCLData Control Language&#xff08;数据控制语言&#xff09;用来创建和管理数据库用户以及控制数据库的访问权限 1、图解 右边的是我们的 MySQL 的数据库服务器&#xff0c;左边是假设的两个用户 1、 DCL 主要控制的就是有哪些用户可以来访问这台 My…

英语翻译程序,可以对用户自己建立的词汇表进行增删查改

⑴ 自行建立一个包含若干英文单词的词汇表文件&#xff0c;系统初始化时导入内存&#xff0c;用于进行句子翻译。 ⑵ 用户可以输入单词或者句子&#xff0c;在屏幕上显示对应翻译结果。 ⑶ 用户可对词汇表进行添加和删除&#xff0c;并能将更新的词汇表存储到文件中。 #defi…

RDD实战:排序算子 - sortBy()

在本实战案例中&#xff0c;我们将使用Apache Spark的sortBy()算子来对一个包含学生信息的RDD进行排序操作。 排序规则如下&#xff1a; 首先按照性别升序排列。在性别相同的情况下&#xff0c;按照年龄降序排列。 步骤1&#xff1a;创建学生信息列表 首先&#xff0c;我们创…

《计算机工程与应用》最新投稿经验2024年5月

研二下第一次投稿&#xff0c;深度学习长时间序列预测方向&#xff0c;选择了《计算机工程与应用》期刊&#xff0c;是CSCD扩展刊北大核心&#xff0c;且在24年被EI收录等等。4.10交稿到最后5.31收到录用通知&#xff0c;历时不到2个月&#xff0c;总的来说编辑部效率确实高。 …

基于强化学习的控制率参数自主寻优

1.介绍 针对控制建模与设计场景中控制参数难以确定的普遍问题&#xff0c;提出了一种基于强化学习的控制律参数自主优化解决方案。该方案以客户设计的控制律模型为基础&#xff0c;根据自定义的控制性能指标&#xff0c;自主搜索并确定最优的、可状态依赖的控制参数组合。 可…

中建环能 | “农村生活污水治理稳质增效与智能运维技术研究及成套装备应用” 科技成果评价

中华环保联合会组织召开了中建环能科技股份有限公司申请的“农村生活污水治理稳质增效与智能运维技术研究及成套装备应用”技术成果评价会。会议由中华环保联合会水环境治理专业委员会秘书长刘愿军主持。 评审会委员 本次评价会邀请了7位相关专业领域的专家组成专家评价委员会。…

spoon工具的安装与配置

spoon对应的jdk包下载资源地址 spoon软件下载资源地址 首先需要安装jdk&#xff0c;配置java环境&#xff0c;安装好后&#xff0c;cmd一下&#xff0c;查看java -version&#xff0c;看看是否成功安装&#xff0c;如果失败&#xff0c;查看系统环境变量&#xff0c;去配置jdk…

Vue3-Vite-ts 前端生成拓扑图,复制即用

完整代码&#xff0c;复制即可用&#xff0c;样式自调 试过 jointjs dagre-d3 vis&#xff0c;好用一点 方法1&#xff1a;Vis.js npm install vis-network <template><div id"mynetwork" class"myChart" :style"{width: 100%, height: 9…

QGraphicsView实现简易地图19『迁徙图』

模仿echarts的迁徙图效果 用到了前2篇制作的散点(涟漪效果)和两年前的路径动画类&#xff1b;然尾迹效果未依附路径&#xff0c;有待优化。 动态演示效果 静态展示图片 核心代码 #pragma once #include "Item/AbstractGeoItem.h" #include "DataStruct/GeoD…

苹果电脑如何清理最近打开的文稿记录 Mac如何移除浏览痕迹保护隐私

日常使用苹果电脑的过程中&#xff0c;我们经常会打开各种文稿&#xff0c;浏览网页等操作。然而&#xff0c;这些操作可能会留下一些记录&#xff0c;涉及到个人隐私和数据安全问题。下面我们来看看苹果电脑如何清理最近打开的文稿记录&#xff0c;Mac如何移除浏览痕迹保护隐私…

JavaSE:异常

1、什么是异常 在生活当中&#xff0c;不管是人还是动物又或是植物&#xff0c;都会生病&#xff1b;在程序中也是&#xff0c;作为程序猿&#xff0c;虽然我们会尽力将程序写的完美&#xff0c;可难免会出现一些问题~ 在程序执行过程中&#xff0c;发生的一些不正常行为&…