[数据结构]单调栈的基本应用2

[数据结构]单调栈的基本应用2

一、前言

单调栈的基本应用2是单调栈的基本应用1的延伸。应用2主要解决的是二维平面的一些问题。

二、基本应用2

虽然已经应用到二维平面,但是单调栈的思想并没有变化
更多应用的是延伸的位置。本质是ai的左/右第一个大于ai的元素位置

下面列举的一些平面内单调栈的例题:

例1: POJ2559 最大矩形面积

题目描述:

给定n个依次排列并且面积为1*hi的矩形,现求这个图形所包含最大的矩形的面积。
上图中7个矩形的h依次为2 1 4 5 1 3 3。注意题目有多组数据读入。
思路:
我们构造一个单调上升栈。这样,我们只需要在这个元素出栈时统计答案。
栈顶元素不断被弹出时,由于栈的单调性,我们记录变量Spread为栈顶元素能延伸的最大宽度。Spread+=Stack[top]来更新下一个会被弹出的元素的最大延伸距离。我们同时也需要更新最大的矩形面积。
while循环结束后,将新元素入栈并更新下一个元素的延伸宽度(不更新所造成的后果稍经思考便能得出)。扫描完毕后,栈内仍可能会有剩余的单调上升元素。我们只需要对每个元素分别统计答案就可以了。
需要注意的是,Spread只统计本次新元素加入后栈内元素的延伸情况,在下一次新元素加入时Spread需要归0。
代码如下:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#define N 100010
typedef long long ll;
using namespace std;
struct node{
    int h,w;
}Stack[N];
int main()
{
    int n,h;ll top,ans,spread;
    while(scanf("%d",&n)&&n){
        ans=top=spread=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&h);
            spread=0;//注意初始化!
            while(top&&Stack[top].h>=h){//维护一个单调上升栈
                //栈顶元素无法在保证其高度的情况下继续延伸
                ll size=Stack[top].h*(Stack[top].w+spread);
                //栈顶出栈时统计最大面积
                spread+=Stack[top].w;
                ans=max(ans,size);
                //题目要求求出最大面积
                top--;
            }
            Stack[++top].h=h;//入栈
            Stack[top].w=spread+1;//此时累加栈顶元素延伸的长度
        }
        spread=0;
        while(top){//对栈内剩余元素的处理
            //栈已经有单调性,所以每次只需要不断出栈,累加延伸长度,统计答案就可以了
            ll size=Stack[top].h*(Stack[top].w+spread);
            ans=max(ans,size);
            spread+=Stack[top].w;
            top--;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

例2: P1950 长方形_NOI导刊2009提高(2)

题目描述:给定一个n*m的矩形。现在让你求出该矩阵中所有"."能组成的矩形的总数量。
思路:
这道题很难会想出单调栈的做法。本题基本思路例1基本一致。
首先用前缀和处理出每列"."的和,当出现字符"*"时前缀和归零。接下来,对于每行我们都进行单调栈处理。
我们分别从l~rr~l进行扫描,分别求出ai向右和向左最大能延伸到的位置。即g1g2
现在我们处理完了g1和g2,那么如何统计答案呢?这里涉及一点乘法原理,我们可以知道每个位置所贡献的答案就是(g1[i]-j)*(j-g2[i])*sum[i]
本题最大的细节问题在于答案的统计,需要注意重复计算答案的问题,请注意sum[st[top]]>sum[i]sum[st[top]]>=sum[i]区别。
代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
char ch[1010][1010];
int n,m,sum[1010],a[1010][1010],st[1010],g1[1010],g2[1010];
ll ans;
inline void Spread(){
    memset(g1,0,sizeof(g1));
    memset(g2,0,sizeof(g2));
    int top=0;
    for(int i=1;i<=m;i++){
        while(top&&sum[st[top]]>sum[i]){
            g1[st[top]]=i;top--;//向右延伸
        }
        st[++top]=i;
    }
    while(top){g1[st[top]]=m+1;top--;}//此时元素最小,最远可以延伸到m+1
    for(int i=m;i>=1;i--){
        while(top&&sum[st[top]]>=sum[i]){//注意可以延伸的范围
            g2[st[top]]=i;top--;//向左延伸
        }
        st[++top]=i;
    }
    while(top){g2[st[top]]=0;top--;}//此时元素最小,最远可以延伸到0
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",ch[i]+1);
        for(int j=1;j<=m;j++){
            if(ch[i][j]=='W') sum[j]++;
            else sum[j]=0;
        }
        Spread();
        for(int j=1;j<=m;j++)//统计答案
            ans+=(g1[j]-j)*(j-g2[j])*sum[j];
    }
    printf("%lld",ans);
    return 0;
}

例3: P1191 矩形

题目描述:
思路:
本题为双倍经验题,解题思路与例二一致。
只是由矩形变换为正方形。
代码:

三、小结

当解决以上矩形类的问题时,一开始可能会找不到头绪。
但是经过简单的模拟可以发现,这些题都是在求序列中元素左、右以及左和右的延伸状态。
由此联想到单调栈,构造一个单调递增栈来求出元素左右第一个值小于它的位置。
这样问题便解决了,视具体情况使用单调栈可以很好地解绝时间复杂度的问题。
以上便是单调栈的基本应用,如果还有其他应用,我会继续更新的。

原文地址:https://www.cnblogs.com/cyanigence-oi/p/11707068.html

时间: 2024-07-31 14:25:52

