以前一直有接触,但是一直没单独进行分析处理
单调栈:维护其中元素单调性的栈
也就是从栈底到栈顶都是有序的
维护:如果入栈的元素满足单调性,直接入栈;如果不满足,就让栈顶元素出栈,直到能让入栈元素满足单调性为止,再将元素入栈
(已经出栈的元素就被抛弃)
例题:
求直方图中包含的最大矩阵面积
题解链接
单调栈问题
我们可以用一个单调栈来维护每个矩阵的高度和宽度,
当前矩阵高于栈顶,进栈
当前矩阵a小于栈顶时,直到栈为空或者栈顶矩形的高度比当前矩形小。在出栈过程中,我们累计被弹出的矩形的宽度和。每弹出一个矩形,就用他的高度(他指被弹出的矩阵)乘上累计的宽度取更新答案,因为每次弹出的高度要比上次弹出的小,相当于依次求以从左往右以每个矩阵高的面积(如图所示)。整个出栈过程结束后,我们把一个高度为当前矩阵高度,宽度为累计值的新矩阵入栈。
全部扫描过后,我们要将栈内剩余矩阵依次弹出,并利用相同的方法更新答案。
最大全 1 矩阵
给一个01矩阵,问最大的全1矩阵是多少?
题解:
先预处理,求出每一行位置上连续的最大的1的矩阵的
高度是多少,再对每一行进行一个单调栈处理即可
代码:
STL的stack版
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;
}
手写stack版
for(int i=1;i<=n;i++){top=0;//每一行清空栈 for(int j=1;j<=m+1;j++){if(top==0||ri[i][j]>=ri[i][st[top]]){st[++top]=j;}else { int id; while(top!=0&&ri[i][j]<ri[i][st[top]]){id=st[top];top--;int w=min(j-id,ri[i][id]);ans=max(ans,w*w);ans2=max(ans2,(j-id)*ri[i][id]);}st[++top]=id;//将最后一次出栈的栈顶元素延申并入栈ri[i][id]=ri[i][j]; }}}
ACM-ICPC 2018 南京赛区网络预赛 B-The writing on the wal
给你一个n * m(1e5 * 100)的矩阵和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;
}