【直方图中最大的矩形】(单调栈) 【矩形牛棚】

直方图中最大的矩形

【题目链接】

https://www.acwing.com/problem/content/description/133/

【题目描述】在这里插入图片描述

【输入样例】

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

【输出样例】

8
4000

【思路】

可以想到要构成最大矩形的话,最朴实的办法就是,从每个高度向两边扩展,直到找到小于当前高度h[i]的左右边界,然后进行区间x高度即为每个高度所能扩展最大面具,然后遍历取最大值即可

【暴力解法】

#include <iostream>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int a[N];
LL res = 0;
int main() {
	int n;
	while(cin >> n, n) {
		for(int i = 0; i < n; i++)	scanf("%d", &a[i]);
		for(int i = 0; i < n; i++) {
			LL l = i, r = i;
			while(a[l] >= a[i])	l--;
			while(a[r] >= a[i])	r++;
			res = max(res, (r - l - 1) * (LL)a[i]);	
		}
		cout << res << '\n';
	}
	
	return 0;
}

这样明显会超时,想到优化,有什么办法可以快速找到边界范围的下标呢,可以省去循环查找的复杂步骤,可以想到如果是单调的,那么能直接锁定左边界范围,用单调栈来存储下标,使得单调栈维护的是一个下标数组,且对应高度呈上升

对应的,要想求有边界的话,我们只需反转数组重复操作一遍即可求出右边界

  • 注意:最后的数组是反转后的,故我们在求答案时对左边界为原数组下标的反向映射

【AC代码如下】

//根据上方思想,我们想办法快速找到每个数对应的l和r边界,可以想到用单调栈

#include <iostream>
#include <algorithm> 
using namespace std;
typedef long long LL;
const int N = 100010;

int n;
int h[N], l[N], r[N];	//分别第i个位置存储高度,可组矩阵的左边界位置or右边界位置 
int q[N];	//一个单调栈,存储下标,所对应元素高度上升排列

void get_range(int a[]) {
	//初始化一个哨兵,省去边界处理的麻烦操作
	h[0] = -1;
	int tt = 0;	//栈从0开始
	for(int i = 1; i <= n; i++) {
		while(h[q[tt]] >= h[i]) tt--;
		a[i] = q[tt];
		q[++ tt] = i;
	}
}
int main() {
	while(cin >> n, n) {
		for(int i = 1; i <= n; i++)	scanf("%d", &h[i]);
		//求边界位置(小于h[i]) 
		get_range(l);
		//反转数组,同样可求右边界范围 
		reverse(h + 1, h + 1 + n);
		get_range(r);
		
		LL res = 0;
		//注意此时数组已经反转,故i,j对应的位置发生改变
		for(int i = 1, j = n; i <= n; i++, j--) {
			//用j代表映射过去的反着的i
			res = max(res, (n + 1 - l[j] - r[i] - 1) * (LL)h[i]); 
		} 
		cout << res << endl;
		
	}
	return 0;
} 

要是觉得上方代码最后的下标绕的话,用下面的方法写也是一样

【AC代码】

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int h[N],q[N],l[N],r[N];
// h为输入中的n个数
// q为单调栈里
// l为每个数左侧离自己最近且比自己小的数
// r为每个数右侧离自己最近且比自己小的数
typedef long long LL;
int main(){
    int n;
    while(scanf("%d",&n),n){
        for(int i = 1;i <= n;i ++) scanf("%d",&h[i]); // 输入
        h[0] = h[n + 1] = -1; // 为了防止越界,把0和n + 1位置赋值为-1
        int tt = -1; // 将栈长度赋值为-1
        q[++ tt] = 0; // 往栈里压入数0
        for(int i = 1;i <= n;i ++){ // 查找每个数左侧离自己最近且比自己小的数
            while(h[q[tt]] >= h[i]) tt --;
            l[i] = q[tt];
            q[++ tt] = i;
        }
        //这里重新赋值栈,反着来
        tt = -1;
        q[++ tt] = n + 1;	//因为存的是位置下标
        for(int i = n;i;i --){ // 查找每个数右侧离自己最近且比自己小的数
            while(h[q[tt]] >= h[i]) tt --;
            r[i] = q[tt];
            q[++ tt] = i;
        }
        LL res = 0; // 答案
        for(int i = 1;i <= n;i ++) res = max(res,(LL)h[i] * (r[i] - l[i] - 1));
        printf("%lld\n",res);
    }
    return 0;
}

