一.题目描述
问题描述
有一个直方图,横轴长度为 n,第 i 列的高度为 h[i]。
请你求出在这个直方图中面积最大的子矩阵。
输入格式
第一行一个正整数 n。
第二行 n 个用空格隔开的非负整数,依次描述 h[1],h[2],…,h[n]。
输出格式
输出一行一个数,表示最大面积。
样例输入
5
2 3 3 3 2
样例输出
10
二.几种解题思路
1.暴力
枚举每一条可能的底边,计算出高,求出面积最大值,复杂度 O ( n 3 ) O(n^3) O(n3)
2.暴力算法邓公版
1)定义曲线上每个极小值点为一个卡位点,记为k(pivot)
2)每个卡位点左右两侧都有挡住他的点(比卡位点小的地方),记为
i
k
i_k
ik和
j
k
j_k
jk,此时围城的矩形的面积为一个极大值,面积为
H
[
k
]
∗
(
j
k
−
i
k
−
1
)
H[k]*(j_k-i_k-1)
H[k]∗(jk−ik−1)
3)枚举每一个卡位点,得到一系列极大值,即可找出矩形的最大值,该算法复杂度
O
(
n
2
)
O(n^2)
O(n2)
3.分治算法
定义一个区间最低lo,最高hi,卡位点k。
根据卡位点k,将一个区间划分为左右两侧,返回左区间最大值,右区间最大值和当前区间的值,这三个值中的最大值,递归调用即可得到结果
复杂度分析:
T ( n ) = { 2 ∗ T ( n 2 ) best T ( n − 1 ) worst + O ( n ) T(n) = \begin{cases} 2*T(\frac n2) &\text{best} \\ T(n-1) &\text{worst } \end{cases}+O(n) T(n)={2∗T(2n)T(n−1)bestworst +O(n)因此,最优时达到了O(nlogn),但是最坏情况为 O ( n 2 ) O(n^2) O(n2)
4.分治+查找表
做一个查找表(look up table),记录每一个区间中的最大值
复杂度分析:
查找:
O
(
1
)
O(1)
O(1)
构造:
O
(
n
2
)
O(n^2)
O(n2)
存储:
O
(
n
2
)
O(n^2)
O(n2)
构造和存储太复杂,不可取!
5.分治+线段树
查找:
O
(
l
o
g
n
)
O(logn)
O(logn)
构造:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
存储:
O
(
n
)
O(n)
O(n)
通过线段树,分治算法被优化到了O(nlogn)
6.单调栈
从左到右枚举直方图的每一列
维护一个栈底到栈顶高度单调上升的栈
若下一列小于栈顶,则弹出栈顶并更新答案
若下一列大于等于栈顶,则将该列压入栈
三.单调栈
int getAnswer(int n, int *height) {
int ans = 0;
stack<int> mystack;//一个单调栈
mystack.push(0);//height[0]=0,边界
//循环到n+1,因为height[n+1]=0,边界
for (int i = 1; i <= n+1; ++i)
{
while (!mystack.empty()&&height[mystack.top()]>height[i]) {
int nowHeight = height[mystack.top()];
mystack.pop();
int L = mystack.empty() ? -1 : mystack.top();
ans = max(ans, nowHeight*(i-L-1));
}
mystack.push(i);
}
return ans;
}
难顶