以前一直有接触,但是一直没单独进行分析处理
单调栈:维护其中元素单调性的栈
也就是从栈底到栈顶都是有序的
维护:如果入栈的元素满足单调性,直接入栈;如果不满足,就让栈顶元素出栈,直到能让入栈元素满足单调性为止,再将元素入栈
(已经出栈的元素就被抛弃)

例题:

求直方图中包含的最大矩阵面积

题解链接
在这里插入图片描述
单调栈问题
我们可以用一个单调栈来维护每个矩阵的高度和宽度,
当前矩阵高于栈顶,进栈
当前矩阵a小于栈顶时,直到栈为空或者栈顶矩形的高度比当前矩形小。在出栈过程中,我们累计被弹出的矩形的宽度和。每弹出一个矩形,就用他的高度(他指被弹出的矩阵)乘上累计的宽度取更新答案,因为每次弹出的高度要比上次弹出的小,相当于依次求以从左往右以每个矩阵高的面积(如图所示)。整个出栈过程结束后,我们把一个高度为当前矩阵高度,宽度为累计值的新矩阵入栈。
在这里插入图片描述

全部扫描过后,我们要将栈内剩余矩阵依次弹出,并利用相同的方法更新答案。

最大全 1 矩阵

给一个01矩阵,问最大的全1矩阵是多少?
题解:
先预处理,求出每一行位置上连续的最大的1的矩阵的
高度是多少,再对每一行进行一个单调栈处理即可

代码:

typedef pair<int, int>pa;

int n, m;
int a[2005][2005];
pa f[2005];
stack<pa>s;

int main() {

    while(~scanf("%d%d", &n, &m)){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                scanf("%d", &a[i][j]);
                if (a[i][j]) a[i][j] += a[i-1][j];//求连续的高度 
            }
        }

        int ans = 0;
        pa v;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                if (s.empty() && !a[i][j]) continue;

                if (s.empty() || s.top().second <= a[i][j])//当高度小于时 
                s.push(make_pair(j, a[i][j]));
                else {
                    while(!s.empty() && s.top().second > a[i][j]){
                        v = s.top();
                        s.pop();
                        int area = (j-v.first)*v.second;//求面积 
                        if (area > ans) ans = area;   
                    }
                    s.push(make_pair(v.first, a[i][j]));
                }

            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

ACM-ICPC 2018 南京赛区网络预赛 B-The writing on the wal

给你一个nm(1e5100)的矩阵和k(1e5)个黑点,黑点会给定坐标,问有多少个不含这些黑点的子矩形。
参考代码
这个讲的巨详细
题解:
相当于每次从一个矩阵的最右下角开始加一个一列的矩阵,加一个两列的矩阵,加一个三列的矩阵.......

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int b[100010][110], up[110];
int main(){
    int T, cas=0;
    scanf("%d", &T);
    while(T--){
        int n, m, K;
        scanf("%d%d%d", &n, &m, &K);
        for(int i=0; i<=n; i++){
            for(int j=0; j<=m; j++){
                b[i][j]=0;
                up[j]=0;
            }
        }
        for(int i=0; i<K; i++){
            int x, y;
            scanf("%d%d", &x, &y);
            b[x][y]=1;
        }
        ll ans=0;
        for(int i=1; i<=n; i++){
            for(int j=1; j<=m; j++){
                if(b[i][j]){
                    up[j]=i;
                }
            }
            for(int j=1; j<=m; j++){
                ll minn=0x7f7f7f7f7f7f7f7f;
                for(int k=j; k>0; k--){
                    minn=min(minn, (ll)(i-up[k]));
                    ans+=minn;
                }
            }
        }
        printf("Case #%d: %lld\n", ++cas, ans);
    }
    return 0;
}