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

来源:互联网 发布:葫芦侠软件源 编辑:程序博客网 时间:2024/05/26 15:57

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


这道题目容易想到的是暴力,枚举每个点,以每个点作为起点在枚举边长,这样的操作肯定超时,而题解中给的方法就非常的棒了,把每个点的周围状态压缩一下,压缩成一个带上下左右的结点来表达周围的状况,例如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 5R R R R RR R R R RR R R F FR R R R RR R R R R*/