栈的应用

Posted chdy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了栈的应用相关的知识,希望对你有一定的参考价值。

栈 属于 一种最基本的数据结构  具体的 维护一个一个序列 且这个序列中的元素满足先进后出 或者 后进先出类似于火车进站 可以想象一下。

而单调的栈 具有一些性质:

                                            1 单调栈里的元素具有单调性

                                            2 元素被加入到栈前 会在栈顶把破坏栈单调性的元素都删除。

                                            3 使用单调栈可以找到元素向左遍历第一个比他小的元素 也可以找到元素向左遍历第一个比他大的元素。(显然 这是单调性的应用吧) 

当然 也有一些其他栈的变形 辅助贪心 如 LIS 维护数列的性质 :可持久化单调栈 什么的。还有一些比较有意思的是 对顶栈维护整个序列。栈还和卡特兰数有关 这些坑都填一下吧。

对顶栈:LINK:HDU4699 

题目意思让你维护一个数列和一个光标 有多个操作 模拟每个操作 并输出对应结果。Q<=1e6

I x 在光标位置插入一个x 插入完后光标移动到x之后 D 删除光标前一个数字 L 光标向左移 R 光标向右移 Q k 在k之前的最大前缀和 且k不超过当前光标位置。

这个题目很有意思由于动态的插入数字我们可以考虑BST 或者 treap splay 。我乍一看是splay 维护区间位置尽管这样做也可以但是对于Q k 的询问呢?设f[i]表示i之前的前缀最大值 s[i]表示前i个数的和。

那么显然有 s[i]+=s[i-1] f[i]=max(f[i-1].s[i]); 由于每次询问都是在光标之前我们可以只维护光标之前的这个东西 并且光标每次至多移动1个位置我们可以动态的维护这个东西了。

复杂度Qlogn 不过写起来会比较麻烦也没有必要 但是貌似没有更好的解决方法了 此时栈的作用就非常的大了。

当然也可以叫做对顶队列 我们发现 由于每次都是动态插入 维护一个 队列的话每次移动是O(n)的 由于每次只在光标处+数字所以可以维护两个队列一个是光标前的一个是光标后的。这样做就舒服多了。

wa 了近乎3次 我是真的菜 操作可能不合法 f[0]=-INF 多组数据 直接怀疑人生啊 这就是菜鸡的表现没有认真读题(题目是英文的我懒得多看两眼...

技术图片
//#include<bits/stdc++.h>
#include<iostream>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<utility>
#include<vector>
#include<iomanip>
#include<cstdlib>
#define INF 2000000000
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define RE register
#define EPS 1e-8
#define ll long long
#define ull unsigned long long
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()

    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;

inline int read()

    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9)if(ch==-)f=-1;ch=getchar();
    while(ch>=0&&ch<=9)x=x*10+ch-0;ch=getchar();
    return x*f;

const int MAXN=1000010;
int Q;
char c[5];
int top,top1;
int a[MAXN],b[MAXN],f[MAXN],s[MAXN];
int main()

    //freopen("1.in","r",stdin);
    while(scanf("%d",&Q)==1)
    
        top=top1=0;f[0]=-INF;
        for(int i=1;i<=Q;++i)
        
            int x;
            scanf("%s",c+1);
            if(c[1]==I)
            
                x=read();
                a[++top]=x;
                s[top]=s[top-1]+a[top];
                f[top]=max(f[top-1],s[top]);
            
            if(c[1]==D)--top;
            if(c[1]==L)
            
                if(!top)continue;
                b[++top1]=a[top];
                --top;
            
            if(c[1]==R)
            
                if(!top1)continue;
                a[++top]=b[top1];
                s[top]=s[top-1]+a[top];
                f[top]=max(f[top-1],s[top]);
                --top1;
            
            if(c[1]==Q)
            
                x=read();
                printf("%d\n",f[x]);
            
        
    
    return 0;
View Code

值得一提的是这种方法的复杂度是O(n) 的而且还好写。值得推荐。

