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

19 篇文章 0 订阅

P1169 [ZJOI2007]棋盘制作 

悬线法

悬线法是一种通用的用于求子矩阵的方法

先对于每个点处理出以该点为基点,(x,y)

向右可以到最远的点left[i][j],向左可以到的最远的点right[i][j]

则对于每条横轴,处理出若干条线,再对于各个横轴dp合并

更新up[i][j]表示该点纵向向上可延申长度

状态和状态方程 

状态

left[i][j]为点(i,j)向左延申最远的点

right[i][j]为点(i,j)向右延申最远的点

up[i][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

题意 

 代码

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

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值