A
给你一个n*m的01矩阵,求出所有最大全1矩阵的数量,保证任意两个矩阵不相互包含。
考虑单调栈求最大矩形面积的做法。
对于矩阵
0 0 0 0
0 0 1 0
0 1 1 0
1 1 1 1
0 0 0 0
我们先预处理h[i][j] 为以该行为底的最大高度。那么我们只要知道这个矩形最左边的能够到达的位置L
由于每次我们是从左到右遍历数据的 所以我们可以确定右边界R ,并且我们预处理最大高度h[i][j]
那么我只要确定下面是否有延伸就可以确定这个矩阵是不是最大的。
也就是说 (R-L+1)!=下一行的相同位置的[L,R]的和。说明这个矩阵是最大的那么我们累计答案。
建立单调栈的时候如果h[i][j] 大于 s.top() 那么我们就把 高度为h[i][j],左边界为j 的元素加入单调栈。
如果h[i][j]<=s.top() 那么就判断h[i][j]是否小于s.top() 如果是 就取出栈顶元素。得到左边界Li和高度Hi右边界为
R=j-1 然后我们判断这个矩阵是不是最大矩阵 如果是则更新答案。然后把栈顶出栈。
直到栈为空 或者栈顶元素等于 h[i][j] ,这时候 我们取出栈顶得到栈顶元素的左边界Li然后,然后把 左边界为Li,高度为
h[i][j]的新元素加入栈。(相当于加入了一个左边界为Li 高度为Hi 的新矩阵) 每次我们每行的单调栈 结尾加入一个高度为0的元素
h[i][m+1] 这样 保证栈被清空 并且栈里面的最后一个最大矩形也被计算。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,x;
int pre[3010][3010];
int h[3010][3010];
struct node
{
int Li,Hi;
};
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%1d",&x);
if(x)
{
h[i][j]=h[i-1][j]+x;
}
else h[i][j]=x;
pre[i][j]=pre[i][j-1]+x;
}
}
stack<node>s;
ll ans=0;
int l,r;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m+1;j++)
{
l=j;
while(!s.empty()&&s.top().Hi>=h[i][j])
{
l=s.top().Li;
r=j-1;
if(s.top().Hi>h[i][j]&&pre[i+1][r]-pre[i+1][l-1]!=r-l+1)
{
ans++;
}
s.pop();
}
if(!h[i][j])
{
while(!s.empty())
{
s.pop();
}
}
s.push(node{l,h[i][j]});
}
}
printf("%lld\n",ans);
return 0;
}