(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...
一道小水题 单调栈维护最大矩形面积,(棋盘制作的子问题)
提示:十年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;
}
题目大意 : 给出 (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 << "
";
}
return 0;
}
end.