「题解」洛谷 P1169 [ZJOI2007]棋盘制作
题目
简化题意
找到给出的矩阵中最大的 \(0,1\) 交替的矩形和正方形。
思路
单调栈,悬线法。
对每个点求出最多向上扩展多少,就变成了直方图最大矩形问题,对于正方形只要宽度和当前高取个最小值就行。
Code
悬线法:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 2001
int min(int a, int b) { return a < b ? a : b; }
int max(int a, int b) { return a > b ? a : b; }
int n, m, map[M][M], up[M][M];
int maxj, maxz, l[M], r[M];
bool okayl(int c, int d) {
if (up[c][d] <= up[c][l[d] - 1] && map[c][l[d]] != map[c][l[d] - 1]) return true;
return false;
}
bool okayr(int c, int d) {
if (up[c][d] <= up[c][r[d] + 1] && map[c][r[d]] != map[c][r[d] + 1]) return true;
return false;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
scanf("%d", &map[i][j]);
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (map[i][j] == map[i - 1][j]) up[i][j] = 1;
else up[i][j] = up[i - 1][j] + 1;
l[j] = r[j] = j;
}
for (int j = 1; j <= m; ++j) {
while (l[j] > 1 && okayl(i, j)) l[j] = l[l[j] - 1];
}
for (int j = m; j >= 1; --j) {
while (r[j] < m && okayr(i, j)) r[j] = r[r[j] + 1];
}
for (int j = 1; j <= m; ++j) {
maxj = max(maxj, up[i][j] * (r[j] - l[j] + 1));
int len = min(up[i][j], r[j] - l[j] + 1);
maxz = max(maxz, len * len);
}
}
std::cout << maxz << '\n' << maxj << '\n';
return 0;
}
单调栈:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 2002
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }
int n, m, map[M][M], up[M][M];
int ansj, ansz, top, s[M], w[M];
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
scanf("%d", &map[i][j]);
if (map[i][j] == map[i - 1][j]) up[i][j] = 1;
else up[i][j] = up[i - 1][j] + 1;
}
}
for (int i = 1; i <= n; ++i) {
top = 0;
s[++top] = 1, w[top] = 1;
map[i][m + 1] = map[i][m];
for (int j = 2; j <= m + 1; ++j) {
if (up[i][s[top]] < up[i][j] && map[i][j] != map[i][s[top]]) {
s[++top] = j;
w[top] = 1;
}
else {
int len = 0;
if (map[i][j] == map[i][s[top]]) {
while (top) {
len += w[top];
int l = min(len, up[i][s[top]]);
ansj = max(ansj, len * up[i][s[top]]);
ansz = max(ansz, l * l);
--top;
}
s[++top] = j, w[top] = 1;
}
else {
while (top && up[i][s[top]] >= up[i][j]) {
len += w[top];
int l = min(len, up[i][s[top]]);
ansj = max(ansj, len * up[i][s[top]]);
ansz = max(ansz, l * l);
--top;
}
s[++top] = j, w[top] = len + 1;
}
}
}
}
std::cout << ansz << '\n' << ansj << '\n';
return 0;
}