左神算法进阶class2—单调栈应用2:求最大子矩阵
1.题目:单调栈应用2:求最大子矩阵
给定一个整型矩阵map,其中的值只有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。
2.分析
本题同样可以用单调栈的思想解题,左神算法进阶class2—单调栈应用:直方图中矩形的最大面积 做为本题的引子,如果上一题不了解,本题可能会看不懂,因为本题是在上一题的基础上修改得来的。
在求直方图面积中,杆子的底侧都在同一水平线,而本题不一定从底侧开始,想一个极端情况,左上角的1,它可以单独作为一个杆子进行滑动。(0用鼠标画的真的好丑!)
也就是说每一行的1都可以作为杆子进行滑动,好了,我们首先可以考虑设置三条平行线,第一条只包含一行,第二条包含上两行,第三条基准线相当于直方图包含所有的值。本题是不是可以分解为把三个基准中所有的杆子进行扫略,最后求最大值。
如果到这里还没明白的朋友还是建议看下上一题哦。
好像是可行的!但是问题又来了,对于下图中间出现的0,相当于我们的杆子被打断了,该怎么解决呢。
我们可以设置一个长度为列的数量的辅助数组help,表示此位置向上有多少个连续的1。首先对于最上面的水平线,单独拎出来。如下图,简化为1,0,1,1。其中第一个1表示在当前0位置,向上有1个1。那么直方图就构成了,1,0,1,1求直方图的最大面积,这一步是可解的也是容易做的,完全是上一题的部分。
可能大家还比较疑惑,我们再来看第二条平行线。下图蓝线为基准线,辅助数组help更新为2,1,2,2,第一个2表示在当前列,向上有连续两个1,所以值为2。同样,辅助数组就变成直方图,计算2,1,2,2的最大矩形。可是,有朋友可能会问为什么辅助数组要表示此位置向上有多少个连续的1?直接是累加不行吗?别急,请再往下看。
现在我们开始计算第三条基准线啦,直接累加好像是3,2,3,2,咦,为什么最后一个是0?原来我们最初的设置基准线的目的就是从基准线开始滑动的杆子,如果是0的话,当然杆子就不存在了。
我们实际的杆子是从基准线开始的
是的,在上一题中,直方图都是从底部开始的,而本题每一个1都可以作为杆子进行滑动,所以我们设置三基准线,就囊括了所有的杆子可能的滑动,而不是只在底侧进行扫略。
下图是所有的杆子及对应的直方图
3.核心代码
现在我们回头看辅助数组到底是怎么设置的,help表示此位置向上有多少个连续的1,如果当前位置是0,上面的1就没有用了,因为杆子只从当前基准进行计算,如果不是0是1呢,那么辅助数组在当前位置就是上一行辅助数组值再加一help[j] = mp[i][j] == 0 ? 0 : help[j] + 1;
。因为上一行同一位置已经统计了,只需要加一即可。
我们对整个数组按行进行遍历,统计出每行的辅助数组,再调用计算同一水平线求直方图面积的函数,找到最大值就是本题的结果。
int maxRecSize(vector<vector<int> > mp)
{
if (mp.size() == 0 || mp[0].size() == 0) return 0;
vector<int> help(mp[0].size());
int maxArea = 0;
for (int i = 0; i < mp.size(); i++)
{
for (int<