包含 min 函数的栈

设计一个支持push,pop,top等操作并且可以在O(1)时间内检索出最小元素的堆栈。

  • push(x)–将元素x插入栈中
  • pop()–移除栈顶元素
  • top()–得到栈顶元素
  • getMin()–得到栈中最小元素

Sample

  1. MinStack minStack = new MinStack();
  2. minStack.push(-1);
  3. minStack.push(3);
  4. minStack.push(-4);
  5. minStack.getMin(); --> Returns -4.
  6. minStack.pop();
  7. minStack.top(); --> Returns 3.
  8. minStack.getMin(); --> Returns -1.

构造第二个栈,栈中第 i 个元素的值,是第一个栈中第 1∼i 个元素中的最小值。

  1. class MinStack {
  2. public:
  3. int st[10010];
  4. int top1;
  5. int st2[10010];
  6. int top2;
  7. int cur;
  8. /** initialize your data structure here. */
  9. MinStack() {
  10. top1 = 0;
  11. top2 = 0;
  12. cur = 1e9;
  13. st2[0] = 1e9;
  14. }
  15. void push(int x) {
  16. cur = min(cur,x);
  17. st[++top1] = x;
  18. st2[++top2] = cur;
  19. }
  20. void pop() {
  21. cur = st2[--top2];
  22. top1--;
  23. }
  24. int top() {
  25. return st[top1];
  26. }
  27. int getMin() {
  28. if(top2==0)return 0;
  29. return st2[top2];
  30. }
  31. };

编辑器

维护两个栈即可

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1000010;
  4. int q;
  5. int prest[N], sufst[N], t1, t2;
  6. int sum[N], maxsum[N];
  7. char op[2];
  8. int x;
  9. int main(){
  10. scanf("%d", &q);
  11. maxsum[0] = -1e9;
  12. while(q--){
  13. scanf("%s", op);
  14. switch (op[0])
  15. {
  16. case 'I':
  17. scanf("%d", &x);
  18. prest[++t1] = x;
  19. sum[t1] = sum[t1-1] + x;
  20. maxsum[t1] = max(sum[t1], maxsum[t1-1]);
  21. break;
  22. case 'D':
  23. if(t1 != 0) t1--;
  24. break;
  25. case 'L':
  26. if(t1 != 0) {
  27. sufst[++t2] = prest[t1--];
  28. }
  29. break;
  30. case 'R':
  31. if(t2 != 0) {
  32. prest[++t1] = sufst[t2--];
  33. sum[t1] = sum[t1-1] + prest[t1];
  34. maxsum[t1] = max(sum[t1], maxsum[t1-1]);
  35. }
  36. break;
  37. case 'Q':
  38. scanf("%d", &x);
  39. printf("%d\n", maxsum[x]);
  40. break;
  41. }
  42. }
  43. return 0;
  44. }

火车进栈

递归:

  1. 如果当前栈中有元素,则优先出栈(因为这样会使得字典序更小)
  2. 元素进栈
    1. #include <bits/stdc++.h>
    2. using namespace std;
    3. int n,cnt = 0;
    4. int st[50],tp;
    5. vector<int> res;
    6. void dfs(int tp,int x){
    7. if(cnt >= 20)return;
    8. if(x > n && tp == 0){
    9. cnt++;
    10. for(int i=0;i<res.size();i++)
    11. cout << res[i];
    12. puts("");
    13. return;
    14. }
    15. if(tp){ // 有元素,就尝试出栈
    16. int now = st[tp];
    17. res.push_back(now);
    18. dfs(tp-1,x);
    19. res.pop_back();
    20. st[tp] = now;
    21. }
    22. if(x <= n){
    23. st[tp+1] = x;
    24. dfs(tp+1,x+1);
    25. }
    26. }
    27. int main(){
    28. scanf("%d",&n);
    29. dfs(0,1);
    30. return 0;
    31. }

    火车进出栈问题

    Acwing 上题目数据范围较大,可在Luogu上找小范围数据的原题(可通过 0x11 栈 - 图1
    求长度为 n 的序列的出栈序列种数。
    方法一:首先递归枚举可以用 0x11 栈 - 图2的时间内解决该题。
    方法二:然后可以考虑使用「动态规划」的思想进行递推。
    设 s[n] 为长度为 n 的序列的出栈序列种数,考虑 1 这个数的出栈位置为 k,那么出栈序列可以分为前 k-1 个数和后 n-k 个数。这两部分是独立的,所以:0x11 栈 - 图3
    时间复杂度为 0x11 栈 - 图4
    方法三:另外一种DP状态可以定义为:f[i][j] 表示为有 i 个数还没有进栈,有 j 个数字还没有出栈的方案数。
    初始状态(可以直接指定答案的状态):f[0][0]=1
    目标状态:f[n][0]
    转移方程:f[i][j] = f[i-1][j+1] + f[i][j-1]
    时间复杂度仍然为 0x11 栈 - 图5
    方法四:可以直接用 Catalan 数计算:0x11 栈 - 图6。组合数学一节将介绍。

    直方图中最大的矩形

    image.png
    先思考暴力做法:对于每个矩形,把它作为右边界,然后枚举左边的矩形作为左边界,枚举方向为从右至左。然后可以计算出每种情况下的最大面积。
    image.png
    image.png
    image.png ```cpp

    include

    using namespace std; typedef long long ll; const int N=100010; int n, h[N], s[N], w[N], top; long long res; int main(){ while(~scanf(“%d”, &n)){ if(n == 0) break; top = 0; for(int i=1;i<=n;i++){ scanf(“%d”, &h[i]); } h[n+1] = top = res = 0; for(int i=1;i<=n+1;i++){ if(h[i] > s[top]) {
    1. s[++top] = h[i];
    2. w[top] = 1;
    } else {
    1. int width = 0;
    2. while(s[top] > h[i]) {
    3. width += w[top];
    4. res = max(res, 1ll * width * s[top]);
    5. top --;
    6. }
    7. s[++top] = h[i];
    8. w[top] = width + 1;
    } } cout << res << endl; } }

```