P1169 [ZJOI2007]棋盘制作 && 悬线法

P1169 [ZJOI2007]棋盘制作

给出一个 \(N * M\)\(01\) 矩阵, 求最大的正方形和最大的矩形交错子矩阵
\(n , m \leq 2000\)

悬线法

悬线法可以求出给定矩阵中满足条件的最大子矩阵

对于每个点, 维护 两条等长的线段, 两线段的底部达到此点的纵坐标, 分别代表能从这个点达到的最左 / 最右端点
大概长这样

   l        r
   |        |
   |        |
   |        |
   |        |
   |    *   |

那么枚举每个点的这两条线段, 不断用 \((r - l + 1) * dis\) 更新答案即可
这就是悬线法

这两条线段看上去很难维护, 其实不然
因为其等长, 我们将这两条线段用如下几个属性表示:
\(l[i][j]\) 表示从 \((i, j)\) 能达到的最左的坐标
\(r[i][j]\) 表示从 \((i, j)\) 能达到的最右的坐标
\(up[i][j]\) 表示 \((i, j)\) 向上达到的 最上坐标, 即悬线的长度

初始化满足条件的每个\(1 * 1\) 小矩阵 \(l[i][j] = r[i][j] = j, up[i][j] = 1\), 即围成一个 \(1 * 1\) 的小小矩形

容易想到维护悬线可以递推, 在满足矩阵限制的条件下, 先初始化

\[l[i][j] = l[i][j - 1]$$ $$r[i][j] = r[i][j + 1] \]

比对上一行,在满足矩阵限制的条件下, 我们只能取最窄满足条件

\[l[i][j] = max(l[i][j], l[i - 1][j])$$ $$r[i][j] = min(r[i][j], r[i - 1][j]) \]

然后悬线长度可以继承上一行的 $$up[i][j] = up[i - 1][j] + 1$$

有了悬线直接计算围出来的面积即可

Solution

此题求最大交错矩阵
交错矩阵任意相邻两格颜色不同
于是限制条件即为相邻两格颜色不等
放个代码理解 悬线法

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
#define REP(i, x, y) for(int i = (x);i <= (y);i++)
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 2019;
int lenx, leny;
int map[maxn][maxn];
int l[maxn][maxn], r[maxn][maxn];
int up[maxn][maxn];
int ans1, ans2;
void init(){
	lenx = RD(), leny = RD();
	REP(i, 1, lenx)REP(j, 1, leny){
		map[i][j] = RD();
		l[i][j] = r[i][j] = j;
		up[i][j] = 1;
		}
	REP(i, 1, lenx)REP(j, 2, leny){
		if(map[i][j] != map[i][j - 1])l[i][j] = l[i][j - 1];//预处理左边界
		}
	REP(i, 1, lenx)for(int j = leny - 1;j >= 1;j--){
		if(map[i][j] != map[i][j + 1])r[i][j] = r[i][j + 1];//右边界
		}
	}
void solve(){
	REP(i, 1, lenx)REP(j, 1, leny){
		if(i > 1 && map[i][j] != map[i - 1][j]){
			l[i][j] = max(l[i][j], l[i - 1][j]);
			r[i][j] = min(r[i][j], r[i - 1][j]);
			up[i][j] = up[i - 1][j] + 1;
			}
		int a = r[i][j] - l[i][j] + 1;//宽
		int b = min(a, up[i][j]);
		ans1 = max(ans1, b * b);
		ans2 = max(ans2, a * up[i][j]);
		}
	printf("%d\n%d\n", ans1, ans2);
	}
int main(){
	init();
	solve();
	return 0;
	}
posted @ 2018-11-05 16:31  Tony_Double_Sky  阅读(261)  评论(0编辑  收藏  举报