题目1:

设计一个支持push,pop,top等操作并且可以在O(1)时间内检索出最小元素的堆栈。
push(x)–将元素x插入栈中
pop()–移除栈顶元素
top()–得到栈顶元素
getMin()–得到栈中最小元素

思路:

该题目中push,pop,top三个操作对于一个栈来说均可在O(1)时间内完成,而对于getMin操作则需要维护一个数组或者栈,来保存栈中每入栈一个数时的最小值,这样即可在O(1)时间完成getMin操作。

题目2:

你将要实现一个功能强大的整数序列编辑器。
在开始时,序列是空的。
编辑器共有五种指令,如下:
1、“I x”,在光标处插入数值x。
2、“D”,将光标前面的第一个元素删除,如果前面没有元素,则忽略此操作。
3、“L”,将光标向左移动,跳过一个元素,如果左边没有元素,则忽略此操作。
4、“R”,将光标向右移动,跳过一个元素,如果右边没有元素,则忽略次操作。
5、“Q k”,假设此刻光标之前的序列为a1,a2,…,an,输出max1≤i≤kSi,其中Si=a1+a2+…+ai

输入格式

第一行包含一个整数Q,表示指令的总数。
接下来Q行,每行一个指令,具体指令格式如题目描述。

输出格式

每一个“Q k”指令,输出一个整数作为结果,每个结果占一行。

数据范围

1≤Q≤1e6
|x|≤1e3
1≤k≤n

输入样例

8
I 2
I -1
I 1
Q 3
L
D
R
Q 2

输出样例

2
3

思路(对顶栈):

此题的四种操作都在光标位置处发生,并且操作完成后光标至多再移动一个位置,考虑到该特性,我们可以使用对顶栈来维护该序列,即光标发生移动则只需要改变对顶栈的栈顶就行,并且再用一个数组来维护光标前序列中前缀和的最大值。

代码:

#include<iostream>
#include<stack>
#include<algorithm>
#include<climits>
using namespace std;
const int N=1000010;
int S[N],s,summax[N];//S[N]是前缀和数组,而summax[N]是维护光标前的序列中前缀和最大值的数组
stack<int> a,b;//对顶栈
int main()
{
    int Q,x,k;
    char c;
    cin>>Q;
    summax[0]=INT_MIN;//此举很重要
    while(Q--)
    {
        cin>>c;
        if(c=='I')
        {
            cin>>x;
            a.push(x);
            s++;
            S[s]=x+S[s-1];
            summax[s]=max(summax[s-1],S[s]);
        }
        else if(c=='D')
        {
            if(!a.empty())//此处if判断如果不加则会Segmentation Fault,因为如果栈已空,则再没有元素出栈,以下if语句同理
            {
                a.pop();
                s--;
            }
        }
        else if(c=='L')
        {
            if(!a.empty())
            {
                b.push(a.top());
                a.pop();
                s--;
            }
        }
        else if(c=='R')
        {
            if(!b.empty())
            {
                a.push(b.top());
                s++;
                S[s]=b.top()+S[s-1];
                b.pop();
                summax[s]=max(summax[s-1],S[s]);
            }
        }
        else if(c=='Q')
        {
            cin>>k;
            cout<<summax[k]<<endl;
        }
    }
    return 0;
}

题目3:

直方图是由在公共基线处对齐的一系列矩形组成的多边形。
矩形具有相等的宽度,但可以具有不同的高度。
例如,图例左侧显示了由高度为2,1,4,5,1,3,3的矩形组成的直方图,矩形的宽度都为1:

在这里插入图片描述
通常,直方图用于表示离散分布,例如,文本中字符的频率。
现在,请你计算在公共基线处对齐的直方图中最大矩形的面积。
图例右图显示了所描绘直方图的最大对齐矩形。

输入格式

输入包含几个测试用例。
每个测试用例占据一行,用以描述一个直方图,并以整数n开始,表示组成直方图的矩形数目。
然后跟随n个整数h1,…,hn。 这些数字以从左到右的顺序表示直方图的各个矩形的高度。 每个矩形的宽度为1。 同行数字用空格隔开。
当输入用例为n=0时,结束输入,且该用例不用考虑。**

输出格式

对于每一个测试用例,输出一个整数,代表指定直方图中最大矩形的区域面积。
每个数据占一行。
请注意,此矩形必须在公共基线处对齐

数据范围

1≤n≤1e5, 0≤hi≤1e9

输入样例

7 2 1 4 5 1 3 3 4
1000 1000 1000 1000
0

输出样例

8
4000

思路(单调栈,时间复杂度O(n)):