矩形牛棚

【题目链接】

https://www.acwing.com/problem/content/description/1415/

[题目描述]

在这里插入图片描述

思路:在这里以下边界为参照,枚举每一列的情况,对每一块的可组成方块进行累加(前缀和高度),这样的话就类似于一个二维的最大矩形的求解过程

【AC代码】

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 3010;
int n, m, p;
int hh[N][N], st[N][N];
int q[N], l[N], r[N];

int main()
{
    cin >> n >> m >> p;
    int a, b;
    while(p --)
    {
        cin >> a >> b;
        st[a][b] = 1;
    }
    for(int i = 1;i <= n; i ++)
    {
        for(int j = 1 ; j <= m; j++)
        { 
            if(st[i][j] == 0)   hh[i][j] = hh[i - 1][j] + 1;           
        }
    }
    int res = 0;
    int tt = -1;
    for(int i = 1;i <= n;i++)
    {
        q[++ tt] = 0;
        hh[i][0] = hh[i][m+1] = -1;
        for(int j=1;j<=m;j++)
        {
            while(hh[i][j]<=hh[i][q[tt]])tt--;
            l[j]=q[tt];
            q[++tt]=j;
        }
        tt = -1, q[++tt] = m + 1;
        for(int j=m;j;j--)                          //记得j--,而不是加加,因为这是从后面遍历找右边的。
        {
            while(hh[i][j]<=hh[i][q[tt]])tt--;
            r[j]=q[tt];
            q[++tt]=j;
        }
        for(int j=1;j<=m;j++)
        res=max(res, hh[i][j]*(r[j]-l[j]-1));       //不断的比较面积大小,取最大的。
    }
    cout<<res<<endl;

    return 0;
}


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用单调栈求解直方图最大矩形面积的C++完整代码: ```c++ #include <iostream> #include <stack> using namespace std; int getMaxArea(int hist[], int n) { stack<int> stk; int maxArea = 0, area = 0, i = 0; while (i < n) { if (stk.empty() || hist[stk.top()] <= hist[i]) { stk.push(i++); } else { int top = stk.top(); stk.pop(); area = hist[top] * (stk.empty() ? i : i - stk.top() - 1); if (area > maxArea) { maxArea = area; } } } while (!stk.empty()) { int top = stk.top(); stk.pop(); area = hist[top] * (stk.empty() ? i : i - stk.top() - 1); if (area > maxArea) { maxArea = area; } } return maxArea; } int main() { int hist[] = {6, 2, 5, 4, 5, 1, 6}; int n = sizeof(hist) / sizeof(hist[0]); cout << "The maximum area of the histogram is " << getMaxArea(hist, n) << endl; return 0; } ``` 在上述代码,我们使用一个单调栈来维护直方图的柱子。具体来说,我们从左到右遍历直方图的柱子,如果当前柱子的高度不小于栈顶柱子的高度,则将当前柱子入栈;否则,我们弹出栈顶柱子,计算以该柱子高度为最小高度的矩形面积,更新最大面积值。在弹出栈顶柱子之后,当前柱子继续与栈顶柱子比较,直到当前柱子的高度不小于栈顶柱子的高度或者栈为空为止。 当所有的柱子都遍历完成后,我们需要将栈剩余的柱子依次弹出,并根据弹出柱子的高度计算以该高度为最小高度的矩形面积,再次更新最大面积值。 最后,我们返回最大面积值即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值