1.问题引入:对于一个矩形图,图中放置着不少障碍,要求出最大的不含障碍的矩形。
2.分析:显然一个极大矩形是左右上下都被障碍挡住,无法再扩大的矩形,此时障碍也包括边界。
3.方法:悬线法考虑以当前点所在行为下界,以往上能达到的最大距离为高度,正上方所有点的往左最大距离的最小值和往右最大距离的最小值 之和作为宽的矩形。
其核心代码固定,使用时可以考虑直接套用。如下:
1.初始化求每个点leftmax,rightmax代码,0表示有障碍,不可走:
void initialize(int** G, int** leftmax, int** rightmax, int n, int m) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (!G[i][j]) continue;
leftmax[i][j] = leftmax[i][j - 1] + 1;
}
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= 1; j--) {
if (!G[i][j]) continue;
rightmax[i][j] = rightmax[i][j + 1] + 1;
}
}
}
2.求最大矩形面积的代码:
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(i > 1 && G[i][j] && G[i-1][j]){
upmax[i][j] = upmax[i-1][j] + 1;
leftmax[i][j] = min(leftmax[i][j], leftmax[i-1][j]);//这样操作可以确保顺序遍历时,每个元素的leftmax都变为正上方元素的最小leftmax
rightmax[i][j] = min(rightmax[i][j], rightmax[i-1][j]);//同理
}
ans = max(ans, (upmax[i][j] + 1) * (leftmax[i][j] + rightmax[i][j] - 1)); //高度+1(加上自身),宽度-1(因为往左包含了当前点,往右也包含了当前点,当前点算了两次)
}
}
P1. 洛谷p4147玉蟾宫
#include<iostream>
#include<cmath>
using namespace std;
int n,m;
void initialize(int** G, int** leftmax, int** rightmax, int n, int m) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (!G[i][j]) continue;
leftmax[i][j] = leftmax[i][j - 1] + 1;
}
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= 1; j--) {
if (!G[i][j]) continue;
rightmax[i][j] = rightmax[i][j + 1] + 1;
}
}
}
void showcurmap(int** a)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}
}
int main()
{
cin>>n>>m;//考虑图置中,有效避免越界
int** G;int**leftmax;int**rightmax;int** upmax;
G=new int*[n+2];
leftmax=new int*[n+2];
rightmax=new int*[n+2];
upmax=new int*[n+2];
for(int i=0;i<=n+1;i++)
{
G[i]=new int[m+2]{0};
leftmax[i]=new int[m+2]{0};
rightmax[i]=new int[m+2]{0};
upmax[i]=new int[m+2]{0};
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
char put;cin>>put;
if(put=='F') G[i][j]=1;
}
initialize(G,leftmax,rightmax,n,m);
long long ans=0;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(i > 1 && G[i][j] && G[i-1][j]){
upmax[i][j] = upmax[i-1][j] + 1;
leftmax[i][j] = min(leftmax[i][j], leftmax[i-1][j]);//这样操作可以确保顺序遍历时,每个元素的leftmax都变为正上方元素的最小leftmax
rightmax[i][j] = min(rightmax[i][j], rightmax[i-1][j]);//同理
}
ans = fmax(ans, (upmax[i][j] + 1) * (leftmax[i][j] + rightmax[i][j] - 1)); //高度+1(加上自身),宽度-1(因为往左包含了当前点,往右也包含了当前点,当前点算了两次)
}
}
cout<<3*ans;
//showcurmap(G);
}