算法笔记:最大矩形面积

最大子矩形问题:在一个给定的矩形网格中有一些障碍点,要找出网格内部不包含任何障碍点,且边界与坐标轴平行的最大子矩形。

常用的算法有两个:

单调栈,时间复杂度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;
}

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值