以前一直有接触,但是一直没单独进行分析处理
单调栈:维护其中元素单调性的栈
也就是从栈底到栈顶都是有序的
维护:如果入栈的元素满足单调性,直接入栈;如果不满足,就让栈顶元素出栈,直到能让入栈元素满足单调性为止,再将元素入栈
(已经出栈的元素就被抛弃)
例题:
求直方图中包含的最大矩阵面积
题解链接
单调栈问题
我们可以用一个单调栈来维护每个矩阵的高度和宽度,
当前矩阵高于栈顶,进栈
当前矩阵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; }