这两道题解法分别是单调栈和单调队列,其实感觉这两个算法没什么区别,都是借助单调性处理问题,在于及时排除不可能的选项,保持单调性,保证策略集合是最优解。
单调栈:
a[n+1] = p = 0;//初始化边界
for(int i=1;i<=n;i++) {
if(a[i]>s[p]) {//满足单调性直接进栈,w数组是一个策略集合
s[++p] = a[i],w[p] = 1;
} else {//不满足单调性则退栈直至满足单调性
int width = 0;//记录退栈的策略
while(s[p]>a[i]) {
width += w[p];
ans = max(ans,(ll)width*s[p]); //答案
p--;
}
s[++p] = a[i];//打破单调性的那个元素入栈
w[p] = width+1;
}
}
单调队列:
int l = r = 1;
q[1] = 0;
for(int i=1;i<=n;i++) {
while(l<=r&&q[l]<i-m) l++;
ans = max(ans,sum[i]-sum[q[l]]);
while(l<=r && sum[q[r]>=sum[i]]) r--;
q[++r] = i;
}
131. 直方图中最大的矩形
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int n;
int l[maxn],r[maxn],s[maxn],h[maxn];
void findleft(int l[]) {
int head = -1;
s[++head] = 0;
h[0] = h[n+1] = -1;
for(int i=1;i<=n;i++) {
while(h[s[head]]>=h[i]) head--;
l[i] = s[head];
s[++head] = i;
}
}
void findright(int r[]) {
int head = -1;
s[++head] = n+1;
h[0] = h[n+1] = -1;
for(int i=n;i>=1;i--) {
while(h[s[head]]>=h[i]) head--;
r[i] = s[head];
s[++head] = i;
}
}
int main()
{
while(cin>>n) {
if(n==0) break;
for(int i=1;i<=n;i++) cin>>h[i];
findleft(l);
findright(r);
ll ans = 0;
for(int i=1;i<=n;i++) {
ans = max(ans,((ll)r[i]-l[i]-1)*h[i]);
}
printf("%lld\n",ans);
}
}
135. 最大子序和
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
int sum[maxn],q[maxn];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) {
int x;
cin>>x;
sum[i] = sum[i-1]+x;
}
int l = 1,r = 1;
q[1] = 0;
int ans = -0x3f3f3f3f;
for(int i=1;i<=n;i++) {
while(l<=r&&q[l]<i-m) l++;
ans = max(ans,sum[i]-sum[q[l]]);
while(l<=r&&sum[q[r]]>=sum[i]) r--;
q[++r] = i;
}
cout<<ans;
}