原题链接:https://www.luogu.com.cn/problem/P4147
题意解读:找到一个只包含'F'的最大的子矩形。
解题思路:
方法1:设R为0,F为1,先计算二维前缀和,再枚举所有子矩形左上角(x1,y1)、右下角(x2,y2),计算子矩形的区间和,更新最大值,只能得到部分分。
方法2:
对于二维矩阵每个点,定义三个属性:h[][],l[][],r[][]
h表示该点最大悬线高度,所谓悬线,如图
点(2,3)的悬线是从(2,3)往上一共有多少个F,所有h[2][3] = 2, h[5][4] = 5
如何计算h[][]?
可以通过递推:
h[i][j] =h[i-1][j] +1; //每个点的悬线高度等于上一个点的悬线高度+1
知道了每个点往上最大的高度,还需要知道每个点这个高度能到左、右什么位置,所以l[][], r[][]就分别表示某个点的悬线能到的最左、最右的坐标。
有了这些属性,只需要枚举每一个点,计算h * (r - l + 1) * 3,即可得到最大的结果。
那么,问题就剩下如何计算l[][], r[][]?
对于l[][]
第一步:遍历每一行,每一行从左到右遍历,将l[i][j]设置为从(i,j)能到的最左边的F的纵坐标,可以通过递推得到
for(int i = 1; i <= n; i++)
{
for(int j = 2; j <= m; j++)
{
if(a[i][j] == 'F' && a[i][j - 1] == 'F')
{
l[i][j] = l[i][j - 1]; //在每一行能到的最左边的F,如果相邻两个点都是F,则为左边点的l值
}
}
}
第二步:遍历每一行,每一行从左到右遍历,将l[i][j]更新为(i,j)的悬线能到的最左边的纵坐标(所谓悬线能到,是指从(i,j)往左的悬线高度都不低于h[i][j]),同样可以通过递推得到
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(a[i][j] == 'F' && a[i - 1][j] == 'F')
{
l[i][j] = max(l[i][j], l[i - 1][j]); //每个点的悬线能到的最左边,如果上一个点l值更大,则更新为上一个点的l
}
}
}
对于r[][]
第一步:遍历每一行,每一行从右到左遍历,将l[i][j]设置为从(i,j)能到的最右边的F的纵坐标,可以通过递推得到
for(int i = 1; i <= n; i++)
{
for(int j = m - 1; j >= 1; j--)
{
if(a[i][j] == 'F' && a[i][j + 1] == 'F')
{
r[i][j] = r[i][j + 1]; //在每一行能到的最右边的F,如果相邻两个点都是F,则为右边点的l值
}
}
}
第二步:遍历每一行,每一行从左到右遍历,将r[i][j]更新为(i,j)的悬线能到的最右边的纵坐标(所谓悬线能到,是指从(i,j)往右的悬线高度都不低于h[i][j]),同样可以通过递推得到
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(a[i][j] == 'F' && a[i - 1][j] == 'F')
{
r[i][j] = min(r[i][j], r[i - 1][j]); //每个点的悬线能到的最右边,如果上一个点r值更小,则更新为上一个点的r
}
}
}
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n, m, ans;
char a[N][N];
int h[N][N]; //每个点是F且能向上达到最大高度(悬线高度)
int l[N][N]; //每个点的悬线能到达的最左端点
int r[N][N]; //每个点的悬线能达到的最右端点
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
cin >> a[i][j];
if(a[i][j] == 'F')
{
h[i][j] = h[i - 1][j] + 1; //每个点的悬线高度等于上一个点的悬线高度+1
l[i][j] = r[i][j] = j; //初始化
}
else h[i][j] = 0;
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 2; j <= m; j++)
{
if(a[i][j] == 'F' && a[i][j - 1] == 'F')
{
l[i][j] = l[i][j - 1]; //在每一行能到的最左边的F,如果相邻两个点都是F,则为左边点的l值
}
}
for(int j = m - 1; j >= 1; j--)
{
if(a[i][j] == 'F' && a[i][j + 1] == 'F')
{
r[i][j] = r[i][j + 1]; //在每一行能到的最右边的F,如果相邻两个点都是F,则为右边点的l值
}
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(a[i][j] == 'F' && a[i - 1][j] == 'F')
{
l[i][j] = max(l[i][j], l[i - 1][j]); //每个点的悬线能到的最左边,如果上一个点l值更大,则更新为上一个点的l
r[i][j] = min(r[i][j], r[i - 1][j]); //每个点的悬线能到的最右边,如果上一个点r值更小,则更新为上一个点的r
}
ans = max(ans, 3 * (r[i][j] - l[i][j] + 1) * h[i][j]);
}
}
cout << ans;
return 0;
}
标签:遍历,玉蟾,int,洛谷题,高度,一行,悬线,P4147,递推 From: https://www.cnblogs.com/jcwy/p/18361265