浅谈单调栈的实现方式和简单应用

一、单调栈的原理和实现方式

1.定义

从栈底元素到栈顶元素呈单调递增或单调递减,栈内序列满足单调性的栈;

2.原理

(1)当新元素在单调性上优于栈顶时(单增栈新元素比栈顶大,单减栈新元素比栈顶小),压栈,栈深+1;

(2)当新元素在单调性与栈顶相同(新元素于栈顶相同)或劣于栈顶时(单增栈新元素比栈顶小,单减栈新元素比栈顶大),弹栈,栈深-1;

3.一般实现形式

以单增栈(栈顶为最大值)为例:

n为元素数,h为入栈序列,tot为栈深,stack为单增栈;

void stacks(){
        int stack[10001],tot=0,n,h;
        memset(stack,0,sizeof(stack));
        scanf("%d",&n);
        for(i=1;i<=n;++i){
                scanf("%d",&h);
                while(tot>0&&h<=stack[tot]) --tot;
                stack[++tot]=h;
        }
}

4.几点注意

(1)在弹栈过程中,注意增加限制条件tot>0,若无此条件,如果新元素为负数,弹栈过程中就可能出现tot<0的现象;

(2)在弹栈压栈过程中,注意随时更新栈深;

(3)多次使用时没必要每次都重置栈,有操作流程知数据直接覆盖不会出现问题,可以节省时间;

二、相关应用

下面我们通过由简到难介绍几个题目来说明单调栈的使用方式:

1.[模板]最长上升子序列 (LIS)

Description

给一个数组 a[1],a[2],...,a[n],找到最长的上升子序列a[b1]< a[b2]< ... < a[bk]的长度,其中b[1]<b[2]< ... <b[k]。

Solution

1.用单调栈优化,利用LIS的单调性,使用单增栈,栈顶为最大值;

2.对于每一个数:

(1)当其大于栈顶时,满足单调性,压栈;

(2)当其小于栈顶时,不满足单调性,向下搜索大于该数的最小值,替换;

(3)当其等于栈顶时,不满足单调性,不作处理;

3.栈深即为LIS的长度;

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;

int a[1005],b[1005],n;
int stk[1005];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+n+1);
    int tot=unique(b+1,b+n+1)-(b+1);
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
    int top=0;
    for(int i=1;i<=n;i++){
        if(top==0||stk[top-1]<a[i]) stk[top++]=a[i];
        else{
            *upper_bound(stk,stk+top,a[i])=a[i];
        }
    }
    printf("%d\n",top);
    return 0;
}

2.[POI 2008&洛谷P3467]PLA-Postering

Description

Byteburg市东边的建筑都是以旧结构形式建造的:建筑互相紧挨着,之间没有空间.它们共同形成了一条长长的,从东向西延伸的建筑物链(建筑物的高度不一).Byteburg市的市长Byteasar,决定将这个建筑物链的一侧用海报覆盖住.并且想用最少的海报数量,海报是矩形的.海报与海报之间不能重叠,但是可以相互挨着(即它们具有公共边),每一个海报都必须贴近墙并且建筑物链的整个一侧必须被覆盖(意思是:海报需要将一侧全部覆盖,并且不能超出建筑物链)

输入格式:第一行为一个整数n(1≤n≤250000),表示有n个建筑,接下来n行中,第i行表示第i个建筑物的宽di与高wi(1≤di,wi≤1 000 000 000),中间由一个空格隔开;

输出格式:第一个为一个整数,表示最少需要几张海报覆盖整个建筑物.

Solution

1.考虑如果整个建筑物链是等高的,一张高为链高,宽为整个建筑物宽的海报即可完全覆盖;

2.若有两个不等高的元素组成建筑物链,那么一定需要两张;

3.因为题目要求海报不可超出建筑物链,那么我们即可用单调栈维护:初始海报数为建筑物数,入栈建筑物链的高度序列,当栈顶大于即将入栈元素时弹栈,若最后弹栈元素与即将入栈元素等高,需要的海报数-1;

4.易证明本方法是正确的:当有两个处于一个峰两侧的等高块时,他们可以用一张海报覆盖,所需海报数显然可减少一个;

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long top=0,n,num=0,i,j,k,stack[250100];
int main(){
    scanf("%lld",&n);
    for(i=1;i<=n;++i){
        scanf("%lld%lld",&j,&k);
        while(top>0&&k<=stack[top]){
            if(k==stack[top])num++;
            --top;
        }
        stack[++top]=k;
    }
    printf("%lld\n",n-num);
    return 0;
}

原文地址:https://www.cnblogs.com/COLIN-LIGHTNING/p/8474668.html

时间: 2024-12-11 13:40:45

浅谈单调栈的实现方式和简单应用的相关文章

浅谈—单调栈

一.概念: 单调栈的本质还是一个栈,只不过是栈的元素从栈底到栈顶单调递增或者是单调递减. 二.单调栈的维护: 每加入一个元素,将这个元素和栈顶元素相比较,(假设你维护的是一个单调递增的栈),如果当前元素大于等于栈顶元素,说明这个元素 没有破坏这个栈的单调性,直接加入:如果当前元素小于栈顶元素,直接向栈中加入元素会破坏栈的单调性,这时我们需要将栈顶元素依次弹出,知道遇到第一个 大于或者等于当前元素的元素,再将该元素压入栈中.时间复杂度O(n),所有的元素都会进栈一次. 三.单调栈的性质: (1)单

