栈和排序(csdn一大堆水题解,我自己出一个吧,)后缀维护+模拟

栈和排序

在这里插入图片描述

题意:

给你一个入栈排列,要你求字典序最大的出栈序列。

思路:

最先想到的:最先出的肯定是n。(它最大,肯定要在最前面)

反思x1:

我一开始想的时候走远了。错误示范:以为找到最大n后,就看栈顶和后面的。如果后面有次大就,其它直接入栈直到找到次大。如果次大在前面,就把大于待入栈元素x的栈顶元素先出栈,其实这种思路是错的,假如次大是第一个入栈,那么无论如何都会去 “如果次大在前面,就把大于待入栈元素x的栈顶元素先出栈”,这种情况,假如把栈顶元素出栈了,那么可能后面待入栈元素有更大的,就矛盾了。

所以引进第二种思路,

下文中的待入栈元素指的是输入,(以用红色标出)
下文中的指的是为解决问题定义的栈。

  1. 对于找到最大后。
  2. 考察顶元素和待入栈元素中的最大(要 O ( n ) O(n) O(n)的查询后面待入栈元素的最大)
  3. 如果顶元素比后面的都大(比待入栈元素的最大 大,那么肯定比后面的都大)那么顶元素出,重复步骤2,3,直到不满足(或为空)。
  4. 3不满足来到4。直到找到待入栈元素的最大,其他都入
  5. 之后重复步骤2
  6. 当没有待入栈元素时,把内元素出就行了。

AC(数据1e3)

#include <iostream>
#include <stack>
#include <queue>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
stack<int>st;
int n,vis[300],x,maxx;
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n;
    For(i,1,n)
    {
        cin>>x;
        maxx=0;
        For(i,1,n)if(!vis[i])maxx=max(i,maxx);
        //没见过的元素,就是待入栈元素
        while(!st.empty()&&maxx<st.top())
        {
            cout<<st.top()<<' ';st.pop();
        }
        st.push(x);
        vis[x]=1;//cout<<i<<endl;
    }
    while(!st.empty())
    {
        cout<<st.top()<<' ';
        st.pop();
    }
    return 0;
}



反思x2:

很明显,上面的代码,当数据量很大时,是会tle的,因为第2步中0n的查询

优化方法:

也很简单,对于待入栈元素,就是后面的元素中的最大值,那么可以用一个suf【】(后缀去预处理位置 i 以后的最大值)

AC(数据1e6)

#include <iostream>
#include <stack>
#include <queue>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
const int maxn=1e5+10;
stack<int>st;
int n,x,a[maxn],suf[maxn];//,pre[maxn]maxx,vis[maxn],t,
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n;
    For(i,1,n)cin>>a[i];
    for(int i=n; i>=1; i--)
    {
        if(a[i]>suf[i+1])suf[i]=a[i];
        else suf[i]=suf[i+1];
    }
    For(i,1,n)
    {
        x=a[i];
        while(!st.empty()&&suf[i]<st.top())
        //suf【i】就是优化,省了0n查询
        {
            cout<<st.top()<<' ';st.pop();
        }
        st.push(x);
    }
    while(!st.empty())//把剩下的输出
    {
        cout<<st.top()<<' ';
        st.pop();
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值