介绍
给定一个矩阵有0,1构成,求矩阵中由1构成的子矩阵面积的最大值
思想
此题是单调栈结构(点击查看单调栈结构)的一个应用。
我们可以逐行来看,是否可以构成一个以当前行为底的一个直方图。即有当前行的每一列为底,向上有多少个1,即为直方图的高度。
经过上述步骤,我们得到了一个可以表示每个位置直方图高度的一维数组,之后我们需要求出当前构成的直方图可以形成的最大子矩阵的面积。
我们以直方图每个位置为高,判断他可以左右平移的最大位置,即是以当前位置可以构成的矩形面积的最大值,一个矩形是否可以左右移动的条件是需要移动的位置的矩形比当前位置的矩形的高度要高,因此,问题可以转换成在一个一维数组中,找到每个元素左右两边离他最近的元素的最小值。
这明显是一个单调栈的应用。
完整代码
#include <iostream>
#include <utility>
#include <stack>
#include <cstring>
using namespace std;
int maxRecFromBottom(int height[], int size){
int maxArea = 0;
stack<int> S;
for(int i = 0; i < size; i++){
while(!S.empty() && height[S.top()] >= height[i]){
int temp = S.top();
S.pop();
int L = S.empty() ? -1 : S.top();
int curArea = (i - L - 1) * height[temp];
maxArea = max(curArea, maxArea);
}
S.push(i);
}
while(!S.empty()){
int temp = S.top();
S.pop();
int L = S.empty() ? -1 : S.top();
int curArea = (size - L - 1) * height[temp];
maxArea = max(curArea, maxArea);
}
return maxArea;
}
int f(int **arr, int n, int m){
int maxArea = 0;
int height[m];
memset(height, 0, sizeof height);
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
//如果当前位置为1,可以构成直方图,高度+1
if(arr[i][j] == 1){
height[j] += 1;
}else{
//如果当前位置为0,不可以构成直方图,高度为0
height[j] = 0;
}
}
maxArea = max(maxArea, maxRecFromBottom(height, m));
}
return maxArea;
}
int main()
{
int n, m;
cin >> n >> m;
int **arr = new int *[n];
for(int i = 0; i < m; i++)
arr[i] = new int [m];
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
cin >> arr[i][j];
}
}
cout << f(arr, n, m) << endl;
return 0;
}