洛谷 1169 棋盘制作
解题思路:
这道题目主要是运用了一个叫做悬线法的方法来求极大矩阵的。
所谓悬线法:就是维护三个数组:up[ ][ ], left_[ ][ ], right_[ ][ ]. 分别表示点(i,j) 向上方,左方,右方走能走的最远距离。
维护方法运用了一些DP思维:
up[i][j] = up[i - 1][j] + 1; left_[i][j] = min(left_[i - 1][j], j - lz); //lz表示左边最近的障碍 right_[i][j] = min(right_[i - 1][j], rz - j); //rz表示右边最近的障碍
还要注意初始化要让第0行的元素为正无穷,防止其对答案造成干扰。
当遇到障碍点时,应先更新最近障碍位置,然后将该点的left_, right_值赋成正无穷。原因同上。
为了方便维护lz, rz 的值,我跑了两边双重循环。。。 (复杂度依然O(nm) )
然后可以分别将0,1当作障碍各求一个解,输出最大值。
AC代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> using namespace std; const int MAXN = 2010; short ma[MAXN][MAXN]; int n,m,lz,rz,ans,bns; int up[MAXN][MAXN]; int left_[MAXN][MAXN]; int right_[MAXN][MAXN]; int read() { int x = 0; char c = getchar(); while(c>'9' || c<'0') c = getchar(); while(c>='0' && c<='9') x = x*10 + c - '0', c = getchar(); return x; } void color_(int c) { memset(up, 0, sizeof(up)); memset(left_, 0, sizeof(left_)); memset(right_, 0, sizeof(right_)); for (int i = 1; i <= m; ++i) left_[0][i] = 999999999, right_[0][i] = 999999999; for (int i = 1; i <= n; ++i) { lz = 0; for (int j = 1; j <= m; ++j) if (ma[i][j] != c) { lz = j; left_[i][j] = 99999999; continue; } else { up[i][j] = up[i - 1][j] + 1; left_[i][j] = min(left_[i - 1][j], j - lz); } } for (int i = 1; i <= n; ++i) { rz = m + 1; for (int j = m; j >= 1; --j) if (ma[i][j] != c) { rz = j; right_[i][j] = 99999999; continue; } else right_[i][j] = min(right_[i - 1][j], rz - j); } for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { ans = max(ans, up[i][j] * (left_[i][j] + right_[i][j] - 1)); bns = max(bns, min(up[i][j], (left_[i][j] + right_[i][j] - 1))*min(up[i][j], (left_[i][j] + right_[i][j] - 1))); } } } int main() { scanf("%d %d", &n, &m); for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) { scanf("%d", &ma[i][j]); if ((i + j) & 1) ma[i][j] = abs(ma[i][j] - 1); } ans = -1; bns = -1; color_(1); color_(0); cout << bns << endl << ans; }