我们以最大全0矩阵为例。对于每个点,以这个点为起点往上引一条直线,直到矩形边界或遇到障碍为止,称之为这个点的悬线。悬线的长度记作 。
对于这个01矩形,答案矩形一定是由某条悬线向左右平移得到。
U很好维护,如果当前点可以从上面排(Mp[i-1][j]
)转移,那么
,否则
。
还有L和R。首先要保证当前排中的端点,即可以转移时有:
然后因为当前悬线 中有一部分是 ,所以需要考虑上面排:
例题:P1169 [ZJOI2007]棋盘制作
题意: 01矩阵,找出最大的正方形和矩形,满足01相见。
//滚动数组优化
#include<bits/stdc++.h>
using namespace std;
const int N = 2002;
int n, m, Mp[N][N];
int u[2][N], l[2][N], r[2][N];
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
scanf("%d", Mp[i] + j);
}
}
int ans1 = 0, ans2 = 0;
for(int i = 1; i <= n; i++) {
int now = i & 1, pre = !now;
l[now][1] = 1;
for(int j = 2; j <= m; j++) { //L
if(Mp[i][j] != Mp[i][j - 1])
l[now][j] = l[now][j - 1];
else
l[now][j] = j;
}
r[now][m] = m;
for(int j = m - 1; j >= 1; j--) { //R
if(Mp[i][j] != Mp[i][j + 1])
r[now][j] = r[now][j + 1];
else
r[now][j] = j;
}
for(int j = 1; j <= m; j++) { //如果可以更新
u[now][j] = 1;
if(i > 1 && Mp[i][j] != Mp[i - 1][j]) {
u[now][j] = u[pre][j] + 1;
l[now][j] = max(l[now][j], l[pre][j]);
r[now][j] = min(r[now][j], r[pre][j]);
}
int H = u[now][j], L = r[now][j] - l[now][j] + 1;
ans1 = max(ans1, min(H, L) * min(H, L));
ans2 = max(ans2, H * L);
}
}
printf("%d\n%d\n", ans1, ans2);
}