直方图dp二维版本。
给你一个N*M
的网格,每个格子有两种状态(R
,F
),让你在其中找一个面积最大的全部由F
组成的矩形。
思路就是把每一行都当作每次直方图dp的底边,执行N
次直方图dp,取最大值。
某一行某一列的柱子的高度可以看成从当前网格出发向上,碰到第一个R
为止(不包括),有几个连续的F
(包括自己)。若当前网格就是R
,那么当前柱子高度就是0
。
网格是一行行输入的,若当前行当前列是F
,那么具体的高度值可以由上一行当前列的值传递下来。
最后,这个题的输入很烦,是一个字符矩阵,每读取一个字符都要考虑这个字符之前有没有换行和空格。可以看出每行行首的字符前都会有一个换行,每行非行首的字符前都会有一个空格。代码中有两种读取形式,一种AC一种WA。我并不知道为什么。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std; // 直方图dp二维版
const int MAXN = 1001;
int M, N, T;
int g[MAXN][MAXN]; // 表示第i行第j列的元素向上数有几个连续的F(包括自己,碰到第一个R为止)
int l[MAXN], r[MAXN];
int ans;
void init()
{
for (int i = 1; i <= M; i++) // 第1行i-1=0,用到了第0行的临界数据
g[0][i] = 0;
ans = -1;
}
int main()
{
char c;
scanf("%d", &T);
for (; T--;)
{
scanf("%d%d", &N, &M);
init();
for (int i = 1; i <= N; i++)
{
for (int j = 1; j <= M; j++)
{
if (j == 1) scanf("\n%c", &c);
else scanf(" %c", &c); // 这样写就AC
//getchar();
//scanf("%c", &c); // 这样就WA,我就想问,这两种有什么区别?
if (c == 'R') g[i][j] = 0;
else g[i][j] = g[i - 1][j] + 1; // 可以从上往下传递,上一行的当前位置是R(0)就说明要从本行算起
}
}
for (int i = 1; i <= N; i++) // 每一行可看成一个直方图dp
{
int t;
l[1] = 1;
for (int j = 2; j <= M; j++)
{
for (t = j; t - 1 >= 1 && g[i][t - 1] >= g[i][j]; t = l[t - 1]);
l[j] = t;
}
r[M] = M;
for (int j = M - 1; j >= 1; j--)
{
for (t = j; t + 1 <= M && g[i][t + 1] >= g[i][j]; t = r[t + 1]);
r[j] = t;
}
for (int j = 1; j <= M; j++)
{
ans = max(ans, g[i][j] * (r[j] - l[j] + 1)); // 即使全是R,但因为高度都是0,所以直接把初始的-1覆盖掉了
}
}
printf("%d\n", ans * 3);
}
return 0;
}