题目链接: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; }