单调栈总结

单调栈总结


前言

刷了几道题没发现啥特别的用处,基本都是个板子;

单调栈可以O(n)的求一个序列中每个数左(右)边第一个大于(小于)他的位置;


例题

美丽的序列

为了研究这个序列的美丽程度,GD定义了一个序列的“美丽度”和“美丽系数”:对于这个序列的任意一个区间[l,r],这个区间的“美丽度”就是这个区间的长度与这个区间的最小值的乘积,而整个序列的“美丽系数”就是它的所有区间的“美丽度”的最大值。现在GD想要你帮忙计算这个序列的“美丽系数”。

 

其实就是个板子,考虑每个数字能够控制的区间就是右边第一个小于他的数字到左边第一个小于他的数字之间,单调栈秒

 

海报PLA

每个建筑物有高和宽,用矩形海报覆盖所有建筑物,求最少需要几张海报;

考虑到一张海报要贴必定是扩展到左右最远处最优,而不同的高度差意味着需要不同的海报;

换句话说,相同高度的两个建筑可以共用一张海报完成,所以初始ans=n,在单调栈过程中遇到相同高度ans-1即可

 

音乐会的等待

N个人正在排队进入一个音乐会。人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人。队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的。

写一个程序计算出有多少对人可以互相看见。

搞一下单调栈,栈内每有一个小于自己的元素就++ans,高度相同的不应该弹栈,就用二元组记录当前高度有多少人

贴代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
    int x=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=0,ch=getchar();
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return f?x:-x;
}
typedef pair<int,int> p;
int n,ans,top;
p st[1000010];
signed main()
{
    n=read();
    for(int x,i=1;i<=n;++i)
    {
        x=read();
        p now(x,1);
        for(;top&&x>=st[top].first;--top)
        {
            ans+=st[top].second;
            if(x==st[top].first) now.second+=st[top].second;
        }
        ans+=(top!=0);
        st[++top]=now;
    }
    printf("%lld\n",ans);
return 0;
}

 

长方形

给定n*m的01矩阵,求全部由1组成的矩阵的个数;

 

对于每个点(i,j)预处理出一个h[ i ][ j ]表示从(i,j)向上最多能延伸远

找出l[ i ][ j ]和r[ i ][ j ] 表示左边第一个h[ i ][ j ]第一个不大于当前h[ i ][ j ]的位置和右边第一个小于当前h[ i ][ j ]的位置

这样可以做到不重不漏

ans+=(j - l[ i ][ j ])*(r[ i ][ j ] - j)* h[ i ][ j ];

式子解释:左边在 j 到 l[ i ][ j ]之间任选位置,右边在r到r[ i ][ j ]之间任选位置,高度在1到h[ i ][ j ]之间任选位置,组合数学QAQ

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
    int x=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') ch=getchar(),f=0;
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return f?x:-x;
}
int n,m,res;
int mp[1010][1010];
int h[1010][1010];
int l[1010],r[1010];
int st[1010],top;
char ch;
inline void work(int x)
{
    top=0;
    for(int i=1;i<=m;++i)
    {
        while(top&&h[x][st[top]]>=h[x][i])
        {
            r[st[top--]]=i;
        }
        st[++top]=i;
    }
    while(top)
    {
        r[st[top--]]=m+1;
    }
    for(int i=m;i>=1;--i)
    {
        while(top&&h[x][st[top]]>h[x][i])
        {
            l[st[top--]]=i;
        }
        st[++top]=i;
    }
    while(top)
    {
        l[st[top--]]=0;
    }
}
signed main()
{
    n=read(),m=read();
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            for(ch=getchar();(ch!='.'&&ch!='*');ch=getchar());
            if(ch=='.') mp[i][j]=1,h[i][j]=h[i-1][j]+1;
        }
    }
    for(int i=1;i<=n;++i)
    {
        work(i);
        for(int j=1;j<=m;++j)
        {
            res+=(j-l[j])*(r[j]-j)*h[i][j];
        }
    }
    printf("%lld\n",res);
return 0;
}

 

玉蟾宫

给定01矩阵,求最大的全部由1构成的矩阵;

与上一题思路类似,这一题求出左右第一个小于的位置然后直接算最大

 

转载于:https://www.cnblogs.com/knife-rose/p/11492184.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值