卡特兰数:是组合数学中一个长出现在各种计数问题的数列。(前两天某人hzk还叫我去学组合数学...的确到现在才发现数据结构就是坑 学多了没好处 不如来点思维的感受一下思考的快乐。

卡特兰数 数列 : 1 1 2 5 14 42 132 429...看出来什么没貌似没有很显然的递推关系 的确很难确定一个公式 因为这个公式是个组合数 光看的话显然很难看出来。

Cn =C0Cn-1+C1Cn-2+...Cn-1C0(n>=2)其中C0=1 C1=1; 哇这原来是一个对称的式子。这意味着 我们之间去递推好像是n^2的。

1 卡塔兰数就是研究栈的问题的。栈的问题模型:给出n个数1~n 和一个栈。每个数都要进出栈一次如果进入栈的顺序是 1,2,3,...n那么可能的出栈序列有多少种。

其中 上述Cn 就是 n个数 答案。(这个公式 找规律应该是可以找到的吧不过看起来有点困难...

自然有通项公式(斐波那契都有通项这个怎么会没有:Cn=c(2n,n)-c(2n,n+1)通过非常简单的变换可得->Cn=C(2n,n)/(n+1);

众所周知求组合数 的复杂度可以是O(n)的 那么这个求Cn得复杂度也可以是O(n)的。

考虑为什么这个问题的答案会是Cn呢?关于证明 见算法进阶P49 。

2 这个还和二叉树构成有关 问n个节点 能构成多少棵不同的二叉树 因为编号不影响结果 所以考虑中序遍历 根节点为第k个出栈 那么就是左边的Ck-1*Cn-k这东西其中k取0~n-1那么就和上述式子一毛一样了。

3 凸多边形的三角形划分。这里就比较玄学了 我看网上有的说的感觉不太对...下面是我的理解 由于划分的顺序和 划分的数量无关我们先选择一条边作为划分基底。

此时连边 以基底为三角形的底边 划分 左边形成一个i边形 右边形成一个 n-i+2变形 其中(i>=3)可以显然 问题规模不断缩小(至少缩小1...

其实就是 Cn=Ci*C(n-i+2) 其中i€[3,n-1] 令ti=c(i+3) ti=上述式子 感觉很稳...(口胡的,,,

4

5

6

7

还有这么多 我看不下去了 往后了...

表达式计算 中缀表达式就那样O(n)的求 不会的话见 代码(luogu 1054)

技术图片
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define INF 2147483646
#define ll long long
#define R register
#define mod 131
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()

    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;

inline int read()

    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9)if(ch==-)f=-1;ch=getchar();
    while(ch>=0&&ch<=9)x=x*10+ch-0;ch=getchar();
    return x*f;

const int MAXN=55;
char s[MAXN];
int n,sl,pos;
char c[MAXN][MAXN];
int flag[MAXN],vis[MAXN];
stack<int>num;
stack<char>op;
void get(char *a)

    for(int i=0;i<=52;i++)a[i]=\0;
    char c=getchar();pos=0;
    while(c==\n||c==\r) c=getchar();
    while(c!=\n&&c!=\r)
    
        a[++pos]=c;
        c=getchar();
    

inline int fast_pow(int b,int p)

    int ans=1;
    for(int i=1;i<=p;++i)ans=ans*b%mod;
    return ans;

inline void js()

    int x=num.top()%mod,ss;num.pop();
    int xx=num.top()%mod;num.pop();
    char w=op.top();op.pop();
    if(w==+)ss=x+xx;
    if(w==-)ss=xx-x;
    if(w==*)ss=x*xx%mod;
    if(w==^)ss=fast_pow(xx,x);
    num.push(ss%mod);

inline int calc(int x,char *w,int len)

    while(num.size())num.pop();
    while(op.size())op.pop();
    for(int i=1;i<=len+1;++i)op.push(();
    w[len+1]=);
    for(int i=1;i<=len+1;++i)
    
        if(w[i]== )continue;
        //cout<<w[i]<<endl;
        if((w[i]>=0&&w[i]<=9)||w[i]==a)
        
            if(w[i]==a)num.push(x);continue;
            int s=w[i]-0;
            while(w[i+1]>=0&&w[i+1]<=9)
            
                s=s*10+w[i+1]-0;
                ++i;
            
            num.push(s%mod);
        
        else
        
            if(w[i]==()op.push(w[i]);
            if(w[i]==+||w[i]==-)
            
                while(op.top()!=()js();
                op.push(w[i]);
            
            if(w[i]==*)
            
                while(op.top()==*||op.top()==^)js();
                op.push(w[i]);
            
            if(w[i]==^)
            
                while(op.top()==^)js();
                op.push(w[i]);
            
            if(w[i]==))
            
                while(op.top()!=()js();
                op.pop();
            
        
    
    return (num.top()%mod+mod)%mod;

int main()

    //freopen("1.in","r",stdin);
    //scanf("%s",s+1);
    get(s);sl=pos;
    //for(int i=1;i<=sl;++i)cout<<s[i];
    n=read();
    for(int i=1;i<=n;++i)get(c[i]),flag[i]=pos;
    for(int i=1;i<=10;++i)
    
        int ans=calc(i,s,sl);
        //cout<<ans<<endl;
        for(int j=1;j<=n;++j)
        
            if(vis[j])continue;
            if(ans!=calc(i,c[j],flag[j]))vis[j]=1;
            //cout<<calc(i,c[j],flag[j])<<endl;
        
        //printf("%d\n",ans);
    
    for(int i=1;i<=n;++i)
    
        if(vis[i])continue;
        printf("%c",char(A+i-1));
    
    return 0;
View Code

单调栈:

LINK:SP1805

求水平线上最大矩阵面积。n<=100000;考虑答案的必要性 下界max每一个矩形的面积。

考虑矩形联系起来怎么统计到答案上 貌似不好搞 分类讨论一下分析答案的存在性:全部都是递增的矩形。那么答案为每一个矩形的长*后面连续矩形的宽。

考虑递减的矩形 倒着来重复刚才的过程。但是为了得到一个普遍解我们不能倒着来 找到正着的一个普遍寻找规律的答案线索。遇到一个自己比上一个矩阵小 那么就不要上一个矩阵累积中间被弹出矩阵的宽度。

最后答案为 每个矩阵长*前面被弹出的矩阵宽度。这还不够优秀 因为上一个矩阵比当前高那么除了他本身给上上一个带来的贡献 它自己就不能再形成贡献了 扔掉 然后把它看成和我一样高的东西。

考虑参差不齐的矩形(也就是正解):obviosly 我们可以把这一段矩形 看成先递增后递减...然后呢?栈里维护单调递增的矩形就好了。

技术图片
//#include<bits/stdc++.h>
#include<iostream>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<utility>
#include<vector>
#include<iomanip>
#include<cstdlib>
#define INF 1000000000
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define RE register
#define EPS 1e-8
#define ll long long
#define ull unsigned long long
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()

    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;

inline ll read()

    ll x=0,f=1;char ch=getc();
    while(ch<0||ch>9)if(ch==-)f=-1;ch=getc();
    while(ch>=0&&ch<=9)x=x*10+ch-0;ch=getc();
    return x*f;

const ll MAXN=100010;
ll n,top,ans;
ll a[MAXN],s[MAXN],w[MAXN];
int main()

    //freopen("1.in","r",stdin);
    while(1)
    
        n=read();ans=0;
        if(!n)break;
        for(ll i=1;i<=n;++i)a[i]=read();
        a[n+1]=-1;s[0]=-1;top=0;
        for(ll i=1;i<=n+1;++i)
        
            if(a[i]>=s[top])
            
                s[++top]=a[i];
                w[top]=1;
            
            else
            
                ll p=0;
                while(a[i]<s[top])
                
                    p+=w[top];
                    ans=max(ans,s[top]*p);
                    --top;
                
                s[++top]=a[i];w[top]=p+1;
            
        
        printf("%lld\n",ans);
    
    return 0;
View Code

LINK: luogu3722

 

以上是关于栈的应用的主要内容,如果未能解决你的问题,请参考以下文章

数据结构实验二 栈的应用——迷宫求解问题(c++版本)

C语言 任意表达式求值。(栈的应用

栈的顺序存储结构及应用(CJava代码)

数据结构栈的实现与简单应用

第三章:2.栈和队列 -- 栈的应用举例

栈的应用(括号匹配算法实战)