栈 = =

理解:先进后出

基本知识:

(1)栈限定为只能在一端进行插入和删除操作;

(2)栈的实现只需要一个一维数组和一个指向栈顶的变量top。我们通过top来对栈进行插入和删除操作

基本操作:

(1)初始化

top = 0;

(2)入栈

top ++;
s[top] = x; //假设需要入栈的字符暂存在字符变量x中

代码可简写为:

s[++ top] = x; //假设需要入栈的字符暂存在字符变量x中

(3)出栈

top --;

分类:

1.普通栈

// tt表示栈顶
int stk[N], tt = 0;

// 向栈顶插入一个数 
stk[ ++ tt] = x;

//从栈顶弹出一个数 
tt --;

//栈顶的值
stk[tt];

// 判断栈是否为空
if(tt > 0)
{
	...
}

2.单调栈

*常见模型:找出每个数左边离它最近的比它大/小的数

模板:

int i;
int tt = 0;
for(i = 1 ; i <= n ; i ++)
{
	while(tt > 0 && check(stk[tt], i)) tt --; //出栈
	stk[ ++ tt] = i; //入栈
}

题目:

题目1(普通栈)(裸题)

实现一个栈,栈初始为空,支持四种操作:

  1. push x – 向栈顶插入一个数 x
  1. pop – 从栈顶弹出一个数;
  2. empty – 判断栈是否为空;
  3. query – 查询栈顶元素。

现在要对栈进行 M个操作,其中的每个操作 3 和操作 4都要输出相应的结果。

输入格式:

第一行包含整数 M,表示操作次数。接下来 M行,每行包含一个操作命令,操作命令为 push xpopemptyquery 中的一种。

输出格式:

对于每个 emptyquery 操作都要输出一个查询结果,每个结果占一行。

其中,empty 操作的查询结果为 YESNOquery 操作的查询结果为一个整数,表示栈顶元素的值。

数据范围:

1≤M≤100000,1≤x≤109

输入样例:

10
push 5
query
push 6
pop
query
pop
empty
push 4
query
empty

输出样例:

5
5
YES
4
NO

AC代码1:(数组模拟)

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int stk[N]; 
int tt;

int main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(false);
	
	int i;
	int m;
	
	cin >> m;
	
	while(m --)
	{
		string s;
		cin >>s;
		
		if(s == "push")
		{
			int x;
			cin >> x;
			stk[++ tt] = x;
		}
		
		if(s == "pop")  tt --;
		
		if(s == "query")  cout << stk[tt] << endl;
		
		if(s == "empty")  cout << (tt == 0 ? "YES" : "NO") << endl;
	} 
	
	return 0;
}

AC代码2:(STL)

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

stack< int >stk;

int tt;

int main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(false);
	
	int i;
	int m;
	cin >> m;
	
	while(m --)
	{
		string s;
		cin >>s;
		
		if(s == "push")
		{
			int x;
			cin >> x;
			stk.push(x); // push函数在栈顶插入元素x 
		}
		
		if(s == "pop")  stk.pop(); // pop函数删除栈顶元素 
		
		if(s == "query")  cout << stk.top() << endl; // top函数访问栈顶元素 
		
		if(s == "empty")
		{
			if(stk.empty()) // empty函数查询栈是否为空,如果为空,则返回真
			{
				cout << "YES" << endl; 
			} 
			else 
			{
				cout << "NO" << endl;
			}
		}  
	} 
	
	return 0;
}

题目2(单调栈)(裸题)

题目描述:

给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。

输入格式

第一行包含整数 N,表示数列长度。第二行包含 N个整数,表示整数数列。

输出格式

共一行,包含 N个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1。

数据范围

1≤N≤10^5,1≤数列中元素≤10^9

输入样例:

5
3 4 2 7 5

输出样例:

-1 3 -1 2 2

AC代码:

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int stk[N],tt = 0;

int main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(false);
	int i;
	int n;
	cin >> n;
	for(i = 0 ; i < n ; i ++)
	{
		int x;
		cin >> x;
		while(tt > 0 && stk[tt] >= x)  tt --; // 比x大且在x的左边比然后不能输出出来,直接弹出 
		if(tt > 0) cout << stk[tt] << ' '; // 输出的是里x左边最近且比x小的数 
		else  cout << "-1" << ' '; // 栈空的话代表没有元素比x小
		
		stk[++ tt] = x; // 将刚才弹出的栈顶元素更新为更加小的(更优的)x 
	}
	return 0;
}

