NJU-高级算法-子矩阵问题

子矩阵问题

本文参考算法题:直方图和0-1矩阵中最大矩形

原作者飞翔的蓝鲸

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值