【单调栈】【附上模板题】单调栈原理和应用

参考博客:单调队列与单调栈用法详解

单调栈原理及应用 详解 附各种类型的题目练习

参考 :[洛谷日报第9期]浅谈单调队列

其中有两幅图片是从上面的博客中获取的!!!

请尊重原创,大家可以点击上面链接找到对应的博客学习。

我只不过是借用两幅图片罢了 

 【单调栈】:

解决问题:

可以获取左边第一个或者右边第一个比当前位大或者小的数。

具体表现为:

1.最基础的应用就是给定一组数,针对每个数,寻找它和它右边第一个比它大的数之间有多少个数。

2.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。

3.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。

 

实现的伪代码:

const int N = 1e5+10;
int Sk[N],L[N],a[N],top;
//分别代表为:
//Sk[]:手工栈
//L[]:比当前为左边第一个大(小)
//a[]:原数组
//top:栈顶指针

    //默认左边:0 , 右边: n+1 无限大(小)
    for(int i=1;i<=n;i++){
        while( top>=1 && a[Sk[top]] ** a[i] ){//把不合法的弹出栈
            top -- ;
        }
        if(top){            //栈非空:栈顶元素就是我们要的左边第一个比当前值大(小)
            L[i] = Sk[top];
        }else{
            L[i] = 1;       //栈为空:左边为1
        }
        Sk[++top] = i;      //下标压栈
    }

画图模拟:

对于序列【3,4,2,6,4,5,2,3】

找出左边第一个比他大的值的下标

用栈维护:

如果当前值比栈顶元素要弹栈

如果当前值比栈顶元素要压栈

综合来说:

就是先把不合法的弹栈,然后必然要压栈

【单调栈】【附上模板题】单调栈原理和应用_第1张图片

 


 

poj_3250   Bad Hair Day

题意:

牛都往右看,然后有所有牛,能看到牛的总个数

#include 
#define For(i,L,R) for(int i=L;i<=R;i++)
using namespace std;
typedef long long ll;
const int N = 1e6+100;
ll a[N],St[N],R[N],top;
ll ans ;
int main()
{
    ll n;
    while(~scanf("%lld",&n)){
        ans = 0;
        For(i,1,n){
            scanf("%lld",&a[i]);
        }
        top = 0;
        for(int i=1;i<=n;i++){
            while( top >= 1 && a[St[top]] <= a[i] ){
                top--;
            }
            ans+=top;
            St[++top] = i;
        }

        printf("%lld\n",ans);
    }
    return 0;
}
/*
6
10 3 7 4 12 2
*/

 

 poj2559_Largest Rectangle in a Histogram

【题意】:

给你一个类似与柱状图一样,请问如果求出最大矩形,在柱状图中。

#include
using namespace std;
typedef long long ll;
const int N = 1e5+100;
ll a[N],L[N],R[N],St[N],top,n;
int main()
{
    while(~scanf("%lld",&n),n){
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        top = 0;
        for(int i=1;i<=n;i++){
            while( top>=1 &&  a[i] <= a[St[top]] )
                top--;
            if( top == 0){
                L[i] = 1;
            }else{
                L[i] = St[top] + 1;
            }
            St[++top] = i;
        }
        top = 0;
        for(int i=n;i>=1;i--){
            while( top>=1 &&  a[i] <= a[St[top]] )
                top--;
            if( top == 0){
                R[i] = n;
            }else{
                R[i] = St[top]-1;
            }
            St[++top] = i;
        }
        ll ans = -1,tmp = 0;
        for(int i=1;i<=n;i++){
            tmp = a[i] * (R[i]-L[i]+1);
            if ( tmp > ans ){
                ans = tmp ;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
/*
7 2 1 4 5 1 3 3
*/

 

poj_3494  Largest Submatrix of All 1’s

【题意】:

给你一个01矩阵,请问最大矩形是多少,这个矩形里面都是1.

这个题目非常有意思,真的不认真想是想不出来的。

看了网上题解:首先进行预处理,把所有01往上延伸。

譬如矩形:

 

0 0 1 0 

1 1 1 0

1 1 1 1

0 0 1 0

 

变成:

0 0 4 0

2 2 3 0

1 1 2 1

0 0 1 0

 

这样的做法就可以枚举每一行,然后看每一行中的左右:问题就转变成上一个题目一样了。

#include
#include
#include
using namespace std;
typedef long long ll ;
const int N = 2e3+10;
ll a[N][N],n,m;
ll L[N],R[N],St[N],top;
int main()
{
	while(scanf("%lld%lld",&n,&m)!=EOF){

		memset(a,0,sizeof(a));
		memset(L,0,sizeof(L));
		memset(R,0,sizeof(R));

		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				scanf("%lld",&a[i][j]);
			}
		}
		for(int i=n-1;i>=1;i--){
			for(int j=1;j<=m;j++){
				if( a[i][j] )
					a[i][j] = a[i][j] + a[i+1][j];
			}
		}
		/*
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				printf("%d%c",a[i][j],j==m?'\n':' ');
			}
		}
		*/
		ll ans = -0x3f3f3f3f , tmp ;
		for(int i=1;i<=n;i++){
			top = 0;
			for(int j=1;j<=m;j++){
				while( top>=1 && a[i][j]<=a[i][ St[top] ] ) top--;
				if( top ) L[j] = St[top]+1;
				else 	  L[j] = 1;
				St[++top] = j;
			}
			top = 0;
			for(int j=m;j>=1;j--){
				while( top>=1 && a[i][j]<=a[i][ St[top] ] ) top--;
				if( top ) R[j] = St[top]-1;
				else 	  R[j] = m;
				St[++top] = j;

				tmp = (a[i][j])  * ( R[j] - L[j] + 1 ) ;
				ans = max( ans ,tmp );
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

 

 

poj_2796  Feel Good

【题意】:

区间最小值乘以区间和,求最大值

#include
#include
#define For(i,L,R) for(int i=L;i<=R;i++)
using namespace std;
typedef long long ll;
const int N = 1e5+100;
ll sum[N],a[N],R[N],L[N],St[N],top;
int main()
{
	int n;
	while(scanf("%d",&n)!=EOF){
		
		For(i,1,n) scanf("%d",&a[i]),
					sum[i] = sum[i-1]+a[i],
					L[i] = R[i] = 0;
		top = 0; 
		For(i,1,n){
			while( top>=1 && a[i] <= a[St[top]] ){
				top -- ;
			}
			if(top){
				L[i] = St[top] + 1;
			}else{
				L[i] = 1;
			}
			St[++top] = i;
		}
		top = 0 ;
		for(int i=n;i>=1;i--){
			while( top>=1 && a[i] <= a[St[top]] ){
				top -- ;
			}
			if(top){
				R[i] = St[top]-1;
			}else{
				R[i] = n;
			}
			St[++top] = i;
		}
		ll tmp = 0 , ans = -0x3f3f3f3f ,ansL,ansR;
		for(int i=1;i<=n;i++){
			tmp = ( sum[R[i]] - sum[L[i]-1] ) *a[i];
			if( ans < tmp ){
				ans = tmp;
				ansL = L[i];
				ansR = R[i];
			}
		}
		printf("%lld\n%lld %lld\n",ans,ansL,ansR);
	}
	return 0;
}	

 

你可能感兴趣的:(单调队)