栈的应用

回眸只為那壹抹淺笑 提交于 2019-11-28 07:35:26

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

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

                                            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

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!