题目链接:HDU6957 Maximal submatrix
题目描述:
给定一个 n ∗ m n*m n∗m 的矩阵,求面积最大的子矩阵,对于该矩阵,满足每列元素是个非递减序列。
输入数据格式为,第一行输入测试样例组数 T T T。每组数据首先输入两个整数 n n n 和 m m m,表示矩阵的大小。接下来 n n n 行,每行输入 m m m 个整数 v i j v_{ij} vij,表示第 i i i 行 第 j j j 列的元素。其中, 1 ≤ n , m ≤ 2 ∗ 1 0 3 1\le n,m \le 2*10^3 1≤n,m≤2∗103, 1 ≤ v i j ≤ 5 ∗ 1 0 3 1\le v_{ij} \le 5*10^3 1≤vij≤5∗103。
输出数据格式为,对于每组测数据,输出一行,该行仅有一个整数,表示最大矩阵的面积。
题目解析:
首先将原矩阵转化为 01 矩阵,当i > 1 && a[i][j] >= a[i-1][j]
时,b[i][j] = 1
。然后用悬线法求这个最大的满足条件的 01 矩阵的面积。
定义对于当前a[i][j]
,up[i][j]
表示所满足条件的最大上界,down[i][j]
表示满足条件的最大下界,len[i][j]
表示矩阵能取到的最大长度,即连续的列数。定义状态转移方程:
{
u
p
[
i
]
[
j
]
=
u
p
[
i
−
1
]
[
j
]
,
if
b
[
i
]
[
j
]
is true
d
o
w
n
[
i
]
[
j
]
=
d
o
w
n
[
i
+
1
]
[
j
]
,
if
b
[
i
+
1
]
[
j
]
is true
l
e
n
[
i
]
[
j
]
=
l
e
n
[
i
]
[
j
−
1
]
+
1
,
u
p
[
i
]
[
j
]
=
m
a
x
(
u
p
[
i
]
[
j
−
1
]
,
u
p
[
i
]
[
j
]
)
,
d
o
w
n
[
i
]
[
j
]
=
m
a
x
(
d
o
w
n
[
i
]
[
j
−
1
]
,
d
o
w
n
[
i
]
[
j
]
)
if b[i][j-1] is true
\begin{cases} up[i][j] = up[i-1][j], & \text {if $b[i][j]$ is true} \\ down[i][j] = down[i+1][j], & \text{if $b[i+1][j]$ is true} \\len[i][j] = len[i][j-1] + 1,up[i][j] = max(up[i][j-1], up[i][j]),down[i][j] = max(down[i][j-1], down[i][j])& \text{if b[i][j-1] is true} \end{cases}
⎩⎪⎨⎪⎧up[i][j]=up[i−1][j],down[i][j]=down[i+1][j],len[i][j]=len[i][j−1]+1,up[i][j]=max(up[i][j−1],up[i][j]),down[i][j]=max(down[i][j−1],down[i][j])if b[i][j] is trueif b[i+1][j] is trueif b[i][j-1] is true
如果当前b[i][j] = 1
,说明up[i-1][j]
一定是可以取到的一个上界,下界转移同理。然后处理矩阵能取到的最大长度,如果b[i][j-1] = 1
,说明b[i][j-1]
一定在这个矩阵里,然后再计算前 j 列区间重叠的部分,最后 ans = max(ans, len[i][j] * (down[i][j] - up[i][j] + 1))
。另外,这题用二维int数组会爆空间,用short优化即可。
参考代码:
#include <iostream>
#include <cstring>
#define FAST ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define endl '\n'
#define debug(x) cout << #x << " = " << (x) << endl
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
using namespace std;
const int maxn = 2e3+5;
int n, m;
short a[maxn][maxn];
bool b[maxn][maxn];
short up[maxn][maxn], down[maxn][maxn], len[maxn][maxn];
void tran() {
mem(b, 0);
rep(i, 1, n) {
rep(j, 1, m) {
if (i == 1) continue;
else if (a[i][j] >= a[i-1][j]) b[i][j] = 1;
}
}
}
void init() {
rep(i, 1, n) {
rep(j, 1, m) {
up[i][j] = i;
down[i][j] = i;
len[i][j] = 1;
}
}
}
int main()
{
FAST;
int t; cin >> t;
while (t--) {
cin >> n >> m;
rep(i, 1, n) {
rep(j, 1, m) {
cin >> a[i][j];
}
}
tran();
init();
rep(j, 1, m) {
rep(i, 2, n) {
if (b[i][j]) up[i][j] = up[i-1][j];
}
per(i, n-1, 1) {
if (b[i+1][j]) down[i][j] = down[i+1][j];
}
}
int ans = m;
rep(j, 1, m) {
rep(i, 1, n) {
if (b[i][j-1]) {
len[i][j] = len[i][j-1] + 1;
up[i][j] = max(up[i][j], up[i][j-1]);
down[i][j] = min(down[i][j], down[i][j-1]);
}
int llen = down[i][j] - up[i][j] + 1;
ans = max(ans, llen * len[i][j]);
}
}
cout << ans << endl;
}
return 0;
}
/*
1
5 5
1 1 5 5 5
2 2 2 2 2
1 3 3 3 3
1 4 4 4 4
1 5 5 5 5
1
2 4
2 3 4 5
1 4 4 5
1
2 5
2 2 2 2 2
2 1 1 1 1
1
10 1
1
2
3
3
4
8
9
10
2
3
5
*/