【算法】求解最大子矩阵的大小:给定一个整型矩阵,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。

求解最大子矩阵的大小
【题目】
给定一个整型矩阵,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。
例如:
1 1 1 0
其中,最大的矩形区域有3个1,所以返回3。再如:
1 0 1 1
1 1 1 1
1 1 1 0
其中,最大的矩形区域有6个1,所以返回6。

思路:

先计算以data[i]为底的矩阵大小,
例如:
1 0 1 1
1 1 1 1
1 1 1 0
以第一行为底,大小[1,0,1,1]
第二行为底,大小[2,1,2,2]
第三行为底,大小[3,2,3,0]
遇到0就置为0,故用 height[i] = data[i*cols+j]==0 ? 0 : height[i]+1; 如果遇到0,就置0,否则,高度+1,得到一个以data[i]为底的矩阵大小;
实际上可以看作一个直方图:例如:以第三行为底,可以得到
在这里插入图片描述
转化为在直方图的计算就是,以同一边作为底,当前最大能覆盖的矩阵面积:
注意
每一个计算得到的直方图,都需要进行计算一下最大矩阵,例如用例4:
在这里插入图片描述
显然,第二三四五行中,得到的height矩阵,都是[0,0,0,0,0]
最大矩阵在第一行,

接下来计算最大矩阵:
例如 h e i g h t [ ] = { 1 , 4 , 3 , 2 , 4 , 5 } height[] = \{1,4,3,2,4,5\} height[]={1,4,3,2,4,5}
定义栈stack,

  1. 当前栈为空,或者遇到一个比当前栈顶大的值,压栈(放入坐标i,而不是height[i])。这样一来,栈里的坐标标注的矩阵,就都是递增的矩阵了。
    在这里插入图片描述

例如这里的1,4,就可以直接入栈了,此时栈中是他们所在的坐标: [ 0 , 1 ] [0,1] [0,1]

  1. 遇到一个和当前栈顶一样大,或者比当前栈顶小的值:
    i f ( ! s t a c k . e m p t y ( )    & &     h e i g h t [ i ] < = h e i g h t [ s t a c k . t o p ( ) ] ) if(!stack.empty()\ \ \&\& \ \ \ height[i]<=height[stack.top()]) if(!stack.empty()  &&   height[i]<=height[stack.top()])
    说明矩阵大小开始收缩了,
    此时以当前遍历到的j = stack.top()为中心,向左右进行扩展
    在这里插入图片描述
    此时遇到了3,j = stack.top(),所以j=1
    可以计算高度为4的矩阵的最大大小了,
    这里,4向左,只能遍历到坐标为1的位置,也就是它自身
    4向右,只能遍历到左边为1的位置,也就是它自身
    所以4所在位置的最大矩阵大小只能是4;
    此时,栈中,j被弹出,再压入当前i,修改后的坐标为: [ 0 , 2 ] [0,2] [0,2]

  2. 可知若 h e i g h t [ i ] < h e i g h t [ s t a c k . t o p ( ) ] height[i]<height[stack.top()] height[i]<height[stack.top()], j向右,最多遍历到i-1 。因为如果可以遍历到i,那就可以直接继续压栈了,由于height[i]<=height[stack.top()] 而且j = stack.top(),所以最多能遍历到i-1

  3. 如果 h e i g h t [ i ] = = h e i g h t [ s t a c k . t o p ( ) ] height[i]==height[stack.top()] height[i]==height[stack.top()] 那么其实j向右,是可以压栈到i的,我们没有必要计算两次,因为i出栈的时候,也会计算一次向左的矩阵大小,显然这个矩阵是包含了j的。

  4. j位置向左,最多可以扩展到新的栈顶k+1的位置,因为当前栈里面的矩阵高度一定是递增的,显然,height[k]<height[j],所以此时可以得到,在j位置,最大矩阵面积是 ( i − k − 1 ) ∗ h e i g h t [ j ] (i-k-1)*height[j] (ik1)height[j]

  5. 注意,只有在出栈时,才会计算最大矩形的面积。
    index = 3 时,height[3] = 2,此时stack中有 [ 0 , 2 ] [0,2] [0,2],可知,以index=2的矩阵,向左有最大矩阵S = ((i-k-1)*height[j]) = 6
    在这里插入图片描述

  6. 当i已经到了height矩阵的最后一个元素,可以令k=-1,i=height.length,继续计算出栈j的值,直到栈中为空为止。

c++

#include <iostream>
#include <stack>
using namespace std;
/**
**  获取以height为底的矩形的最大面积
**/
int GetMaxArea(int* height, int length) {
	stack<int> s;
	int j = 0, k = 0;
	int maxArea = 0;
	
	int i = 0;
	while (i<length || !s.empty())
	{
		while (!s.empty() && height[i] <= height[s.top()])
		{
			j = s.top();
			s.pop();
			if (s.empty())
			{
				k = -1;
			}
			else {
				k = s.top();
			}
			maxArea = max(maxArea, (i - k - 1) * height[j]);
		}
		if (i<length)
		{
			s.push(i);
			i++;
		}
	}
		
	return maxArea;
}

/**
*  获取最大子矩阵的面积大小
**/
int GetMaxSubMatrixArea(int* data,int rows,int cols) {
	if (data==nullptr||rows<=0||cols<=0)
	{
		return 0;
	}
	int* height = new int[cols];
	for (int i = 0; i < cols; i++)
	{
		height[i] = 0;
	}
	int max = 0,area = 0;
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			height[j] = data[i * cols + j] == 0? 0 :height[j] + 1  ;
		}
		area = GetMaxArea(height, cols);
		if (max<area)
		{
			max = area;
		}
	}
	return max;
}


void GetMaxSubMatrixArea_Test() {

	int data[] = { 1,0,1,1,
				   1,1,1,1,
				   1,1,1,0 };
	// 6 
	cout << GetMaxSubMatrixArea(data, 3, 4) << endl;


	int data2[] = { 1,0,1,1,
					0,1,1,0,
					1,1,1,1,
					0,1,1,1,
					1,1,1,1 };
	// 9 
	cout << GetMaxSubMatrixArea(data2, 5, 4) << endl;


	int data3[] = { 1,0,0,1,0,
					0,1,1,0,1,
					1,1,1,1,1,
					1,1,1,1,0,
					0,1,1,0,0 };
	// 8
	cout << GetMaxSubMatrixArea(data3, 5, 5) << endl;


	int data4[] = { 0,1,1,1,1,
					0,0,0,0,0,
					0,0,0,0,0,
					0,0,0,0,0,
					0,0,0,0,0 };
	// 4
	cout << GetMaxSubMatrixArea(data4, 5, 5) << endl;


	int data5[] = { 0,1,1,1,1,
					0,0,1,0,0,
					0,0,1,0,0,
					0,0,1,0,0,
					0,0,1,0,0 };
	// 5
	cout << GetMaxSubMatrixArea(data5, 5, 5) << endl;

	int* data6 = nullptr;
	// 0 异常输入
	cout << GetMaxSubMatrixArea(data6, 0, 0) << endl;
}

int main()
{
	GetMaxSubMatrixArea_Test();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值