单调栈:
字面意思就是一个结构栈其中的元素是单调的(严格递增或递减)
对于一个新元素的加入,会把栈顶所有破坏单调性的元素给“弹走”,例如:【7,5,3,新加入一个元素5,会把原栈中的5,3“弹走”,这时形成新的栈为【7,5
用途:
- 最基础的应用就是给定一组数,针对每个数,寻找它和它右边第一个比它大的数之间有多少个数。
- 给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。
- 给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大
通常来讲,单调栈更倾向于一维数组的问题,或者是多维数组可以转化为一维数组的问题。多存储元素坐标而非元素值。
问题多见于寻找数组元素左区间或右区间最大最小问题,或者找出元素的两边界问题。
问题 J: 小C的数学问题
时间限制: 1 Sec 内存限制: 128 MB
提交: 596 解决: 150
[提交] [状态] [命题人:外部导入]
题目描述
小C是个云南中医学院的大一新生,在某个星期二,他的高数老师扔给了他一个问题。
让他在1天的时间内给出答案。
但是小C不会这问题,现在他来请教你。
请你帮他解决这个问题。
有n个数,每个数有权值。
数学老师定义了区间价值为区间和乘上区间内的最小值。
现在要你找出有最大区间价值的区间是什么,并输出区间价值。
输入
每个输入文件只包含单组数据。
第一行一个整数n。(1 <= n <= 100000)
第二行n个整数a_1,a_2,...,a_n。(0 <= a_i <= 1000000)
输出
第一行输出一个整数,表示最大的区间价值。
第二行输出两个整数,表示区间的起点和终点。
保证答案唯一。
样例输入
复制样例数据
6
10 1 9 4 5 9
样例输出
108
3 6
题解:题意很简单,就是求出最大的【区间最小值乘上区间和】,当然用到了前缀和的知识
#include<bits/stdc++.h>
const double PI=acos(-1);
const int INF=1e9+7;
const int xmax=1e6+7;
const int xmin=-1e6+7;
typedef long long ll;
using namespace std;
int n,a[xmax];
ll sum[xmax]; ///前缀和
int l[xmax]; ///左区间
int r[xmax]; ///右区间
ll ans,L,R;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i]; ///前i项的前缀和
}
for(int i=1;i<=n;i++){ ///找i的左区间
int j=i;
while(j>1&&a[j-1]>a[i]){j=l[j-1];}
l[i]=j;
}
for(int i=n;i;i--){ ///找i的右区间
int j=i;
while(j<n&&a[j+1]>=a[i]){j=r[j+1];}
r[i]=j;
}
///以上两个for循环找的是对每一个i来说,i作为一个区间的最小值的【l[i],r[i]】区间
ans=-1;
for(int i=1;i<=n;i++){
///遍历每一个以i为最小值的区间,求出最大值
ll t=a[i]*(sum[r[i]]-sum[l[i]-1]);
if(t>ans){
ans=t;
L=l[i];
R=r[i];
}
}
printf("%lld\n%lld %lld\n",ans,L,R);
return 0;
}
以下是学习单调栈过程中。借鉴或引用了一些结论的博客
https://blog.youkuaiyun.com/zuzhiang/article/details/78134247
https://blog.youkuaiyun.com/renzijing/article/details/89024366
https://www.cnblogs.com/Atanisi/p/7563178.html