51nod 1158 全是1的最大子矩陣

2022-09-10 05:42:12 字數 4418 閱讀 2189

題目傳送門

題意:給出1個m*n的矩陣m1,裡面的元素只有0或1,找出m1的乙個子矩陣m2,m2中的元素只有1,並且m2的面積是最大的。輸出m2的面積。

這道題,是一道十分玄學的題目。

在題目上方有兩個標籤——「單調棧」和「dp」。但實際上,這道題根本就不需要單調棧或是dp。

因為我不會,所以我只會玄學

首先,我們隨便拿一組資料(樣例):

110

1110

11

我們把每乙個1上方的連續的1的數量寫一下:

110

2210

32

那麼好了,我們一行行來。首先看第一行,在第一行及其上方(其實也就是第一行)的最大全1矩形面積為2,第二行及其上方的最大全1矩陣面積為4,第三行及其上方的最大全1矩陣面積同樣為4。所以整個矩陣的最大全1矩陣面積為4。

這個面積的求法,對於該行每乙個連續的非0段,統計它的長度和最小值(也就是高度),求出面積,取所有段的最大值即可。

想到這裡的我,愉快的寫了乙份**,交了上去。

提交鏈結1

#

include

using

namespace std;

int a[

505]

[505];

intmain()

for(

int i=

1;i<=n;i++)if

(a[i]

[j])}}

ans=

max(ans,maxlen*maxhig);}

printf

("%d"

,ans)

;return0;

}

然後呢?

wa了6個點。

我就想,這道題真的只能用單調棧或是dp嗎?

於是,我下了一組資料。

000

1111

0110

0111

0011

0010

10

正解是6,也就是右上角長為2,高為3的矩形。但我的程式輸出了5。看到第三行,原來在計算第三行第三到五個數組成的段時,第三個數的值為1,所以,後面的高度就被鎖定成了1,而忽略了後面3的高度。

怎麼辦?

我的原程式是從左往右掃,於是我想到了乙個玄學的方法:

從左往右掃,再從右往左掃

想到這裡的我,愉快的寫了乙份**,交了上去。

提交鏈結2

#

include

using

namespace std;

int a[

505]

[505];

intmain()

for(

int i=

1;i<=n;i++)if

(a[i]

[j])}}

ans=

max(ans,maxlen*maxhig)

;//從右往左掃

nowlen=

0,maxlen=

0,nowhig=

1<<

30,maxhig=0;

for(

int j=n;j>=

1;j--)if

(a[i]

[j])}}

ans=

max(ans,maxlen*maxhig);}

printf

("%d"

,ans)

;return0;

}

然後呢?

wa了1個點:#7。

其實這個結果,在我交之前,早就已經想到了。我還知道它們會出什麼樣的資料來卡我這個程式。例如

011

1001

1100

1110

0111

0111

11

在最後一行,不管你是從左往右掃,還是從右往左掃,都會在第乙個數字鎖死高度,因為它是對稱的。

於是,我又想到了乙個更加玄學的方法:

先從上往下掃,再從下往上掃。

這樣就可以完美處理這個毒瘤資料。

想到這裡的我,愉快的寫了乙份**,交了上去。

提交鏈結3

#

include

using

namespace std;

int a[

505]

[505];

intmain()

for(

int i=

1;i<=n;i++)if

(a[i]

[j])}}

ans=

max(ans,maxlen*maxhig)

; nowlen=

0,maxlen=

0,nowhig=

1<<

30,maxhig=0;

for(

int j=n;j>=

1;j--)if

(a[i]

[j])}}

ans=

max(ans,maxlen*maxhig);}

//還原矩陣

for(

int i=

1;i<=n;i++

)for

(int j=

1;j<=m;j++)if

(a[i]

[j])

a[i]

[j]=1;

//矩陣翻轉

for(

int i=

1;i<=n/

2;i++

)for

(int j=

1;j<=m;j++

)swap

(a[i]

[j],a[n-i+1]

[j])

;//計算其上連續1個數

for(

int i=

1;i<=n;i++

)for

(int j=

1;j<=m;j++)if

(a[i]

[j])

a[i]

[j]=a[i-1]

[j]+1;

//反著掃

for(

int i=

1;i<=n;i++)if

(a[i]

[j])}}

ans=

max(ans,maxlen*maxhig)

; nowlen=

0,maxlen=

0,nowhig=

1<<

30,maxhig=0;

for(

int j=n;j>=

1;j--)if

(a[i]

[j])}}

ans=

max(ans,maxlen*maxhig);}

printf

("%d"

,ans)

;return0;

}

然後呢?

震驚!!!

這個114行(含注釋118行)的**成功的ac了!!!

要什麼單調棧呀,要什麼dp呀,上下左右各掃幾遍就行了。

對於這個演算法的時間複雜度,輸入需要 o(n

m)

o(nm)

o(nm

) ,從上往下掃需要 o(2

)o(2n²)

o(2n²)

,還原矩陣需要 o(n

m)

o(nm)

o(nm

) ,翻轉矩陣需要 o(n

m2

)o(\frac)

o(2nm​

) ,重新計算需要 o(n

m)

o(nm)

o(nm

),從下往上掃需要 o(2

)o(2n²)

o(2n²)

,由於n和m是同乙個數量級的,所以總時間複雜度為 o(n

m)

o(nm)

o(nm

),可能常數會比較大(因為上下左右都在掃),但對於500的資料來說,已經完全足夠了。

by 232323 in 51nod

參考文獻:

51nod 1158 全是1的最大子矩陣 (單調棧) 詳細**

勘誤:當你們看我的**時,是不是感覺有點不太對勁?

乙個n*m的矩陣,列舉每乙個點,i是從1到n,j又是從1到n。

自己改吧。(居然還ac了,不可思議)

既然都已經發現了勘誤1,那麼在時間複雜度的分析裡,從上往下掃和從下往上掃的時間複雜度都應該是 o(2

nm

)o(2nm)

o(2nm)

,對總時間複雜度無傷大雅,但畢竟還是要嚴謹的。

51Nod 1158 全是1的最大子矩陣

1158 全是1的最大子矩陣 基準時間限制 1 秒 空間限制 131072 kb 分值 80 難度 5級演算法題 給出1個m n的矩陣m1,裡面的元素只有0或1,找出m1的乙個子矩陣m2,m2中的元素只有1,並且m2的面積是最大的。輸出m2的面積。input 第1行 2個數m,n中間用空格分隔 2 ...

51nod 1158 全是1的最大子矩陣

題目鏈結在這兒 如果我的部落格費解,可以看這篇部落格,認為清晰。首先把子矩陣預處理,g i j 表示第 i j 號元素能向左延伸的長度。進而針對每一列,假設是c列,考慮g i c i 1,row 得到g i c 元素在這一列上作為作為最小值的長度。假設這個區間是 u,d 那麼長度為d u 1,那麼臨...

51nod 1158 全是1的最大子矩陣(單調棧)

思路 與這一題largest rectangle in a histogram很像,只不過本題需要做m次單調棧 因為有m行 先處理出每一行矩形的高度,然後以每一行為底求矩形的面積即可。include using namespace std const int n 1e5 7 int n,m,x,an...