持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情。
题目描述
给定一个 N×MN×M 的 0101 矩阵,矩阵下标从 00 开始。
有 QQ 个询问,第 ii 个询问为:将矩阵中 (xi,yi)(xi,yi) 的元素改成 00 之后,只包含 11 的子矩阵的最大面积是多少。
注意:
- 每次询问均是独立的。
- 询问方格内元素可能本来就是 00。
- 子矩阵的面积是指矩阵的大小。
输入格式
第一行包含两个整数 N,MN,M。
接下来 NN 行,每行包含 MM 个 0101 字符。
再一行包含整数 QQ。
接下来 QQ 行,每行包含 22 个整数 (xi,yi)(xi,yi)。
输出格式
每个询问输出一行一个结果,表示最大面积。
数据范围
对于 20%20% 的数据,1≤N,M,Q≤101≤N,M,Q≤10 对于 50%50% 的数据,1≤N,M,Q≤1001≤N,M,Q≤100 对于 100%100% 的数据,1≤N,M≤2000,1≤Q≤1051≤N,M≤2000,1≤Q≤105, 0≤xi<n,0≤yi<m0≤xi<n,0≤yi<m
输入样例:
4 2
10
11
11
11
3
0 0
2 0
3 1
输出样例:
6
3
4
思路
本题是在Leetcode 0085的基础上进行拓展,每次我们会将一个格子变为0,此时矩形不能包含这个格子,此时我们可以依据这个格子将整个平面分成上下左右四个部分,计算出四个部分的面积格子对应的最大值,最后对这四个最大值求最大值就是该询问对应的结果
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2010;
int n, m; // 行数、列数
char g[N][N];
int l[N], r[N], q[N]; // 单调栈使用到的数组, q中存储的是下标
int s[N][N]; // 柱状图,可以递推得到
int U[N], D[N], L[N], R[N]; // 被挖掉的格子的上下左右部分对应的最大矩形的面积
// 对应 Acwing 131. 直方图中最大的矩形; 对应 Leetcode 0084 柱状图中的最大矩形
int calc(int h[], int n) {
h[0] = h[n + 1] = -1;
int tt = 0;
q[0] = 0;
for (int i = 1; i <= n; i++) {
while (h[q[tt]] >= h[i]) tt--;
l[i] = q[tt];
q[++tt] = i;
}
tt = 0;
q[0] = n + 1;
for (int i = n; i; i--) {
while (h[q[tt]] >= h[i]) tt--;
r[i] = q[tt];
q[++tt] = i;
}
int res = 0;
for (int i = 1; i <= n; i++)
res = max(res, h[i] * (r[i] - l[i] - 1));
return res;
}
void init() {
// 计算从第i行向上对应的柱状图中的最大面积
// s[i]表示从i行向上的柱状图
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
if (g[i][j] == '1') s[i][j] = s[i - 1][j] + 1;
else s[i][j] = 0;
U[i] = max(U[i - 1], calc(s[i], m));
}
// 计算从第i行向下对应的柱状图中的最大面积
// s[i]表示从i行向下的柱状图
memset(s, 0, sizeof s);
for (int i = n; i; i--) {
for (int j = 1; j <= m; j++)
if (g[i][j] == '1') s[i][j] = s[i + 1][j] + 1;
else g[i][j] = 0;
D[i] = max(D[i + 1], calc(s[i], m));
}
// 计算从第i列向左对应的柱状图中的最大面积
// s[i]表示从i列向左的柱状图
memset(s, 0, sizeof s);
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++)
if (g[j][i] == '1') s[i][j] = s[i - 1][j] + 1; // 从第i-1列推第i列
else s[i][j] = 0;
L[i] = max(L[i - 1], calc(s[i], n));
}
// 计算从第i列向左对应的柱状图中的最大面积
// s[i]表示从i列向左的柱状图
memset(s, 0, sizeof s);
for (int i = m; i; i--) {
for (int j = 1; j <= n; j++)
if (g[j][i] == '1') s[i][j] = s[i + 1][j] + 1;
else s[i][j] = 0;
R[i] = max(R[i + 1], calc(s[i], n));
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%s", g[i] + 1);
init(); // 初始化计算U、D、L、R
int Q;
scanf("%d", &Q);
while (Q--) {
int x, y;
scanf("%d%d", &x, &y);
x++, y++;
printf("%d\n", max(max(U[x - 1], D[x + 1]), max(L[y - 1], R[y + 1])));
}
return 0;
}
注意:
时空复杂度分析
- 时间复杂度:O(n×m)O(n×m),n、m为行数、列数。
- 空间复杂度:O(n×m)O(n×m)。