b_51_面积最大的矩形=柱状图 & 好子数组的最大值(暴力 / 单调栈优化)
面积最大的矩形(柱状图)
有一个正整数的数组(数组值为柱子的高度),化为直方图,求此直方图包含的最大矩形面积。
暴力
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
ll n,a[N];
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n; for (int i=0; i<n; i++) cin>>a[i];
ll ans=0;
for (int i=1; i<n-1; i++) {
ll l, r;
for (l=i-1; l>=0; l--)if (a[l]<a[i]) break; //在左边找第一个小于a[i]的
for (r=i+1; r<n; r++) if (a[r]<a[i]) break; //在右边找第一个小于a[i]的
ans=max(ans, (r-l-1)*a[i]);
}
cout<<ans;
return 0;
}
暴力法中我发现需要在每一个位置i的左&右找第一个小于当前元素a[i]的位置,这一步可以用单调栈预处理,栈顶则存的是当前位置左/右边第一个小于自己的位置
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
ll n,h[N],l[N],r[N];
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
ll ans=0; stack<int> st;
cin>>n; for (int i=1; i<=n; i++) cin>>h[i];
h[0]=-1, st.push(0); //添加一个0后最左边的柱子就可以有底部长度为1(自己本身)
for (int i=1; i<=n; i++) {
while (!st.empty() && h[st.top()]>=h[i]) st.pop();
l[i]=i-st.top();
st.push(i);
}
st=stack<int>(), st.push(n+1); //添加一个n+1后最右边的柱子就可以有底部长度为1(自己本身)
for (int i=n; i>=1; i--) {
while (!st.empty() && h[st.top()]>=h[i]) st.pop();
r[i]=st.top()-i;
st.push(i);
}
for (int i=1; i<=n; i++) ans=max(ans, h[i]*(l[i]+r[i]-1));
cout<<ans;
return 0;
}
好子数组的最大分数
子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1) 。一个 好 子数组的两个端点下标需要满足 i <= k <= j 。请你返回 好 子数组的最大可能 分数 。
思路:单调栈O(n)求出lmin、rmin(左、右边第一个比自己小的位置),加个判断条件即可
class Solution {
public:
int maximumScore(vector<int>& A, int k) {
int n=A.size();
A.insert(A.begin(), 0), A.insert(A.end(), 0);
vector<int> lm(n+1,0), rm(n+1,0);
stack<int> st; st.push(0);
for (int i=1; i<=n; i++) {
while (!st.empty() && A[st.top()] >= A[i]) st.pop();
lm[i]=st.top();
st.push(i);
}
st=stack<int>(); st.push(n+1);
for (int i=n; i; i--) {
while (!st.empty() && A[st.top()] >= A[i]) st.pop();
rm[i]=st.top();
st.push(i);
}
k++;
int ans=0;
for (int i=1; i<=n; i++) {
if (lm[i] < k && k < rm[i])
ans=max(ans, (rm[i]-lm[i]-1) * A[i]);
}
return ans;
}
};