2019牛客多校第八场题解(A)

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
#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;
        }
    }
    stacks;
    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;
}

 

你可能感兴趣的:(个人,暑假集训)