TIOJ-1063

給一$n\times m$矩陣,求最大的子矩陣面積滿足子矩陣內全部都是1

$n,m\leq 200$


這題最直覺的做法就是枚舉一個維度的左右界,可以轉化成最大連續和問題,時間複雜度$\mathcal{O}(n^2m)$

但可以透過建表來得知一格的高(該格以上有多長的連續1),比如範測:

1
2
3
4
0 0 1 1 0
0 1 1 1 1
0 1 1 1 1
0 0 1 0 0

可以蓋成:

1
2
3
4
0 0 1 1 0
0 1 2 2 1
0 2 3 3 2
0 0 4 0 0

這時就可以在單一維度求解了
例如第三列0 2 3 3 2
若取23則面積是$min(2,3)\times (3-2+1)=4$

取2
4則面積是$min(2,3,3,2)\times (4-2+1)=8$就是答案

處理這個可以用到直方圖求最大面積的手段 - 單調stack
維護一個單調遞增的stack,可以得知每個點所能觸及到(比該點大)的左右界
若這個點比前面的點小,則後面的點的左界肯定不會是前面的點,可以直接pop
pop時,因為該點比前面的點小才會pop,同時可維護被pop點的右界
最後就該點長度*左右界區間
枚舉維度$n$,每一點只會進出stack一次,時間複雜度$\mathcal{O}(nm)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
-------------- | /
| | /
| | /
| * |/ | | ------ *
| | | | / \
| | |\ | | | |\ |
\ | | | \ | | | | \ |
\ | | | \ | | \ / \ |
V | | \ \__/| ----- \ |
*/
#include <bits/stdc++.h>
using namespace std;

#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL);
/*-----------------------------------------------------------------------------------------------------*/

int arr[201][201], l[201], r[201];
signed main() {
EmiliaMyWife

int n, m, ans = 0;
cin >> n >> m;

for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
cin >> arr[i][j];

for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(arr[i][j])
arr[i][j] += arr[i-1][j];//蓋表
}

arr[i][0] = -INF;
stack<int> cur;
cur.push(0);

for(int j = 1; j <= m; j++) {
while(arr[i][cur.top()] >= arr[i][j])
r[cur.top()] = j, cur.pop();//維護stack
l[j] = cur.top();
cur.push(j);
}

while(cur.size())
r[cur.top()] = m+1, cur.pop();

for(int j = 1; j <= m; j++)
ans = max(ans, arr[i][j] * (r[j]-l[j]-1));//更新答案
}
cout << ans;

return 0;
}