题目链接:https://ac.nowcoder.com/acm/contest/882/H

题目大意:

给定一个N行M列的01矩阵,求面积次大的全由1组成的子矩阵面积
当矩阵中只存在数量小于两个这样的子矩阵时输出0

解题思路:

先分析特殊条件:当矩阵中只存在数量小于两个这样的子矩阵时输出0
根据题目定义,任何面积大于等于2的全1矩阵都能拆成至少两个全1矩阵
比如:1 1,就可以拆成 1,1,1 1这三个
所以当全1子矩阵数量小于2时,要么矩阵为零矩阵,要么矩阵中只有一个1,特判输出0即可。

其他一般情况:
我用的方法是在用单调栈的算法求最大全1子矩阵求次大,可以用一个优先队列来储存所有出现过的子矩阵大小,取次大即可。

单调栈思路来源于另一道单调栈经典题,可以先看一看那个题的解法,有助于本题思路理解
详见本人另一篇博客:https://blog.csdn.net/Izayoi_w/article/details/96561059

个人思路可能有些冗余:次大全1子矩阵要么是最大全1子矩阵的子矩阵,要么是其他分布情况的次大全1子矩阵,在这种方法下其实可以统一。

对于矩阵中每一个1,我们先预处理出其向下能延申多少个连续的1,这样我们再遍历整个矩阵,横向遍历其中一行时,我们就可以每一个点向下延申多少个1横向排列看成一个直方图(当然为0的点向下延申0个连续的1),然后用单调栈算法计算这个直方图中的最大子矩形(就像我上面贴的那个题一样)。这样计算完所有行直方图的最大子矩形,同时把他们存入优先队列,取次大即可。事实上优先队列中的元素数量可以只维护在两个就可以!

AC代码:

#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <vector>
#include <bitset>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const long long LINF = 1e14;
const double PI = acos(-1.0);
const int MAXN = 1e5+10;
char mat[1010][1010];
int sum[1010][1010], n, m, top = 0;
struct node
{
    int area, row, col;
    node(int a = 0, int r = 0, int c = 0)
    {
        area = a; row = r; col = c;
    }
    bool operator < (const node& n)const
    {
        return area < n.area;
    }
};
struct nd
{
    int w, h;
}S[1010];
priority_queue q;
int main()
{
    scanf("%d%d", &n, &m);
    int one = 0;
    for(int i = 1; i <= n; i++)
    {
        char ch[1010];
        scanf("%s", &ch);
        for(int j = 1; j <= m; j++)
        {
            mat[i][j] = ch[j-1];
            if(mat[i][j] == '1') one++;
        }
    }
    memset(sum, 0, sizeof sum);
    for(int i = n; i >= 1; i--)
        for(int j = 1; j <= m; j++)
            if(mat[i][j] == '1') sum[i][j] = sum[i+1][j] + 1; 
            //从mat[i][j]点算起向下有多少个连续的1
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++) //单调栈计算每一行直方图的最大子矩形
        {
            int d = 0;
            while(top && S[top].h > sum[i][j]) 
            {
                d += S[top].w;
                q.push(node(d*S[top].h, d, S[top].h)); //每个子矩形压入优先队列
                top--;
            }
            S[++top] = (nd){d+1, sum[i][j]};
        }
        int d = 0;
        while(top)
        {
            d += S[top].w;
            q.push(node(d*S[top].h, d, S[top].h));
            top--;
        }
    }
    if(one <= 1) puts("0");
    else
    {
        if(q.top().area == 1) puts("1");
        else
        {
            int a1 = q.top().area - min(q.top().row, q.top().col);
            if(q.size() == 1) printf("%d", a1);
            else
            {
                q.pop();
                int a2 = q.top().area;
                a1 = max(a1, a2);
                printf("%d\n", a1);
            }
        }
    }
    return 0;
}