第四周任务——单调栈

前言

这是大二的第一次正规周任务,要求写一个博客文章来记录自己所学的东西,于是就有了本文章。

目录

点击标题可返回目录

  • 含义
  • 模型
  • 实现
  • 应用
  • 练习
  • 参考

含义

单调栈,顾名思义,是一个内部元素保持单调性的栈结构,可能单调递增,也可能单调递减ps(这里的单调是指从栈顶到栈底)。

模型

对于一个单调递减的单调栈来说,假设有一个序列1、4、5、3、2、6,让这个序列从左到右依次进栈。
对于一个元素来说

  • 如果栈为空,则此元素直接进栈。
  • 如果栈顶元素小于此元素,则此元素直接进栈
  • 如果栈顶元素大于此元素,则将栈里的元素不断出栈,知道栈顶元素小于此元素或者栈为空时,此元素进栈。

实现模型如下:
对于1、4、5则直接进栈
第四周任务——单调栈_第1张图片
当3进栈时、则将5、4出栈
第四周任务——单调栈_第2张图片
最后2、6进栈、则栈的所有元素为6、2、1
第四周任务——单调栈_第3张图片

实现

上面介绍了单调递减的模型,单调递增则反之。
具体怎么实现还是要看代码

  • 单调递减
/*
a[n]共n个元素,编号为 0~n-1
*/
while(栈为空) 栈顶元素出栈; //先清空栈
a[n]=-1;
for(i=0;i<=n;i++)
{
	if(栈为空或入栈元素大于等于栈顶元素) 入栈;
	else 
	{
		while(栈非空并且栈顶元素大于等于入栈元素)
		{
			栈顶元素出栈;
			更新结果; 
		} 
		将最后一次出栈的栈顶元素(即当前元素可以拓展到的位置)入栈; 
		更新最后一次出栈的栈顶元素其对应的值; 
	} 	 
}
输出结果; 
  • 单调递增
/*
a[n]共n个元素,编号为 0~n-1
*/
while(栈为空) 栈顶元素出栈; //先清空栈
a[n]=inf;
for(i=0;i<=n;i++)
{
	if(栈为空或入栈元素小于等于栈顶元素) 入栈;
	else 
	{
		while(栈非空并且栈顶元素小于等于入栈元素)
		{
			栈顶元素出栈;
			更新结果; 
		} 
		将最后一次出栈的栈顶元素(即当前元素可以拓展到的位置)入栈; 
		更新最后一次出栈的栈顶元素其对应的值; 
	} 	 
}
输出结果; 

应用

单调栈概念简单,但相关用法还是要自己活学活用。大多跟序列和矩阵有关。比如求一个序列的所有子序列的最小值之和,或者一个01矩阵的最大子矩阵。

练习

题目一 Largest Rectangle in a Histogram
理解题目意思,就是求每一个序列元素到它左边第一个比他小的元素和到它右边第一个比他小的元素的距离之和,再乘上它的值就是一个矩形的大小,最后从这些矩形中找出最大值。找右边和找左边分别用两个递减的单调栈就行了。

#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
int n;
int a[100005];
int l[100005],r[100005];        //左右分别为该点上大于等于该点高度的柱子的端点
int main()
{
    while(scanf("%d",&n)&&n)
    {
        ll ans=0;
        memset(a, 0, sizeof(a));
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            l[i]=r[i]=i;
            while(l[i]>0&&a[l[i]-1]>=a[i])
            {
                l[i]=l[l[i]-1];
            }
        }
        for(int i=n-1; i>=0; i--)
        {
            while(r[i]<n-1&&a[r[i]+1]>=a[i])
            {
                r[i]=r[r[i]+1];
            }
        }
        for(int i=0; i<n; i++)
        {
            ll s=(ll)a[i]*(r[i]-l[i]+1);
            if(s>ans)
            {
                ans=s;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

题目二 Largest Submatrix of All 1’s
理解题目意思,发现这一题和上一题差不多的思路,只是需要对每一行进行单调栈的判断。同样维护两个单调栈。

//http://poj.org/problem?id=3494
#include
#include
#define int long long
using namespace std;
signed main(){
	int n,m;
	while(~scanf("%lld%lld",&n,&m)){
		int num[2010]={0};
		int maxSquare=0;
		int r[2010],l[2010];
		for(int i=0;i<n;i++){
		//每一行分别进行操作
			for(int j=0;j<m;j++){
				int x;
				scanf("%lld",&x);
				if(x){
					num[j]++;
				}else{
					num[j]=0;
				}
				l[j]=r[j]=j;
				while(l[j]>0&&num[l[j]-1]>=num[j]){
					l[j]=l[l[j]-1];
				}
			}
			for(int j=m-1;j>=0;j--){
				while(r[j]<m-1&&num[r[j]+1]>=num[j]){
					r[j]=r[r[j]+1];
				}
			} 
			for(int j=0;j<m;j++){
				maxSquare=max(maxSquare,(int)(r[j]-l[j]+1)*num[j]);
			}
		}
		printf("%lld\n",maxSquare);
	}
	return 0;
} 

其实还有一道组长出的题,但是为了防止收到攻击就暂时不写了。
剩下还有三道题则是暑期训练时做的题,这里篇幅有限,就只挂一个连接吧
2019牛客暑期多校训练题解

参考

本文章仅供周任务使用,参考的博客如下:
奆佬一
奆佬二
组长的神奇代码
大组长的神奇解析

你可能感兴趣的:(周任务)