题目描述
给定一个01矩阵,找出一个面积最大的子矩阵,使得其中的元素均为1。输出这个最大面积乘3。
输入
第一行两个整数N,M,表示矩形土地有N行M列。
接下来N行,每行M个用空格隔开的字符'F'或'R',描述了矩形土地。
输出
输出一个整数,表示你能得到多少银子,即(3*最大'F'矩形土地面积)的值。
样例输入
5 6
R F F F F F
F F F F F F
R R R F F F
F F F F F F
F F F F F F
样例输出
45
题解
单调栈
考虑最暴力的做法:枚举矩形左上右下的点,判断是否均为1。
好一点的暴力做法:枚举矩形下方的两个点(这个过程只需要枚举3个坐标),然后再找最大能够向上延伸多高。容易预处理出每个位置向上最多延伸多高,那么要求的就是这两个点直接所有点(包括这两个点)的延伸高度的最小值。
对于区间最小值作贡献问题,一个常用的套路是枚举最小值,考虑贡献。那么可以使用单调栈处理出一个数左边第一个小于它的位置和右边第一个小于它的位置。这两个位置之间的都大于它,此时这个数作为最小值,最大宽度为位置差,直接计算贡献即可。
时间复杂度$O(nm)$。
#include <cstdio> #include <cstring> #include <algorithm> #define N 1010 using namespace std; int a[N][N] , sta[N] , tot , lp[N] , rp[N]; char str[5]; int main() { int n , m , i , j , ans = 0; scanf("%d%d" , &n , &m); for(i = 1 ; i <= n ; i ++ ) { for(j = 1 ; j <= m ; j ++ ) { scanf("%s" , str); if(str[0] == 'F') a[i][j] = a[i - 1][j] + 1; } tot = 0 , sta[0] = 0; for(j = 1 ; j <= m ; j ++ ) { while(tot && a[i][j] <= a[i][sta[tot]]) tot -- ; lp[j] = sta[tot] , sta[++tot] = j; } tot = 0 , sta[tot] = m + 1; for(j = m ; j ; j -- ) { while(tot && a[i][j] <= a[i][sta[tot]]) tot -- ; rp[j] = sta[tot] , sta[++tot] = j; } for(j = 1 ; j <= m ; j ++ ) ans = max(ans , a[i][j] * (rp[j] - lp[j] - 1)); } printf("%d " , ans * 3); return 0; }