[数据结构]单调栈的基本应用2的相关文章

数据结构--单调栈--烽火台

京东笔试编程题:烽火台,动态规划解法| Hexo https://www.nowcoder.com/discuss/8704?type=0&order=0&pos=5&page=0 战争游戏的至关重要环节就要到来了,这次的结果将决定王国的生死存亡,小B负责首都的防卫工作.首都处于一个四面环山的盆地中,周围的n个小山构成一个环,作为预警措施,小B计划在每个小山上设置一个观察哨,日夜不停的瞭望周围发生的情况. 一旦发生外敌入侵事件,山顶上的岗哨将点燃烽烟.若两个岗哨所在的山峰之间没有更

数据结构--单调栈--求最大子矩阵的大小

求最大子矩阵的大小给定一个整型矩阵map, 其中的值只有0和1两种, 求其中全是1的所有矩形区域中, 最大的矩形区域为1的数量.例如:1 1 1 0其中, 最大的矩形区域有3个1, 所以返回3.再如:1 0 1 11 1 1 11 1 1 0其中, 最大的矩形区域有6个1, 所以返回6. 解:将其放到一个矩阵中,同时从第0行开始计算,以该行打底时,直方图的最大面积 如第0行,数组为[1, 0, 1, 1] 此时按照下面的求直方图最大面积. 然后以第1行打底,此时数组为[2, 1, 2, 2],同

数据结构——单调栈&amp;单调队列(解决滑动窗口问题)

单调队列解答: /*******************单调队列!=优先队列单调队列是为了保证队列内的元素具有单调性,在保持了元素原本顺序的同时,对元素进行了过滤,舍弃了会影响单调性的元素而优先队列本质上还是个队列不会舍弃任何元素,每个元素都在队列之中,但是在队列中的位置由优先队列定义的优先级来确定,损失了原数组中的数据相对位置关系.所以很显然,单调队列是解决:寻找在某元素左侧区间或者右侧区间的最值问题,而优先队列的应用是寻找整个区间内的最高优先级别的内容./******************

数据结构--单调栈结构

解决的问题:在一个数组中,每一个位置的num,找到左边离num近的>num的值,和右边离num近的>num的值 时间复杂度:O(N) 准备一个栈:栈底到栈顶  从大到小 遍历数组,将数组中的元素num依次入栈,同时保证栈中的元素比num大,如果num大于栈中的元素,则将栈中的元素弹出,同时记录弹出元素的结果,num就是弹出元素右边离它近的大于它的,栈中它挨着的元素就是左边离它近的大于它的元素. 一直出栈,直到栈中元素大于将要入栈的元素,或者栈为空停止 当数组遍历完后,栈中的元素依次弹出,此时它

ACM数据结构-单调栈、队列

1.最大数 代码: #include <stdio.h> #include <memory.h> #include <math.h> #include <string> #include <vector> #include <set> #include <stack> #include <queue> #include <algorithm> #include <map> #define

【NOIP数据结构专项】单调队列单调栈

[洛谷P1901 ]发射站 http://www.luogu.org/problem/show?pid=1901 题目描述 某地有 N 个能量发射站排成一行,每个发射站 i 都有不相同的高度 Hi,并能向两边(当 然两端的只能向一边)同时发射能量值为 Vi 的能量,并且发出的能量只被两边最近的且比 它高的发射站接收. 显然,每个发射站发来的能量有可能被 0 或 1 或 2 个其他发射站所接受,特别是为了安 全,每个发射站接收到的能量总和是我们很关心的问题.由于数据很多,现只需要你帮忙计 算出接收

数据结构之单调栈单调队列模板

单调栈 int rear=0; for(int st=1;st<=N;st++) { while(rear>0&&H[que[rear]]>=H[st]) --rear; if(rear==0) le[st]=0; else le[st]=que[rear]; que[++rear]=st; } 单调队列 int que[maxn],elem[maxn]; int front=1,rear=0; for(int i=1;i<K;i++) { while(rear&g

小Z爱序列(NOIP信(sang)心(bin)赛)From Fall_Dream(粗制单调队列&amp;单调栈的算法解析)

原题: 小Z最擅长解决序列问题啦,什么最长公共上升然后下降然后上升的子序列,小Z都是轻松解决的呢. 但是小Z不擅长出序列问题啊,所以它给了你一道签到题. 给定一个n个数的序列ai,你要求出满足下述条件的点对的数量. 假设点对是(i , j),max(l,r)是[l,r]当中最大的ai的值. 这个点对满足条件当且仅当i+1<j 且 ai < max(i+1,j-1) < aj 为了简单,保证输入的是一个1-n的排列.相信你已经会做了吧? 输入/输出格式 输入数据第一行有一个数字n,然后第二

51nod1158 单调栈 个人的想法以及分析

单调栈,顾名思义就是保持内部元素单调性并且保证FILO的一种数据结构. 单调栈的代码实现没有什么难度,但是使用姿势难以想到. 在51nod1158中描述了这样一个问题: 给定一个 0-1 矩阵, 求这个矩阵最大的全 1 子矩阵的面积. 问题十分好理解. 现在,我们将这个问题拆分成一些子问题来逐个击破. 首先,为了保证程序操作的规律性与有序性,我们需要把问题等价成一个任意规模下的问题. 比如我们可以将其等价为: 对于第 i 行(列), 求出 以它为边界的最大全 1 子矩阵面积. 这样子我们最终所需