题目链接:HDU6957 Maximal submatrix

题目描述:

给定一个 n ∗ m n*m nm 的矩阵,求面积最大的子矩阵,对于该矩阵,满足每列元素是个非递减序列。

​输入数据格式为,第一行输入测试样例组数 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 1n,m2103 1 ≤ v i j ≤ 5 ∗ 1 0 3 1\le v_{ij} \le 5*10^3 1vij5103

​输出数据格式为,对于每组测数据,输出一行,该行仅有一个整数,表示最大矩阵的面积。

题目解析:

​首先将原矩阵转化为 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[i1][j],down[i][j]=down[i+1][j],len[i][j]=len[i][j1]+1,up[i][j]=max(up[i][j1],up[i][j]),down[i][j]=max(down[i][j1],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 */