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

传送门POJ 2796

 

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

 

Sample Input

6
3 1 6 4 5 2

Sample Output

60
3 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;
}

 

 

 

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值