题目链接: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 */