P1169 [ZJOI2007]棋盘制作
悬线法
悬线法是一种通用的用于求子矩阵的方法
先对于每个点处理出以该点为基点,
向右可以到最远的点,向左可以到的最远的点
则对于每条横轴,处理出若干条线,再对于各个横轴dp合并
更新表示该点纵向向上可延申长度
状态和状态方程
状态
为点(i,j)向左延申最远的点
为点(i,j)向右延申最远的点
为该点的线向上延申的最长距离
状态转移方程
题意
代码
#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int maxn = 2005;
class QIO { //快读模板
public:
char buf[1 << 21], * p1 = buf, * p2 = buf;
int getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
int read() {
int ret = 0, f = 0;
char ch = getc();
while (!isdigit(ch)) {
if (ch == '-')
f = 1;
ch = getc();
}
while (isdigit(ch)) {
ret = ret * 10 + ch - 48;
ch = getc();
}
return f ? -ret : ret;
}
char Buf[1 << 21], out[20];
int P, Size = -1;
void flush() {
fwrite(Buf, 1, Size + 1, stdout);
Size = -1;
}
void write(int x, char ch) {
if (Size > 1 << 20) flush();
if (x < 0) Buf[++Size] = 45, x = -x;
do {
out[++P] = x % 10 + 48;
} while (x /= 10);
do {
Buf[++Size] = out[P];
} while (--P);
Buf[++Size] = ch;
}
}io;
int Left[maxn][maxn], Right[maxn][maxn], up[maxn][maxn], a[maxn][maxn];
int main() {
int n, m;
n = io.read(); m = io.read();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
a[i][j] = io.read();
Left[i][j] = Right[i][j] = j; //初始化
up[i][j] = 1;
}
}
for (int i = 1; i <= n; i++) //预处理向左延申点
for (int j = 2; j <= m; j++)
if (a[i][j] != a[i][j - 1])
Left[i][j] = Left[i][j - 1];
for (int i = 1; i <= n; i++) //预处理向右延申点
for (int j = m - 1; j >= 1; j--)
if (a[i][j] != a[i][j + 1])
Right[i][j] = Right[i][j + 1];
int ans1 = 0, ans2 = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (i > 1 && a[i][j] != a[i - 1][j]) { //动态规划
Left[i][j] = max(Left[i][j], Left[i - 1][j]);
Right[i][j] = min(Right[i][j], Right[i - 1][j]);
up[i][j] = up[i - 1][j] + 1;
}
int r = Right[i][j] - Left[i][j] + 1;
int c = min(r, up[i][j]);
ans1 = max(ans1, c * c);
ans2 = max(ans2, r * up[i][j]);
}
}
printf("%d\n%d\n", ans1, ans2);
}