题目3(单调栈)(简单题)

题目描述:

直方图是由在公共基线处对齐的一系列矩形组成的多边形。

矩形具有相等的宽度,但可以具有不同的高度。

例如,图例左侧显示了由高度为 2,1,4,5,1,3,3

的矩形组成的直方图,矩形的宽度都为 1

通常,直方图用于表示离散分布,例如,文本中字符的频率。

现在,请你计算在公共基线处对齐的直方图中最大矩形的面积。

图例右图显示了所描绘直方图的最大对齐矩形。

输入格式

输入包含几个测试用例。

每个测试用例占据一行,用以描述一个直方图,并以整数 n

开始,表示组成直方图的矩形数目。

然后跟随 n个整数 h1,…,hn。

这些数字以从左到右的顺序表示直方图的各个矩形的高度。

每个矩形的宽度为 1。

同行数字用空格隔开。

当输入用例为 n=0时,结束输入,且该用例不用考虑。

输出格式:

对于每一个测试用例,输出一个整数,代表指定直方图中最大矩形的区域面积。

每个数据占一行。

请注意,此矩形必须在公共基线处对齐。

数据范围

1≤n≤100000,0≤hi≤1000000000

输入样例:

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

输出样例:

8
4000

思路:

1.若暴力

首先考虑暴力做法,以每个矩形的高度为准,向两边扩展,直到遇到比它矮的为止

如图所示,记录每个矩形向两侧扩展的边界 [l,r]

它扩展出的矩形面积为 s=(r−l+1)∗h

最优解会在这些扩展出的矩形中产生。

时间复杂度 为(n^2):

每个矩形向两侧扩展的最大宽度为矩形个数 n,共进行 n 次这样的操作

2.单调栈优化

在计算每个矩形可以扩展的左边界时,可以发现有一些矩形是可以不考虑的

如图所示,由于2号矩形的存在,在计算2右边的矩形的左边界时,可以不考虑1号矩形。

高度高于2号的矩形会被2卡住,高度小于等于2号的也必然小于等于1号。

观察可知,在计算左边界时,靠左的且较高的矩形可以省略,因此可以用单调栈优化。

q[tt]作为栈顶元素下标,当满足h[q[tt]] >= h[i]时,弹出栈顶
时间复杂度 为O(n)

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 1e5 + 10;

int h[N],q[N],l[N],r[N];

int main()
{
	int i;
    int n;
    while(cin >> n && n != 0)
    {
        for(i = 1; i <= n; i ++)  cin>>h[i];
        h[0]=h[n+1]=-1;
 
        int tt=-1;
        q[++ tt]=0;
        for(i = 1 ; i <= n ; i ++)
        {
            while(h[q[tt]] >= h[i])  tt--;
            l[i] = i - q[tt];
            q[++ tt] = i;
        }
        
        tt=0;
        q[0] = n+1;
        for(i = n ; i ; i --)
        {
            while(h[q[tt]] >= h[i])  tt--;
            r[i] = q[tt]-i;
            q[++ tt] = i;
        }
 
        ll res = 0;
        for(i = 1;i <= n ; i ++)  res = max(res,(ll)h[i]*(l[i] + r[i] - 1));
        cout<<res<<endl;
    }
    return 0;
}

题目4(单调栈)(简单题)

题目描述

You are going to implement the most powerful editor for integer sequences.

The sequence is empty when the editor is initialized.

There are 5 types of instructions.

  • I x Insert x after the cursor.
  • D Delete the element before the cursor.
  • L Move the cursor left unless it has already been at the begin.R Move the cursor right unless it has already been at the end.
  • Q k Suppose that the current sequence BEFORE the cursor is {a1,a2…an}\{ a_1,a_2\dots a_n \}{a1​,a2​…an​}. Find max1≤i≤kSimax_{1\leq i\leq k} S_imax1≤i≤k​Si​ where  S;=a1+a2+⋅⋅+a¡S_; = a_1 +a_2+··+ a_¡S;​=a1​+a2​+⋅⋅+a¡​
 

输入描述

