題目傳送門
題意:給出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
n²
)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
n²
)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...