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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章