2019 GDUT Winter Training V ( 算法优化) C - Feel Good
poj传送门
专题传送门
题目大意:
给出一组数字,求一区间,使得区间元素和乘以区间最小值最大,结果要求给出这个最大值和区间的左右端点。
题目分析:
用一个单调递减栈,如果栈为空或入栈元素大于等于栈顶元素,则入栈,否则将破坏栈的单调性,则将栈顶元素出栈,直到栈为空或碰到第一个大于等于入栈元素的元素。然后将最后一次出栈的栈顶元素入栈,并将其像左右拓展,并更新其对应的值。
由于维护单调栈会改变原数组的值,同时为了方便求区间元素值,我们设置一个sum数组,记录前缀和。
我们将原数组的最后一个值设为最小值,以方便最后将栈内所有元素出栈。
注意:最后一次出栈的栈顶元素就是当前入栈元素可以向左拓展到的最大距离。
例如
我们要维护一个单调递增的栈,给出一组数11,4,8,5,13。我们模拟一下入栈过程,则如果入栈元素值小于栈顶元素值或者栈为空,则入栈;否则,则需要把比入栈元素小的元素全部出栈,因为如果入栈则会破坏栈的单调性。单调递减的栈反之。
11入栈时,栈为空,直接入栈,栈内元素为14。
4入栈时,栈顶元素11比4大,则入栈,栈内元素为11,4。
8入栈时,栈顶元素4比8小,则栈顶元素出栈,此时栈顶元素为11,比8大,则入栈,栈内元素为11,8。
5入栈时,栈顶元素8比5大,则入栈,栈内元素为11,8,5。
13入栈时,栈顶元素5比13小,5出栈,此时栈顶元素为8,仍比13小,栈顶元素8继续出栈,此时栈顶元素为11,仍比13小,11出栈,此时栈为空,13入栈,栈内元素为13。
这里给出伪代码
/*
* 本伪代码对应的是单调递减栈
*共n个元素,编号为0~n-1
*/
while(栈为空) 栈顶元素出栈; //先清空栈
a[n]=-1;
for(i=0;i<=n;i++)
{
if(栈为空或入栈元素大于等于栈顶元素) 入栈;
else
{
while(栈非空并且栈顶元素大于等于入栈元素)
{
栈顶元素出栈;
更新结果;
}
将最后一次出栈的栈顶元素(即当前元素可以拓展到的位置)入栈;
更新最后一次出栈的栈顶元素其对应的值;
}
}
代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <stack>
#define ll long long
using namespace std;
const int maxn = 1e5+5;
int emo[maxn];
ll sum[maxn];
stack<int> s;
int main(){
int days;cin>>days;
for(int i=1;i<=days;i++){
scanf("%d",&emo[i]);
sum[i]=sum[i-1]+emo[i];
}
emo[days+1]=-1;
ll maxemo=-1;
int pos1,pos2,fir;
for(int i=1;i<=days+1;i++){
if(s.empty()||emo[s.top()]<=emo[i]) s.push(i);
else{
while(!s.empty()&&emo[s.top()]>emo[i]){
fir=s.top();s.pop();
ll tmp=sum[i-1]-sum[fir-1];
tmp*=emo[fir];
if(tmp>maxemo){
maxemo=tmp;
pos1=fir;
pos2=i;
}
}
s.push(fir);
emo[fir]=emo[i];
}
}
printf("%lld\n",maxemo);
printf("%d %d\n",pos1,pos2-1);
return 0;
}