子矩阵问题
原作者飞翔的蓝鲸
Description
给定一个矩形区域,每一个位置上都是1或0,求该矩阵中每一个位置上都是1的最大子矩形区域中的1的个数。
Input
输入第一行为测试用例个数。每一个用例有若干行,第一行为矩阵行数n和列数m,下面的n行每一行是用空格隔开的0或1。
Output
输出一个数值。
Sample Input 1
1
3 4
1 0 1 1
1 1 1 1
1 1 1 0
Sample Output 1
6
思路
思路看的论坛飞翔的蓝鲸大神的,原文非常清晰。
本题中可以将矩阵转化为记录从上往下将矩阵逐行作为底层往上算每个位置连续为1的个数
1 0 1 1 --- 1 0 1 1
1 1 1 1 --- 2 1 2 2
1 1 1 0 --- 3 2 3 0
这样可以将转化后的矩阵每一行看作一个直方图
例如,对于第二行:3 2 3 0
代码
#include <iostream>
#include <algorithm>
#include <stack>
using namespace std;
int a[99][99] = { 0 };
int main()
{
int t, n, m;
scanf("%d", &t);
for (int turn = 0; turn < t; turn++) {
scanf("%d%d", &n, &m);
fill(a[0], a[0] + n * m, 5);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &a[i][j]);
}
}
//从第二行开始将每一行中为1的元素改为从该位置往上连续1的个数
for (int i = 1; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] == 1 && a[i - 1][j] != 0) {//只向上算连续的1的个数
a[i][j] += a[i - 1][j];
}
}
}
int res = 0;//res为最终结果
//遍历矩阵每行算各行的符合条件的最大子矩阵,然后取各行结果maxTemp最大的为最终结果res
for (int i = 0; i < n; i++) {
stack<int> s;
int j = 1, maxTemp = a[i][0];
s.push(0);
while (j<m || j==m && !s.empty()) {
//未将矩阵当前行所有元素入栈且(栈为空或当前遍历元素>=栈顶元素)时,
//将当前元素(矩阵该行的元素下标,如a[i][j],就将j入栈)入栈
if (j != m && (s.empty() || a[i][j] >= a[i][s.top()])) {
s.push(j);
j++;
}
//否则记录下栈顶元素对应在矩阵中的值topNum,并弹出栈顶元素
//若当前栈不空,则当前弹出元素对应的最大子矩阵1的个数为topNum*(j-s.top()-1)
//若当前栈空了,则说明弹出的元素为最小元素,其对应最大子矩阵1个数就是topNum*j
//然后更新一下当前行的最大子矩阵1的个数maxTemp值
else {
int topNum = a[i][s.top()];
s.pop();
int currMax = !s.empty() ? topNum * (j - s.top() - 1) : topNum * j;
maxTemp = max(currMax, maxTemp);
}
}
res = max(maxTemp, res);
}
if (turn + 1 == t) {
printf("%d", res);
}
else {
printf("%d\n", res);
}
}
return 0;
}