悬线法 洛谷 P1169 [ZJOI2007]棋盘制作

it2022-05-05  119

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); }

 


最新回复(0)