P4147 玉蟾宫
题目背景
有一天,小猫 rainbow 和 freda 来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们
并赐予它们一片土地。
题目描述
这片土地被分成 \(N\times M\) 个格子,每个格子里写着 'R' 或者 'F',R 代表这块土地被赐予了 rainbow,
F 代表这块土地被赐予了 freda。现在 freda 要在这里卖萌。。。它要找一块矩形土地,
要求这片土地都标着 'F' 并且面积最大。但是 rainbow 和 freda 的 OI 水平都弱爆了,找不出这块土地,
而蓝兔也想看 freda 卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为 S,它们每人给你 S 两银子。
输入格式
第一行两个整数 N,M,表示矩形土地有 N 行 M 列。
接下来 N 行,每行 M 个用空格隔开的字符 'F' 或 'R',描述了矩形土地。
输出格式
输出一个整数,表示你能得到多少银子,即 (3\times \text{最大 '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% 的数据,1 <= N,M <= 200
对于 100%100%100% 的数据,1≤N,M≤1000
我们可以想到O(n^4)的暴力。
枚举每个矩形的左上角和右下角,然后算出每个矩形的贡献,
最后在对每个矩形的贡献取个max便是最后的答案
但这样只能过一半的数据,然后我们就要考虑优化。
我们可以对每个点求出他向下所能延伸的高度,也就是矩形的高。
那么怎么确定矩形的宽呢?
如果,我们能求出他所能向左和向右能延伸的长度,那么这个矩形的贡献不就很好的求出来了吗?
一个点能向左右两边延伸,当且仅当左右两边能向下延伸的高度比他大的时候。
我甩给你一张图:
颜色表示每个点能向下延伸的最大长度,看看这张图应该能明白吧(雾)
自己可以手动模拟一下。
求序列中第一个比他小的数,那这不是单调栈吗?
所以,我们对每一行从左往右跑一边单调栈,在反过来跑一边就可以求出每个点的贡献
最后答案一定不要忘记乘三(当初我傻乎乎的调了半天才发现没乘三)
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
char ch;
int n,m,top,ans;
int sta[1010],ls[1010],rs[1010],h[1010][1010];
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
return s * w;
}
int main()
{
n = read(); m = read();
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
cin>>ch;
if(ch == 'F') h[i][j] = h[i-1][j] + 1;//求出每个点能向下延伸的高度也就是矩形的高
}
top = 0; sta[++top] = 0;
for(int j = 1; j <= m; j++)//正着跑一遍单调栈求出每个点能向左延伸的长度
{
while(top && h[i][sta[top]] >= h[i][j]) top--;
ls[j] = sta[top]; sta[++top] = j;
}
top = 0; sta[++top] = m+1;
for(int j = m; j >= 1; j--)//反着跑一遍单调栈求出每个点向右能延伸的长度
{
while(top && h[i][sta[top]] >= h[i][j]) top--;
rs[j] = sta[top]; sta[++top] = j;
}
for(int j = 1; j <= m; j++)
{
ans = max(ans,3 * h[i][j] * (rs[j]-ls[j]-1));//统计答案
}
}
printf("%d\n",ans);
return 0;
}
听别的大佬用悬线法切了这道题。
但蒟蒻我太菜了,没学会。
先占上坑吧,后期再补。