最大子矩形问题:在一个给定的矩形网格中有一些障碍点,要找出网格内部不包含任何障碍点,且边界与坐标轴平行的最大子矩形。
常用的算法有两个:
单调栈,时间复杂度O(n*m)
极大化思想,时间复杂度O(S*S)(S是障碍点的个数),原文章见下:
http://t.csdnimg.cn/L1Khe(原文没有提如何解决忽略了左边界与整个举行的左边界重合,而右边界覆盖了一个障碍点的情况,实际解决方法是从左到右、从右到左各搜一遍)
单调栈解法最为常见:(最大矩形面积其实是柱状图最大矩形面积的进阶)
洛谷P4147 玉蟾宫
输入格式
第一行两个整数 N,M,表示矩形土地有 N 行 M 列。
接下来 N 行,每行 M 个用空格隔开的字符 'F' 或 'R',描述了矩形土地。
输出格式
输出一个整数,表示你能得到多少银子,即 (3×最大 ’F’ 矩形土地面积) 的值。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
char ma[1001][1001];
int h[1001] = {0};
int sta[1001];
int cnt = 0;
int ans = 0;
int main(){
cin>>n>>m;
for(int i = 0;i<n;i++){
for(int j = 0;j<m;j++){
cin>>ma[i][j];
}
}
for(int i = 0;i<n;i++){
for(int j = 0;j<m;j++){
if(ma[i][j]=='F'){
h[j] = h[j]+1;
}else{
h[j] = 0;
}
}
for(int j = 0;j<m;j++){
while(cnt>0&&h[sta[cnt-1]]>=h[j]){
//注意这里是>=
//原因可以去看看左神视频:单调栈(上),里面最柱状图最大矩形的解析
int height = h[sta[cnt-1]];
cnt--;
int l;
if(cnt!=0){
l = sta[cnt-1];
}else{
l = -1;
}
ans = max(ans,height*(j-l-1));
}
sta[cnt++] = j;
}
while(cnt>0){
int height = h[sta[cnt-1]];
int l;
cnt--;
if(cnt!=0){
l = sta[cnt-1];
}else{
l = -1;
}
ans = max(ans,height*(m-l-1));
}
}
cout<<ans*3;
}
极大化思想,时间复杂度比单调栈好,对时间复杂度要求更高或者矩形过大时使用。但在障碍点过多时与单调栈算法在时间上类似。
洛谷P1578 奶牛浴场
输入格式
输入文件的第一行包含两个整数 L 和 W,分别表示牛场的长和宽。
文件的第二行包含一个整数 n,表示产奶点的数量。
以下 n 行每行包含两个整数 x 和 y,表示一个产奶点的坐标。
所有产奶点都位于牛场内,即:0≤x≤L,0≤y≤W。
输出格式
输出文件仅一行,包含一个整数 S,表示浴场的最大面积。
代码:
#include<bits/stdc++.h>
using namespace std;
int read()//快读
{
int sum = 0 , f = 1;
char c = getchar();
while(c < '0' or c > '9')
{
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' and c <= '9')
{
sum = (sum << 1) + (sum << 3) + c - '0';
c = getchar();
}
return sum * f;
}
struct S{//存放障碍点信息
int x , y;
}s[5010];
bool cmp1(S a , S b)//按横坐标排序
{
if(a.x != b.x) return a.x < b.x;
else return a.y < b.y;
}
bool cmp2(S a , S b)//按纵坐标排序
{
if(a.y != b.y) return a.y < b.y;
else return a.x < b.x ;
}
int l , w , n , ans;
int main()
{
l = read() , w = read() , n = read();
for(int i = 1; i <= n; i ++) s[i].x = read() , s[i].y = read();
s[++ n].x = 0 , s[n].y = 0;//将四个顶点设为障碍点
s[++ n].x = 0 , s[n].y = w;
s[++ n].x = l , s[n].y = 0;
s[++ n].x = l , s[n].y = w;
int x1 , x2 , y1 , y2;//x1为左边界,x2为右边界,y1为下边界,y2为上边界
//从左往右搜
sort(s + 1 , s + n + 1 , cmp1);
for(int i = 1; i <= n; i ++)
{
x1 = s[i].x , y1 = 0 , y2 = w;
for(int j = i + 1; j <= n; j ++)
{
x2 = s[j].x;
ans = max(ans , (x2 - x1) * (y2 - y1));
if(s[j].y < s[i].y) y1 = max(y1 , s[j].y);//更新上下边界
else y2 = min(y2 , s[j].y);
}
}
//从右往左搜
for(int i = n; i >= 1; i --)
{
x1 = s[i].x , y1 = 0 , y2 = w;
for(int j = i - 1; j >= 1; j --)
{
x2 = s[j].x;
ans = max(ans , (x1 - x2) * (y2 - y1));
if(s[j].y < s[i].y) y1 = max(y1 , s[j].y);
else y2 = min(y2 , s[j].y);
}
}
//处理特殊情况
sort(s + 1 , s + n + 1 , cmp2);
for(int i = 1; i <= n - 1; i ++)
{
ans = max(ans , l * (s[i + 1].y - s[i].y));
}
printf("%d" , ans);
return 0;
}