1. 程式人生 > >郵箱:[email protected]

郵箱:[email protected]

題意

給出一個矩陣,求全1子矩陣的最大面積

解析

開局的處理方式和最大求和子矩陣類似,壓縮處理。

預處理h[i][j],表示第i行第j列往上(行數遞減方向)可以接上的全1串的最長長度,然後處理第一行到第i行的ans時,就可以看成處理h[i]一行了

eg:

n=3 m=4

 M陣列            H陣列
0 1 1 0         0 1 1 0
1 1 1 1   -->   1 2 2 1 
1 0 1 0         2 0 3 0 

接下來,對於每一行該怎麼處理?

最大面積一定是某一個點的高 * 往左右延大於等於這個高的長度,所以只要對於每個高,處理出其可以延伸的左端點和右端點,ans=max(ans,H*len)

,用單調棧可以在n的時間內得到所有高的len

單調棧

假設H陣列如下

這裡寫圖片描述

維護一個單調不減的棧,首先s[0]=0,h[1]=1>0,入棧s[1]=1(下標),h[2]>s[1],入棧s[2]=2,同理s[3]=3

這個時候,每個入棧的都比前面的大,即當前為最大,故當前高度的左端點就是前面那個的下標,有 L[1]=0,L[2]=1,L[3]=2

(若i可以往左延伸到i-j,L[i]==i-j-1,同理R[i]==i+j+1)

如果將要入棧的數比棧頂元素小,那麼說明兩點:

  1. 對於棧頂的那個下標,已經延伸不到當前點了,就可以確定下其右端點R了
  2. 對於入棧元素,棧頂下標的高度可以讓其延伸,那麼就使其出棧,直到延伸不到棧頂元素為止,就可以得到將要入棧下標的左端點L

eg:
h[4]=2,比s[3]小,所以s[3]出棧,出棧的時候,s[3]為3,即第3個數不能延伸到4了,所以R[3]=4,這個時候,下標為3高度為3的左右端點已經得出,ans=max(ans,3*(4-2-1))

至於為什麼相同的不出棧

其實出不出都可以,就是寫法上的區別

首先,同一段上的相同高度,只要有一個正確,就可以了
eg:h陣列 3 2 4 2 3 那麼len[2]=1~5,len[4]=1~5,其中就算有一個是變小了,也不會影響ans=2*5=10

假設不出棧,h[s[2]]==h[4]==2,本來這個點是可以延伸到的,但是我們不出棧,所以第二個數的L不準確了,但是第一個數的R準確了,那麼第一個數因為遇到不相同的出棧了,得到的就是準確的

如果出棧的話,前面那個點的R就不準確了,但是接下來的相同的數的L是準確的,只要遇到一個不相同的使任意一個出棧,那麼最後一個相同的數的R就是準確的了,因此得到的還是一段準確的區間

程式碼

#define N 2009

int n,m;
int M[N][N];
int h[N][N];
int ans;
int s[N],L[N],R[N];

void fin(int row){//相同不出棧
    s[0]=0;int top=0;
    h[row][m+1]=0;//用於得到最後沒出棧元素的R
    for(int i=1;i<=m+1;i++){
        int ar=s[top];
        while(h[row][i]<h[row][ar]){
            R[ar]=i;top--;
            ar=s[top];
        }
        L[i]=ar;s[++top]=i;
    }
    for(int i=1;i<=m;i++)
        if(h[row][i])
        ans=max(ans,h[row][i]*(R[i]-L[i]-1));
}

void fin_(int row){//相同出棧
    s[0]=0;int top=0;
    h[row][m+1]=0;
    for(int i=1;i<=m+1;i++){
        int ar=s[top];
        while(h[row][i]<=h[row][ar]){
            R[ar]=i;top--;
            if(top<0)break;//最後m+1的時候把s[0]也出棧了
            ar=s[top];
        }
        L[i]=ar;s[++top]=i;
    }
    for(int i=1;i<=m;i++)
        if(h[row][i])
        ans=max(ans,h[row][i]*(R[i]-L[i]-1));
}

int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        mmm(h,0);ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&M[i][j]);
                if(M[i][j])h[i][j]=h[i-1][j]+1;
            }
        }
        for(int i=1;i<=n;i++)fin(i);
        printf("%d\n",ans);
    }
}
/*
4 4
0 1 1 1
1 0 1 1
1 1 1 1
1 1 1 0
*/