The input file consists of multiple test cases. For each test case:
The first line contains an integer Q, which is the number of instructions. The next Q lines contain an instruction asdescribed above.
(1<Q<1061 \lt Q \lt 10^61<Q<106,∣x[<103|x[ \lt10^3∣x[<103 for I instruction, 1<k<n1 \lt k\lt n1<k<n. for Q instruction)

输出描述

For each Q instruction, output the desired value.

示例1

输入

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

输出

2
3

说明:

The following diagram shows the status of sequence after each instruction:


1 ≤ Q ≤ 10^6
∣x∣ ≤ 10^3
1 ≤ k ≤ n 

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 1e6 + 10;

int stk[N],p[N];
char str[2];
int l,r,stkl[N],stkr[N];

void push(int i)
{
    stkl[++ l] = i;
    stk[l] = stk[l - 1] + i;
    p[l] = max(p[l - 1],stk[l]);
}         

int main()
{
    int n,k;
    cin >> n;
    p[0] = INT_MIN;
    while(n --)
    {
    	cin >> str;
    	if(str[0] == 'I')
        {
            cin>>k;
            push(k);
        }
       if(str[0] == 'D')
        {
          if(l > 0)  l--;
        }
       if(str[0] == 'L')
        {
            if(l > 0)  stkr[++r] = stkl[l--];
        }
        if(str[0] == 'R')
        {
            if(r > 0)  push(stkr[r--]);
        }
        if(str[0] == 'Q')
        {
            cin >> k;
            cout << p[k] << endl;
        }
	}
    return 0;
}

题目5(单调栈)(中档题)

有一天,小猫 rainbow 和 freda 来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地。

这片土地被分成 N×M个格子,每个格子里写着 R 或者 FR 代表这块土地被赐予了 rainbow,F 代表这块土地被赐予了 freda。

现在 freda 要在这里卖萌。。。它要找一块矩形土地,要求这片土地都标着 F 并且面积最大。

但是 rainbow 和 freda 的 OI 水平都弱爆了,找不出这块土地,而蓝兔也想看 freda 卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为 S,它们将给你 3×S两银子。

输入格式

第一行包括两个整数 N,M,表示矩形土地有 N 行 M列。

接下来 N行,每行 M个用空格隔开的字符 FR,描述了矩形土地。

每行末尾没有多余空格。

输出格式

输出一个整数,表示你能得到多少银子,即(3×

最大 F 矩形土地面积)的值。

数据范围

1≤N,M≤1000

输入样例:

5 6
R F F F F F
F F F F F F
R R R F F F
F F F F F F
F F F F F F

输出样例:

45

思路:

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1010;

int s[N][N], l[N], r[N];    //h表示每个矩形高度,l和r分别表示矩形能向左右两侧扩展的边界
int q[N];                   //q储存单调栈
int n,m;

int work(int h[])           //传入直方图的高,这个函数就是131题的原代码
{
	int i;
    h[0] = h[m + 1] = -1;   //保证矩形左右两侧一定有小于它高度的矩形,即保证栈非空
                            //可省去判断条件tt >= 0
    //求左边界
    int tt = 0;             //tt表示栈顶
    q[0] = 0;
    for (i = 1; i <= m ; i ++ )
    {
        while (h[q[tt]] >= h[i]) tt -- ;    //往左找到第一个比h[i]小的位置为止
        l[i] = q[tt];                       //记录第一个比h[i]小的矩形的位置
        q[ ++ tt] = i;                      //添加当前矩形到栈中
    }

    //求右边界
    tt = 0;
    q[0] = m + 1;
    for (i = m; i ; i -- )
    {
        while (h[q[tt]] >= h[i]) tt -- ;
        r[i] = q[tt];
        q[ ++ tt] = i;
    }

    int res = 0;
    for (i = 1; i <= m; i ++ )
        res = max(res, h[i] * (r[i] - l[i] - 1));           //更新矩形最大值

    return res;
}

int main()
{
	int i;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
    {
    	
	    for (int j = 1; j <= m; j ++ )
        {
            char c;
            cin >> c;
            if (c == 'F')  s[i][j] = s[i - 1][j] + 1;        //累计F及其上方一共有几个F
        }
    } 
    int res = 0;
    for (i = 1; i <= n; i ++ )  res = max(res, work(s[i]));  //当下边界为i时,计算最大面积
    cout << res * 3 << endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

21RGHLY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值