题解 LuoGu P4147 玉蟾宫 最大子矩阵问题 悬线法 学习笔记
题目描述
这片土地被分成N*M个格子,每个格子里写着’R’或者’F’,R代表这块土地被赐予了rainbow,F代表这块土地被赐予了freda。
现在freda要在这里卖萌。。。它要找一块矩形土地,要求这片土地都标着’F’并且面积最大。
但是rainbow和freda的OI水平都弱爆了,找不出这块土地,而蓝兔也想看freda卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为S,它们每人给你S两银子。
输入格式:
第一行两个整数N,M,表示矩形土地有N行M列。接下来N行,每行M个用空格隔开的字符’F’或’R’,描述了矩形土地。
输出格式:
输出一个整数,表示你能得到多少银子,即(3*最大’F’矩形土地面积)的值。
本题要求 最大子矩阵面积
O(N^3^)
暴力做法,枚举每个点,每个点向下的宽度,每个点向右的长度,比较
TLE
O(N^2^) 悬线法
考虑这样一个问题,在一个点,向左右不断延伸直到无法延伸
对这一行连续的点进行该操作
那么对于这一行连续的点来说,向左向右延伸的和相同
说明矩阵的长度已经确定,现在需要确定宽度
再不断向上延伸,但是为了保证矩形,所以长度变成了向上延伸的每行的宽度的最小值
如图:
那么再来思考一个问题,一个点在向上拓展的过程中,可能由于上一行的宽度比现在的宽度小,那么等于为了高度舍弃了一定宽度,为什么会算出最大矩形呢?
如图:
答案是不会的,矩形的确定与拓展的高度有关。
如果对每个点都进行该操作,那么图中的绿色点得到的矩形就是紫色矩形,
只用在最后比较每个点拓展的矩形的面积大小即可
这就是悬线法,知道了原理后总结一下:
对每个点向左向右拓展的宽度,再算出向上拓展的高度
计算每个点组成的矩形面积
比较一下答案
总结一下
因为是矩形,最好的复杂度好不过N^2^
N^3^的做法通常是算出的结果有重复,每个点算了多次
N^2^可以使每个点只算一遍,减少了非比较重复计算
代码:
#include<bits/stdc++.h>
int LL[1001][1010],RR[1001][1010],HH[1001][1010];
int a[1010][1010];
int ans=0;
int main (){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
char x=' ';
while(x==' '||x=='\n')scanf("%c",&x);
if(x=='F'){
a[i][j]=1;
HH[i][j]=HH[i-1][j]+1;
LL[i][j]=LL[i][j-1]+1;
}
}
for(int i=1;i<=n;++i)
for(int j=m;j>=1;--j){
if(a[i][j]){
RR[i][j]=RR[i][j+1]+1;
}
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
if(LL[i-1][j]>0){
LL[i][j]=std::min(LL[i-1][j],LL[i][j]);
RR[i][j]=std::min(RR[i-1][j],RR[i][j]);
}
ans=std::max(ans,(LL[i][j]+RR[i][j]-1)*HH[i][j]);
}
printf("%d",ans*3);
}
0 条评论