题目描述 Description
有一天,小猫rainbow和freda来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地
这片土地被分成N∗MN∗M个格子,每个格子里写着’R’或者’F’,R代表这块土地被赐予了rainbow,F代表这块土地被赐予了freda
现在freda要在这里卖萌…它要找一块矩形土地,要求这片土地都标着’F’并且面积最大
但是rainbow和freda的OI水平都弱爆了,找不出这块土地,而蓝兔也想看freda卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为S,它们每人给你S两银子
输入描述 Input Description
第一行两个整数N,MN,M,表示矩形土地有NN行列。
接下来NN行,每行个用空格隔开的字符’F’或’R’,描述了矩形土地
输出描述 Output Description
输出一个整数,表示你能得到多少银子,即(3*最大’F’矩形土地面积)的值
样例输入 Sample Input
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
样例输出 Sample Output
45
数据范围及提示 Data Size & Hint
对于50%的数据,1≤N,M≤2001≤N,M≤200
对于100%的数据,1≤N,M≤10001≤N,M≤1000
Solution
首先坑比的数据读入建议使用fget
因为我的编译器不准用get()
接下来就转化为了01矩阵中的最大全0矩阵了
论文 朱晨光《基本数据结构在信息学竞赛中的应用》中写到我们可以用单调栈来A掉这道题
首先,一个极大矩形(即无法继续扩大的全0矩形)中一定存在着一条竖直的”悬线”(即从一点开始不断向上扩展,直到遇到1或者整个矩阵边界为止所形成的竖线),并且这个矩形就是由这条”悬线”不断向左向右扩展得到
悬线的长度可以很容易用动态规划算法得到
设方格(x,i)(x,i)的悬线长度为h(x,i)h(x,i),则h(x,i)h(x,i)的递推式为
现在的任务是,对于矩阵的每一行,在求出这行中所有h(x,i)h(x,i)的值后,需要得到每一条悬线能够最多往左往右各延伸多长的距离而不碰到障碍”1”
从h(x,1)h(x,1)开始,依次向栈里插入值.但是与简单的入栈不同,这里必须考虑h(x,i)h(x,i)与栈里元素之间的关系
设栈顶元素的值为AA,对应的列坐标值为
- 如果栈为空或待插入的h(x,i)>Ah(x,i)>A,则将h(x,i)h(x,i)插入栈,成为新的栈顶元素,我们已经可以确定h(x,i)h(x,i)的左边界,但是栈顶元素的右边界不能确定
- 如果h(x,i)=Ah(x,i)=A,右边界还不能确定,而且因为h(x,i)h(x,i)与栈顶元素左边界相同,扩展出的矩形相同,所以他们是等价的,不必插入
- 如果A>h(x,i)A>h(x,i),那么我们就不断弹出栈顶元素,因为这些比h(x,i)h(x,i)大的垂线不可能再向右延伸了.在弹出元素的过程中,对于每一个出栈的元素AA,若他的横坐标为,那么它所对应的垂线最多向右扩展i−ji−j格,所以用A∗(i−j)A∗(i−j)更新MAXSIZEMAXSIZE.弹出一直持续到栈为空或者栈顶元素的值A≤h(x,i)A≤h(x,i). 如A=h(x,i)A=h(x,i) ,则按照2情况处理,如栈为空或待插入的h(x,i)>Ah(x,i)>A,由于我们至少弹出了一个元素,设他的列坐标为jj,显然所代表的悬线能狗一直向左扩展到第jj列,此时将入栈,不过横坐标应该设为jj,表示他能一直向左扩展到第列
并且我们需要在行尾添加一个哨兵h(x,n+1)=−1h(x,n+1)=−1,使得栈中非哨兵元素会被全部弹出,从而不遗漏任何一个值
这个算法时间复杂度为O(n2)O(n2),空间复杂度为O(n)O(n),还是很优秀的
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int a[N][N];
int num[N][N];
pair <int,int> sta[N];
int top;
int MAX=0;
int read() {
int ans=0,flag=1;
char ch=getchar();
while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
if(ch=='-') {flag=-1;ch=getchar();}
while(ch>='0' && ch<='9') {ans=ans*10+ch-'0';ch=getchar();}
return ans*flag;
}
char str[N<<2];
int main() {
int n=read(),m=read();
for(int i=1;i<=n;++i) {
fgets(str,m*3,stdin);
for(int j=1;j<=m;++j)
if(str[j*2-2]=='F')
a[i][j]=1;//用的全1矩阵,一样的.
}
for(int i=1;i<=n;++i) {
for(int j=1;j<=m;++j) {if(a[i][j]==1) num[i][j]=num[i-1][j]+1;}
num[i][m+1]=-1;
while(top) sta[top--]={0,0};
for(int j=1;j<=m+1;++j) {
if(!top || num[i][j]>sta[top].first) sta[++top]={num[i][j],j};
else if(num[i][j]!=sta[top].first){
int last=j;
while(top && num[i][j]<sta[top].first) {
MAX=max(MAX,sta[top].first*(j-sta[top].second));
last=min(last,sta[top].second);
sta[top--]={0,0};
}
if(sta[top].first!=num[i][j])
sta[++top]={num[i][j],last};
}
}
}
printf("%d\n",MAX*3);
return 0;
}