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