单调栈的思想就是:及时排除不可能的选项,保持策略的高度有效性和秩序性。
本题中想要求最大的矩形区域面积,即需要枚举每个小矩形R并且找到该小矩形左侧第一个比其矮的小矩形A,以及右侧第一个比其矮的小矩形B,此时A前面的矩形对R来说已经无用了,对R后面即将要枚举的小矩形来说也没有用,所以可以直接删除,然后该序列就可以成为一个依照矩形高度单调递增的栈。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100010;
ll res;//直方图中最大矩形的区域面积
int h[N],hleft[N],hright[N],q[N],num;
//h[N]为直方图的各个矩形的高度,hleft[N]保存直方图中各个矩形左边第一个比该矩形矮的矩形编号
//hright[N]保存直方图中各个矩形右边第一个比该矩形矮的矩形编号
//q[N]是单调栈,保存单调递增的矩形高度的矩形编号
void get(int n,int k[])//该函数查找直方图中各个矩形左边第一个比该矩形矮的矩形编号
{
    for(int i=1;i<=n;i++)
    {
        while(h[i]<=h[q[num]]&&num) num--;//如果栈顶的矩形高度小于等于h[i]那么前面栈中的元素均没有用,所以出栈
        k[i]=q[num];//当前栈顶元素一定是第一个比该矩形矮的矩形
        q[++num]=i;//该矩形入栈
    }
}
int main()
{
    int n;
    while(cin>>n,n)//循环输入n,当n为0时终止输入
    {
        num=0;res=0;//num和res均得赋初值为0
        for(int i=1;i<=n;i++)
        cin>>h[i];
        h[0]=-1;
        h[n+1]=-1;//这里让h[0]和h[n+1]都为-1,是为了方便处理边界问题
        get(n,hleft);
        reverse(h+1,h+n+1);//将[1,n+1)区间内的数组翻转即可把求右侧的问题转换为求左侧的问题,只是会带来后面计算矩形区域面积的麻烦...(编号直接绕晕)
        num=0;//这里让num=0相当于让栈为空,不这么做答案肯定不对
        get(n,hright);
        // for(int i=1;i<=n;i++)
        // cout<<hleft[i]<<" "<<n-hright[n-i+1]+1<<endl;
        for(int i=1;i<=n;i++)
        res=max(res,h[n-i+1]*(n-hright[n-i+1]+1-hleft[i]-1ll));//这里的计算方式及编号自己去理解吧,画画就懂了
        cout<<res<<endl;
    }
    return 0;
}

题目4:

这里有n列火车将要进站再出站,但是,每列火车只有1节,那就是车头。这n列火车按1到n的顺序从东方左转进站,这个车站是南北方向的,它虽然无限长,只可惜是一个死胡同,而且站台只有一条股道,火车只能倒着从西方出去,而且每列火车必须进站,先进后出。
也就是说这个火车站其实就相当于一个栈,每次可以让右侧头火车进栈,或者让栈顶火车出站。
车站示意如图:

        出站<——    <——进站
               |车|
               |站|
               |__|

现在请你按《字典序》输出前20种可能的出栈方案。
输入格式

输入一个整数n,代表火车数量。

输出格式

按照《字典序》输出前20种答案,每行一种,不要空格。

数据范围

1≤n≤20

输入样例

3

输出样例

123
132
213
231
321

思路:

本题方法很多,由于本题所给的数据范围较小,所以猜测本题允许的时间复杂度应该是O(2ⁿ),所以可以考虑用搜索(枚举/递归)的方法去做,一共维护三个状态,出站的序列用vector保存,车站和进站序列用两个栈保存,对于每一步操作,要么火车进站,要么火车从车站出站。

代码:

#include<iostream>
#include<stack>
#include<vector>
using namespace std;
stack<int> a,b;//进站和车站的两个栈
vector<int> c;//出站的vector序列
int cnt=20;//最多输出20种方案
int n;
void dfs()//搜索的函数
{
    if(!cnt) return;//如果超过20则返回
    if(c.size()==n)//如果出站序列元素个数达到n,则输出
    {
        for(auto x:c)
        cout<<x;
        cout<<endl;
        cnt--;//输出了一种方案,cnt--
        return;
    }
    if(!a.empty())//当a不为空,则选择将a中栈顶元素出栈进入到序列c中
    {
        c.push_back(a.top());
        a.pop();
        dfs();//进入下一步
        a.push(c.back());
        c.pop_back();//两步还原现场
    }
    if(!b.empty())//当b不为空,则选择将b中栈顶元素出栈进入到栈a中
    {
        a.push(b.top());
        b.pop();
        dfs();//进入下一步
        b.push(a.top());
        a.pop();//两步还原现场
    }
}
int main()
{
    cin>>n;
    for(int i=n;i>=1;i--)
    b.push(i);//先将要进站的序列存入栈b
    dfs();//开始搜索
    return 0;
}

栈——未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值