题意:给定矩阵,有一些坏点,要求最大的内部无坏点的矩阵
思路
写得时候没想到
遍历
明确要做遍历,发现对数据范围 或者说 可以过
如果暴力搜,那就得 想办法优化,首先是前缀和优化高度
如果分开上下两个自由度遍历来求每列中连通的矩阵有多长的话,那就得(上下不固定,从左到右遍历)
可以用前缀和,逐步往下数,数到坏点就重新数,可以优化掉一维
考虑到,经典题型——直方图求最大面积
可以固定下边界(或者说遍历下边界),此时从当前下边界到上边界的距离可以用上述的前缀和确定,也就是确定了两边,接下来找左右
要怎么找才是最大矩阵呢?首先是矩阵,下边界确定了,那就得上边界高度一致才可以,或者两边高度比当前列高才可以拓展为矩阵,那就涉及到找到数组中左边比自己小的数 与 右边比自己小的数
用单调栈,从起点遍历,设定边界值(最好设定到不需要考虑边界问题),先将边界值入栈,然后数组开始遍历,例如找左边比自己小的数,当前栈顶不是该数索引就出栈,直到找到了,将当前索引入栈
这样是单调查找的,被弹掉的数绝对不会是下一次要找的第一个满足条件的数,例如找左边比自己小的数,弹掉了一些数,说明那些数比自己大,如果下一个数找左边第一个小数,则一定会先找到自己,弹掉的数不影响
实现
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 3010;
int n, m, p;
int g[N][N];
int h[N][N], l[N], r[N];
//枚举下边界后剩下对直方图的面积求极值
int get_max(int h[])
{
//预处理边界避免麻烦的讨论,这样就无论如何找得到小值
h[0] = h[m + 1] = -1;
int stack[N] = {};
int top = 0;
stack[ ++ top] = 0;
//找左边
for(int i = 1; i <= m; i ++)
{
//最左边已经被赋值-1
while(h[stack[top]] >= h[i]) top --;
l[i] = stack[top];
//弹掉的都比当前大,当前值入栈,如果被弹掉,之前比当前大的数在下一个数的查找中也没有用处
stack[++ top] = i;
}
//找右边,先入右边边界
top = 0;
//用++ top 是为了区别开 0 和 m+1
stack[++ top] = m + 1;
for(int i = m; i > 0; i --)
{
while(h[stack[top]] >= h[i]) top --;
r[i] = stack[top];
stack[++ top] = i;
}
int res = 0;
for(int i = 1; i <= m; i ++)
{
res = max(res, h[i] * (r[i] - l[i] - 1));
}
return res;
}
int main()
{
cin >> n >> m >> p;
//memset(g, 0, sizeof g);
for(int i = 0; i < p; i ++)
{
int a, b;
cin >> a >> b;
g[a][b] = 1;
}
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
{
//预处理每行高度,是坏掉的就重新数
if(!g[i][j])
h[i][j] = h[i - 1][j] + 1;
}
int ans = 0;
//遍历下边界
for(int i = 1; i <= n; i ++)
{
int max_now = get_max(h[i]);
ans = max(ans, max_now);
}
cout << ans << endl;
return 0;
}