这题是典型的单调栈的题目。这里说说我对单调栈的理解:单调栈就是在一个栈里面维持单调的性质,即单调增或者单调递减。如果是单调增的话,准备入栈的元素和栈顶作比较,如果大于
栈顶,就入栈;否则就将栈顶元素退栈,在用新的栈顶元素和入栈元素比较,直到入栈元素大于栈顶元素。
这道题就是求出一个数在一个最大的区间内使得该数是区间内最小。所以需要求出每一个数所能扩展区间的右端点和左端点。单调栈就是用来维护这两个端点的。
在一个准备入栈的数和栈顶元素比较时,如果栈顶元素较小而退栈,此时退栈的数的右端点就是准备入栈的数的位置的前一位,而左端点是其在栈中的下一个元素的位置的后一位(当然需要
留意数组中的第一个和最后一个元素的处理)。当栈只剩下一个元素时,最后一个元素的左端点就是1.
#include<iostream> #include<cmath> #include<cstring> #include<algorithm> #include<cstdio> #include<list> #include<stack> using namespace std; struct pnt { int ls,rs,val,biao; //ls是左端点,rs是右端点,val是值,biao代表位置 }a[100050]; long long sum[100050]; void init(int n) { sum[0]=0; for(int i=1;i<=n;i++) { a[i].biao=i; sum[i]=sum[i-1]+a[i].val; } } stack<pnt> sta; void work(int n) { sta.push(a[1]); int minn,tem; pnt top,final; bool push_not; for(int i=2;i<=n;i++) { push_not=0; minn=sta.top().val; tem=a[i].val; if(tem>minn) sta.push(a[i]); //大于栈顶就入栈 else //否则,栈顶元素退栈 { while(!sta.empty()) { if(tem<=sta.top().val) { top=sta.top(); sta.pop(); a[top.biao].rs=i-1; if(sta.empty()) a[top.biao].ls=1; else a[top.biao].ls=sta.top().biao+1; } else { sta.push(a[i]);push_not=1;break;} } if(push_not==0) sta.push(a[i]); } } final=sta.top(); sta.pop(); a[final.biao].rs=n; if(sta.empty()) a[final.biao].ls=1; else a[final.biao].ls=sta.top().biao+1; while(!sta.empty()) //最后符合单调性质的栈,右端点都是数组的最后一位 { top=sta.top(); sta.pop(); if(sta.empty()) { a[top.biao].ls=1; a[top.biao].rs=final.biao; } else { a[top.biao].ls=sta.top().biao+1; a[top.biao].rs=final.biao; } } } int main() { int n; long long maxn=-1;//在这道题中maxn要初始为小于0的数 int ls,rs; cin>>n; for(int i=1;i<=n;i++) { scanf("%d",&a[i].val); } init(n); work(n); for(int i=1;i<=n;i++) { //cout<<"a["<<i<<"].ls="<<a[i].ls<<" rs="<<a[i].rs<<" val="<<a[i].val<<endl; long long tem=(sum[a[i].rs]-sum[a[i].ls-1])*a[i].val; if(tem>maxn) { maxn=tem;ls=a[i].ls; rs=a[i].rs;} } //cout<<biao<<endl; cout<<maxn<<endl; cout<<ls<<' '<<rs<<endl; }