POJ 2796 Feel Good 单调栈的应用 代码详解

来源:互联网 发布:淘宝童装模特拍摄价格 编辑:程序博客网 时间:2024/05/16 09:53

传送门:POJ 2796


题目大意:给出一组数字,求一区间,使得区间元素和乘以区间最小值最大,结果要求给出这个最大值和区间的左右端点。


Sample Input

63 1 6 4 5 2

Sample Output

603 5

前置技能:单调栈的原理及应用。


思路:求序列中的最小值乘以这个序列的和的值最大,是典型的单调栈的应用。一般的思路是求每个数字所在的能使其值为区间最小值的最大区间,然后求出区间元素和乘以该值并更新结果的最大值。普通的做法时间复杂度为O(n^2),用单调栈可以达到O(n)。


具体实现用一个单调递减栈,如果栈为空或入栈元素大于等于栈顶元素,则入栈,否则将破坏栈的单调性,则将栈顶元素出栈,直到栈为空或碰到第一个大于等于入栈元素的元素。然后将最后一次出栈的栈顶元素入栈,并将其像左右拓展,并更新其对应的值。

由于维护单调栈会改变原数组的值,同时为了方便求区间元素值,我们设置一个sum数组,记录前缀和。

我们将原数组的最后一个值设为最小值,以方便最后将栈内所有元素出栈。


注意最后一次出栈的栈顶元素就是当前入栈元素可以向左拓展到的最大距离。


#include<stdio.h>#include<iostream>#include<stack>using namespace std;typedef long long LL;int main(){int i,n,pos1,pos2; //pos1和pos2记录区间的开始和结束位置 //tmp为临时变量,记录区间内的和;top指向栈顶元素;ans为结果;sum为前缀和 LL tmp,top,ans,a[100010],sum[100010];stack<int> st; //单调栈,记录元素位置 while(~scanf("%d",&n)){while(!st.empty()) st.pop(); //清空栈 sum[0]=0;for(i=1;i<=n;i++){scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i]; //计算前缀和 }a[n+1]=-1; //将最后一个设为最小值,以最后让栈内元素全部出栈 ans=0;for(i=1;i<=n+1;i++){if(st.empty()||a[i]>=a[st.top()]){ //如果栈为空或入栈元素大于等于栈顶元素,则入栈 st.push(i);}else {while(!st.empty()&&a[i]<a[st.top()]){ //如果栈非空并且入栈元素小于栈顶元素,则将栈顶元素出栈 top=st.top();st.pop();tmp=sum[i-1]-sum[top-1]; //计算区间内元素和 tmp*=a[top]; //计算结果 if(tmp>=ans) { //更新最大值并记录位置 ans=tmp;pos1=top;pos2=i;}}st.push(top); //将最后一次出栈的栈顶元素入栈 a[top]=a[i]; //将其向左向右延伸并更新对应的值 }}printf("%lld\n",ans);printf("%d %d\n",pos1,pos2-1);}return 0;}

原创粉丝点击