浅谈单调队列、单调栈

       初谈这个话题,相信许多人会有一种似有所悟,但又不敢确定的感觉.没错,这正是因为其中"单调"一词的存在,所谓单调是什么,学过函数的people都知道单调函数或者函数的单调性,直白一点说单调就是一直增或一直减.例如:1,3,5,9就是一个单调增数列,数列中不存在后一个数比前一个数小的现象.那么同样,在这里谈到的话题也有类似特点.        先说一下单调队列吧!      单调队列,就是一个符合单调性质的队列,它同时具有单调的性质以及队列的性质.他在编程中使用频率不高,但却

浅谈单调队列:死海不是海,单调队列不是队列

1.滑动窗口最值问题 给定一个长度为n的序列a1,a2,-ai,-,an,将一个长为k的滑动窗口自序列最左端向右边滑动.例如:初始时,窗口内的子序列为a1,a2,-,ak:当窗口向右滑动一位,此时窗口内的子序列变为a2,a3,-,ak+1. 我们要解决的问题是,给定长度为n的序列以及滑动窗口的大小k,求每一个滑动窗口内的最小值和最大值. 以长度为5的序列1, 3, 4, 5, 7滑动窗口k=3为例说明: 第1个滑动窗口(1, 3, 4)的最小值.最大值分别为1和4: 第2个滑动窗口(3, 4,

浅谈创建对象的两种方式

经常使用IDE不容易看出编译和运行的明显区别,因为像eclipse这样的开发工具会自动进行编译.当你创建一个类的时候就编译成一个class文件,在此基础上做的修改保存后又会触发一次编译.所以我们可以借助记事本来看看什么是运行时调用,来体验一下创建对象的两种方式. 首先来看一个例子,有以下的接口和两个实现类: public interface Fruit { public void color(); } public class Apple implements Fruit { @Override

浅谈js模块加载方式(初级)

1.简介:  前端模块化开发日渐鼎盛,如何将零散的插件或者是普通的js脚本文件统一管理及引用,是众多开发者共同的目标.本人是从事.net开发的,最近对前端的一些东西特别的感兴趣,也会尝试的夹杂一点自己的想法,写一些小东西.东西不牛逼,但是感觉用起来还是方便那么一点的.下面就展示一下简短的小代码. 2.中心思想:通过外部调用事先封装好的模块加载方法,传入参数(包括主目录及模块js或者css的目录 ),在程序运行的同时,会动态的将相应的css或者是js代码追加引用到head标签内,这样,就可以使用被

浅谈一下关于ios开发中一些简单地开发模式

最近公司的工作还比较轻松,自己想去写一些东西,因为本人是做ios开发的,能够希望在这个行业能够结交更多地朋友,下面是本人对一些常用的开发模式,小例子都是去采用的OC语言,希望大家能够提出观点,咱们多多去交流. 下面这个小案例就是简单工厂模式: 简单工厂模式: (1)计算器小例子 ######### // 计算器的类 #import <Foundation/Foundation.h> @interface Calutor : NSObject { float _num1; float _num2

浅谈设计模式之工厂类模式由简单到复杂的演变

前言 在软件设计过程中,我们总是需要创建很多对象,而且系统越庞大,创建的对象越复杂.而今天我们将讨论的就是解决对象创建时的难题--工厂类模式.为了贴近工厂这个词,我们采用工厂建造汽车这个例子来阐明工厂类模式的演变和什么场景下使用什么模式. 场景1.:一位顾客要开车从上海到苏州,他需要一辆汽车,于是他自己组装汽车,给车装轮胎.导航仪.车灯等. 问题:1.显然,顾客只是想拥有一辆汽车,他不想知道怎么去买汽车,更不想知道怎么组装,然后还要给汽车上漆. 2.如果他想换个型号的汽车,他得重新来遍组装汽车.

浅谈Android studio中OKHttp安装及简单使用

Google貌似在6.0版本里面删除了HttpClient相关API,鉴于okhttp的口碑相当好,介绍一下OKHttp的安装及使用: 一.安装 对于Android Studio的用户,在Project Structure--->Dependencies 点击"+"号选Library dependency在搜索页面分别搜okttp,okio 注:okhttp内部依赖okio,别忘了同时导入okio 二.用法 (一)Http Get  Http Post //创建okHttpCli

浅谈鸽巢原理的证明和简单应用

一.鸽巢原理的证明 1.定义: 若有n个鸽巢和kn+1只鸽子,所有的鸽子都进入鸽巢,那么至少有一个巢中有k+1只鸽子(n,k≥0). 2.证明(反证法): 若每个鸽巢中的鸽子数都不大于k,则总鸽子数<=kn,与已知相悖.得证. 3.拉姆齐(Ramsey)定理的证明:6个人中,要么存在三个人彼此互相认识,要么存在三个人彼此都不认识: 证明:设六个人为六个点,认识或不认识用两种不同颜色的线段代表,因为两人只有一种关系,所以任意一点一定会引出连向其他5点的五个线段,根据鸽巢定理,有2种关系,有2*2+