Issue:
Thinking:
- 题目要求最大子矩阵的面积,就可以把每一行看做是直方图中最大矩形问题,这题马氏风格,暴力枚举每一行
- 把每一行中非递减的“矩形”用h[][]数组存下来,这样每一行都是一个直方图中最大矩形问题
- 问题转化为直方图中最大矩形问题,依次枚举每一列,算出每一列能够向左和向右能够到达的最大长度分别用l[]和r[]数组记录,这一步就可以用单调栈来优化,时间复杂度为O(n)
- 每一行的公示就是(h[i] * (l[i] + r[i] - 1))
- 最后,对每一个结果取max
- 别忘了多组数据测试,所以每一次都要清空h[][]数组
Algorithm:
二维单调栈
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 2e3 + 10;
int a[N][N], h[N][N], q[N], l[N], r[N];
int tt;
int main()
{
int T;
scanf("%d", &T);
while (T -- )
{
int n, m;
scanf("%d%d", &n, &m);
memset(h, 0, sizeof h);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
{
scanf("%d", &a[i][j]);
if (a[i][j] >= a[i - 1][j]) h[i][j] = h[i - 1][j] + 1;
else h[i][j] = 1;
}
LL ans = 0;
for (int i = 1; i <= n; i ++ )
{
tt = -1;
q[ ++ tt ] = 0;
for (int j = 1; j <= m; j ++ )
{
while (h[i][q[tt]] >= h[i][j]) tt -- ;
l[j] = j - q[tt];
q[ ++ tt] = j;
}
tt = -1;
q[ ++ tt] = m + 1;
for (int j = m; j; j -- )
{
while (h[i][q[tt]] >= h[i][j]) tt -- ;
r[j] = q[tt] - j;
q[ ++ tt] = j;
}
for (int j = 1; j <= m; j ++ ) ans = max(ans, (LL)h[i][j] * (l[j] + r[j] - 1));
}
printf("%lld\n", ans);
}
return 0;
}
Tips:
- 这是完全马师傅风格的代码,感觉从一开始无从下手,到超时,到最后的Ac,中间改了几次,觉得深入学习算法的进步是可以看得到的
- 差点忘记了这题的特点,一来题目是要算面积,二来也是要找左右两边最小的数,就是典型的二维单调栈模型
- 现在自己的水平还是很low,从题目还读不懂,到看懂样例,到尝试多发WA,到看题解整理出自己的思路,到写出ac代码,到完成博客,一共要花4个小时,速度很慢,希望多次写博客后,速度能够有所提升
- 还是国豪哥对我说的:算法竞赛,贵在坚持!
- 最后紧跟一下时事:吴签到,亦值WA,凡死了!
|