原题链接:https://www.luogu.com.cn/problem/P4147
玉蟾宫
题目背景
有一天,小猫 rainbow 和 freda 来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地。
题目描述
这片土地被分成 N × M N\times M N×M 个格子,每个格子里写着 ‘R’ 或者 ‘F’,R 代表这块土地被赐予了 rainbow,F 代表这块土地被赐予了 freda。
现在 freda 要在这里卖萌。。。它要找一块矩形土地,要求这片土地都标着 ‘F’ 并且面积最大。
但是 rainbow 和 freda 的 OI 水平都弱爆了,找不出这块土地,而蓝兔也想看 freda 卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为 S S S,它们每人给你 S S S 两银子。
输入格式
第一行两个整数 N N N, M M M,表示矩形土地有 N N N 行 M M M 列。
接下来 N N N 行,每行 M M M 个用空格隔开的字符 ‘F’ 或 ‘R’,描述了矩形土地。
输出格式
输出一个整数,表示你能得到多少银子,即 ( 3 × 最大 ’F’ 矩形土地面积 3\times \text{最大 'F' 矩形土地面积} 3×最大 ’F’ 矩形土地面积) 的值。
输入输出样例
输入 #1
5 6
R F F F F F
F F F F F F
R R R F F F
F F F F F F
F F F F F F
输出 #1
45
说明/提示
对于
50
%
50\%
50% 的数据,
1
≤
N
,
M
≤
200
1 \leq N, M \leq 200
1≤N,M≤200。
对于
100
%
100\%
100% 的数据,
1
≤
N
,
M
≤
1000
1 \leq N, M \leq 1000
1≤N,M≤1000。
题解
最大子矩阵居然真的可以用数据结构,是👴浅薄了。
这钵是用单调栈求解最大子矩阵问题,首先我们可以预处理出每个F点能向上延伸的长度h[i][j]
,如此就把整个网格分隔成了若干宽度为
1
1
1的矩形,以样例为例:
可以发现,对于并列的若干矩形,想要把它们合并成一个大矩形,那么矩形的高度只取决于最矮的那个矩形,所以当我们从左向右遍历所有矩形时,对于新加入的矩形,前面已加入的比它高的矩形都无意义,因为要合并的话新矩形的高就等于它自己的高,我们大可弹出前面的矩形,只保留宽度信息。
所以我们可以维护一个单增的单调栈,栈内保存的是当前加入矩形的高度和加入时弹出的若干矩形的宽度之和+1(1是自己的宽度)。在某矩形弹出栈的时候用(该矩形记录的加入该矩形时弹出矩形的累计宽度+此次弹出时在该矩形之前弹出的矩形的累计宽度) × \times ×高度来更新答案。因为该矩形自己记录的只包含以比它高且在它左侧的矩形合并出的矩形,而在单增单调栈中该矩形右侧后面加入的矩形实际上也比它高,要合并的话也是最终矩形也是它的高度,所以我们就再加上在它之前弹出的矩形的累计宽度。另外,最后记得用栈内的元素再更新一次答案。
但是这就存在一个问题,同一列可能有多个矩形,而上述做法只能解决一列只有一个矩形的问题。于是我们直接暴力枚举行,每一行都做一次单调栈,复杂度 O ( n 2 ) O(n^2) O(n2)。
上述描述可能有点抽象,建议结合代码理解。
代码
scanf
用多了,今天才知道cin
读字符可以过滤空格,女少口阿。
#include<bits/stdc++.h>
using namespace std;
const int M=1005;
struct Rec{int h,w;}stk[M];
int n,m,h[M][M],ans,top;
void ask(int x)
{
stk[top=1]=(Rec){h[x][1],1};
for(int i=2,w;i<=m;++i)
{
for(w=0;top&&h[x][i]<=stk[top].h;--top)w+=stk[top].w,ans=max(ans,w*stk[top].h);
stk[++top]=(Rec){h[x][i],w+1};
}
for(int w=0;top;--top)
{
w+=stk[top].w;
ans=max(ans,w*stk[top].h);
}
}
void in()
{
scanf("%d%d",&n,&m);
char a;
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
{
cin>>a;
h[i][j]=a=='F'?h[i-1][j]+1:0;
}
}
void ac()
{
for(int i=1;i<=n;++i)ask(i);
printf("%d\n",ans*3);
}
int main()
{
in(),ac();
system("pause");
}