给定整形矩阵map,计算最大矩形区域为1的数量
如题:
思路:
此处需要用到单调栈
1.首先对每一行进行遍历,并构造以当前行作为底部,的一个直方图。
举个例子,假设数据如下:
3 4
1 0 1 1
1 0 1 0
1 1 1 1
那么先遍历第一行,得到的直方图就是 1 0 1 1, 然后我们计算最大的包含1的矩形区域
然后比那里第二行,这次以第二行作为底部,进行直方图的构建, 2 0 2 0 ,即从第2行往上,如果接壤,就继续+1,否则从0 开始
第三行就是 3 1 3 1
2.然后对于每一行所的得到的直方图,用单调栈进行计算这个包含最大的矩形面积。
因为某一行如果出现了不接壤,所以很可能会断掉,所以要对每一行的直方图进行计算。
如何计算呢?
可以利用单调栈,对每一个直方图的点histogram[i] 进行寻找
1).距离 histogram[i]最近的右边 且 histogram[i]的值 的 下标
2).距离 histogram[i]最近的左边 且 histogram[i]的值 的 下标
3).然后根据 histogram[i]的高度,进行面积的计算,最后取最大即可
代码:
#include<iostream>
#include<cstdlib>
#include<stack>
#include<cstring>
using namespace std;
int map_[1000][1000];
int histogram[1000];
//利用单调栈进行计算
int cal_max(int arr[],int len){
stack<int> stk;
int max_ = 0;
//对每一个值进行以单调栈的计算方式 栈顶至栈底为从大到小的形式
for(int j=0;j<len;j++){
// 如果碰到 当前遍历的值,大于栈顶,则直接压栈
// 反之 则把当前栈顶的值弹出来进行计算
while(!stk.empty() && arr[stk.top()]>=arr[j]){
int right_min = j; // 距离栈顶最近的右边 且 比栈顶小的值 的 下标
int high = arr[stk.top()]; stk.pop(); // 当前栈顶的 高度
int left_min = stk.empty()? -1 : stk.top(); //距离栈顶最近的左边 且 比栈顶小的值 的 下标
max_ = max(max_,high*(right_min - left_min-1));
}
stk.push(j);
}
//最后要处理未弹出完毕的值
//因为此时一定是栈顶至栈底是从大到小
//所以如果栈中还 存在值, 说明当前一定包含最后一个数
//所以右边 距离栈顶最近的右边 且 比栈顶小的值 的 下标 不存在
int right_min =len;
while(!stk.empty()){
int high = arr[stk.top()]; stk.pop();
int left_min = stk.empty()? -1 : stk.top();
max_ = max(max_,high*(right_min - left_min-1));
}
return max_;
}
/*
3 4 对应直方图
1 0 1 1 ==》 1 0 1 1
1 0 1 0 2 0 2 0
1 1 1 1 3 1 3 1
*/
int main(){
int M,N;// map 的 高 和宽
scanf("%d %d",&M,&N);
for(int i=0;i<M;i++){
for(int j=0;j<N;j++){
scanf("%d",&map_[i][j]);
}
}
//设计一个统计直方图
memset(histogram,0,N*sizeof(int));
int max_ = 0;
for(int i=0;i<M;i++){
for(int j=0;j<N;j++){
//从第i行为底 进行直方图的统计, 即如果不黏连,则为0,否则,加上之前的统计
histogram[j] = map_[i][j] == 0 ? 0:histogram[j]+1;
}
//对这个直方图进行计算 求最大值
int temp = cal_max(histogram,N);
max_ = max(max_,temp);
}
printf("%d\n",max_);
return 0;
}
3.注意
1.单调栈必须要确保序列不重复的情况,本题虽然存在重复情况,但是最后一个重复值依然能够计算出正确答案。
2.这里单调栈存的是下标,但是决策依然是已经直方图的“值”来进行决策的。