《算法竞赛-训练指南》第一章-1.22_LA 3209

这道题目也蛮好的,是求满足条件最大子矩阵的,用到的方法非常的独特。


这道题目容易想到的是暴力,枚举每个点,以每个点作为起点在枚举边长,这样的操作肯定超时,而题解中给的方法就非常的棒了,把每个点的周围状态压缩一下,压缩成一个带上下左右的结点来表达周围的状况,例如up[i][j] 代表的就是以这个点的周围的矩形最大高度,left[i][j]表示这个矩形能到的最左边的左边, right[i][j]表示这个矩形能到的最右边的距离。知道了这些信息,其实扫一遍所有的点不就能出来最大的面积了么?

当然重要的就是更新每个结点的问题,这个题目也是很好的,当这个点是不能被利用的,那么这个点的up = 0,左断点为0,又断电为N - 1,为什么要这么设计呢?因为下面的更新非常重要(例如,假设第一排全部都不能用,而第二排中有能用的,你的那个递推公式要能用的话,left[i][j] = max(left[i - 1][j], lo + 1) , right[i][j] = min(right[i][j], lo - 1)假设第二排第三个点到最后一个点都能用,你要保证能更新成功所有的right[i][j]的话,那么必须使得不能用时的右端点为N - 1,请自行举例验证一下,当然left是一样的道理的)。


难点就是模型的建立和left和right的具体递推和注意事项。


贴出代码:

#include <stdio.h>
#include <string.h>
#include <string>

using namespace std;

const int MAXN = 1000 + 11;

int N, M;

int map[MAXN][MAXN];

int up[MAXN][MAXN];

int left[MAXN][MAXN];

int right[MAXN][MAXN];

int main()
{
	char str[10];
	int T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d", &N, &M);
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < M; j++)
			{
				scanf("%s", str);
				map[i][j] = (str[0] == 'R') ? 0 : 1;
			}
		}
		int lo = -1;
		for (int j = 0; j < M; j++)
		{
			if (map[0][j] == 0)
			{
				up[0][j] = left[0][j] = 0;
				lo = j;
			}
			else
			{
				up[0][j] = 1;
				left[0][j] = lo + 1;
			}
		}
		lo = M;
		for (int j = M - 1; j >= 0; j--)
		{
			if (map[0][j] == 0)
			{
				right[0][j] = N - 1;
				lo = j;
			}
			else
			{
				right[0][j] = lo - 1;
			}
		}
		int ans = 0;
		for (int i = 1; i < N; i++)
		{
			lo = -1;
			for (int j = 0; j < M; j++)
			{
				if (map[i][j] == 0)
				{
					up[i][j] = 0;
					left[i][j] = 0;
					lo = j;
				}
				else
				{
					up[i][j] = up[i - 1][j] + 1;
					left[i][j] = max(left[i - 1][j], lo + 1);
				}
			}
			lo = M;
			for (int j = M - 1; j >= 0; j--)
			{
				if (map[i][j] == 0)
				{
					up[i][j] = 0;
					right[i][j] = M - 1;
					lo = j;
				}
				else
				{
					right[i][j] = min(right[i - 1][j], lo - 1);
				}
				int t = (right[i][j] - left[i][j] + 1) * up[i][j];
				if (ans < t)
				{
					ans = t;
				}
			}
		}
		printf("%d\n", ans * 3);
	}
//	system("pause");
	return 0;
}
/*
5 5
R R R R R
R R R R R
R R R F F
R R R R R
R R R R R
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值