栈学习笔记

\(\Huge 栈\)

\(\huge认识一下\)

一种 线性 数据结构 , 栈的修改是按照 后进先出 的原则进行的, 因此又称 后进先出 (last in first out) 表 , 简称 LIFO[^释义]表


\(\huge 基操\)

我们可以用 数组 方便の模拟一个栈 ↓

  • int stk[N]; 使用 cnt 代表栈中元素 , 同时也是栈顶下标
  • stk[++cnt] = x; 压栈
  • int u = stk[cnt]; 取栈顶
  • if(cnt) --cnt; 弹栈 , 注意越界问题 , cnt == 0 时不能继续弹出
  • cnt = 0; 清空栈

**だが ** 但是.. STL是个好东西

#include <stack> //头文件
stack<typename> s; //定义
s.size(); //返回栈中元素个数
s.empty(); //栈为空则返回1, 否则返回0
s.pop(); //删除栈顶元素但不返回值
s.top(); //返回栈顶元素但不删除
s.push(x); //在栈顶压入新元素x
//支持赋值运算符 =

//顺手把队列的也放这里好了
#include <queue>
queue<typename> q;
q.size(); //队列元素个数
q.empty(); //队列为空返回true, 否则返回false
q.pop(); //删除队首元素但不返回值
q.front(); //返回队首元素但不删除
q.push(); //在队尾压入新元素
q.back(); //返回队尾元素但不删除

具体实现戳这 队列


\(\Huge 单调栈\)

\(\huge 认识一下\)

满足 单调性 的栈结构。与单调队列相比,其只在一端进行进出。

单调递增栈:栈中数据 出栈 的序列为单调递增序列

单调递减栈:栈中数据 出栈 的序列为单调递减序列


\(\huge 使用\)

\(\LARGE 插入\)

为了维护单调性 , 在插入一个元素时要弹出一些元素 , 并且弹出的元素保证最少

栗如 → \(1,3,5,7,9\) , (1为栈顶 , 递增栈) , 现要插入 \(6\) , 弹出 \(1,3,5\) , 此时栈内元素为 \(6,7,9\)

伪代码 ↓

insert x
while (!sta.empty() && sta.top() < x) 
    sta.pop();
sta.push(x);

一个非常非常非常基础的栗题→我是蒻题!!


\(\huge 一些栗题\)

怎么可能只考栈呢,当然要和其他的奇怪的东西结合起来啊(大雾

我找的都是用栈就能做的,当然也可以用别的比如dp...

T116755 WJQ的矩形

一道小水题 单调栈维护最大矩形面积,(棋盘制作的子问题)

提示:十年OI一场空,不开龙龙见祖宗

#include <bits/stdc++.h>
#define ll long long
using namespace std;

stack<ll> s;
ll n, ans, o[100009];

int main(){
	std::ios::sync_with_stdio(0);
	cin >> n;
	s.push(0);
	for(int i = 1; i <= n; i++) cin >> o[i];
	for(int i = 1; i <= n; i++) {
		while(s.size() > 1 && o[i] < o[s.top()]) {
			int oo = s.top();
			s.pop();
			ans = max(ans, (ll)(o[oo] * (i - 1 - s.top()))); 
		}
		s.push(i);
	}
	while(s.size() > 1) {
		int oo = s.top();
		s.pop();
		ans = max(ans, (ll)(o[oo] * (n - s.top())));
	}
	cout << ans;
	return 0;
} 

P1169 [ZJOI2007]棋盘制作

题目大意 : 给出 \(N*M\) 的 01 矩阵,分别找到一个 0 和 1 交错的矩形和一个正方形

我们考虑枚举每一行,再枚举当前行的每个点,h数组表示当前点能向上延伸的最大长度(黑白相间),对于和前一个点不同颜色的点,进栈(维护一个栈的内部单增的栈),维护ans的最大值,如果遇到和前一个点颜色相同,则把栈都弹出,维护ans,顺手求一下正方形的面积就行了

洛谷的样例太水了,这里给出一个同样水的样例 ↓

6 6 
1 0 0 1 0 1 
0 1 1 0 0 1 
0 0 0 1 0 0 
1 1 1 0 1 0 
0 1 0 1 0 0
1 0 1 1 0 1  //输入
9 
10 //输出

喜欢用STL,但自从考试有次T了之后就改用数组模拟了qwq,就不对本篇的STL做更改了

#include <bits/stdc++.h>
using namespace std;

int n, m, F, J, x, y, h[2004];
bool o[2004][2004];
stack<int> s;

int main() {
    std::ios::sync_with_stdio(false);
    cin >> n >> m; 
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> o[i][j]; 
    for(int i = 1; i <= n; i++){ //枚举行
        for(int j = 1; j <= m; j++){ //h数组的维护工作
            if(i > 1 && o[i][j] != o[i - 1][j]) h[j] ++;
            else h[j] = 1;
        } 
        int j = 1;  //j为当前点下标
        while(j <= m){ //边界
        	s.push(j - 1);
			s.push(j++); //丢进第一个点
            while(j <= m && o[i][j] != o[i][j - 1]){ //若不超边且当前点和前一个不同
                while(s.size() > 1 && h[j] < h[s.top()]){ //栈保留压底的0,当前点比栈顶小
                    x = h[s.top()]; //栈顶的高(矩形的长)
                    s.pop(); //栈顶弹出
                    y = j - 1 - s.top(); //矩形的宽
                    J = max(J, x * y); //矩形
                    F = max(F, min(x, y) * min(x, y)); //顺手来个正方形
                }
                s.push(j++); //当前点丢入
            }
            while(s.size() > 1){ //全都弹出 
                x = h[s.top()];
                s.pop();
                y = j - s.top() - 1; 
                J = max(J, x * y); 
                F = max(F, min(x, y) * min(x, y));
            }
            s.pop();
        }
    }
    cout << F << endl << J;
    return 0;
}

看到这如果都懂了是不是觉得栈真(?)(?)是个好东西 \(\small嗝\) ~

P4147 玉蟾宫 双倍经验

这个题改改上面那个代码的条件就行了

P3551 [POI2013]USU-Take-out【征集 spj】(思维好题)

题目大意 : 一排方块,白块个数是黑块的 \(k\) 倍,现要消除此序列,每次消除 \(k+1\) 块砖,其中 \(k\) 块白色 (biały) 1 块黑色 (czarny),消除的块之间不能有已经消除的块(看不明白就走一边样例

从前到后用栈扫一遍就可以了,逆向想,在 \(k+1\) 的范围里出现一个黑块就可以消去了,记录在一个数组里,最后倒序输出就可以了

#include<bits/stdc++.h>
using namespace std;

int n, k, top, c, cx[1000009], s[1000009], ans[1000009];    
char o[1000009];

int main() {
    cin >> n >> k >> o + 1;
    for(int i = 1; i <= n; i++){
        s[++top] = i; 
        cx[top] = cx[top - 1] + (o[i] == 'c');
        if(top >= (k + 1) && cx[top] - cx[top - k - 1] == 1)
             for(int j =top - k - 1; j < top; top--) ans[++c] = s[top];
    }
    for(int i = n; i; i--) {
        cout << ans[i] << " ";
        if(i % (k + 1) == 1) cout << "\n";
    }
    return 0;
}

end.

posted @ 2020-07-14 18:31  hulne  阅读(214)  评论(1编辑  收藏  举报