poj2796--Feel Good(单调栈求区间问题)

题意:区间内数的总和 乘上 区间内的最小值 为心情值,求心情值的最大值

思路:虽然知道是单调栈但是想了好久都没想出来怎么用到单调栈,后来终于给我推出来了,我用图解的形式一步步告诉你

讲解之前先放上单调栈的概念,已经知道的可以忽略~

单调栈指栈中的元素从上往下看都是按一定的大小排下来的。所以一个元素进栈时要先让一些元素出栈才能再进站。

这里我用单调递增栈做个例子,即从栈顶往下看是越来越大的。

有一群数字2,9,4,7,3,10

栈为空,2进栈、

9要进栈,但是2比9小,故2先出栈,9再进栈。

4进栈,没问题,9比4大。

7要进栈,但是4比7小,故4出栈,9比7大,不必出栈,故7进栈。

3比7小,进栈

10要进栈,故3、7、9出栈,10进栈。

整个栈的变化为NULL->2->9->9,4->9,7->9,7,3->10

 

首先,对于这题,最重要的一点是用最小值表示区间,而不是用区间表示区间。

就拿3,1,6,4,5,2这个例子来讲,先放上图,在配上说明

3首先入栈,以3(3)表示,意为 当前以3为最小值的区间中的总和为3

随后要放入1,1比3要小,那么也就意味着 以3为最小值的区间已经结束了,因为3的左右都是比它要小的数字,所以无法再扩展出去,所以3要出栈,出栈时计算3*3,更新max,同时1入栈,表示为1(4),即以1为最小值的区间中的数的和为4.

然后再要放入6,6比1大,故直接放入,栈中表示为1(4),6(6),6(6)指以6为最小值的区间中的总和为6.

然后再放入4,因为6的左边是1,6又比4大,所以以6为最小值的区间已经结束,故6出栈,出栈时计算6*6,更新max,4入栈,为4(10)。

5入栈,栈中为1(4),4(10),5(5)。

再放入2,因为2比5小,5的区间已结束,5出栈,计算5*5. 5出栈后还有个4,4的区间范围是包括5那部分的,所以4出栈时要计算4*(10+5)。

现在所有数字都已经放过了。栈中还剩下1(4)和2(17)。这两个区间的好心情值还没算过,依次出栈,计算2*17,

然后1出栈,因为1是包括2的那部分的,所以计算1*(4+17)。

最终得知答案为4*(10+5)=60那次

 

 

#include<iostream>
#include<string>
#include<cmath>  
#define M 100005
using namespace std;

int a[M];
struct stack
{
	int top,num[M],l[M];        //num指数字,l指它的最左端,sum指已经覆盖的值的总和
	long long sum[M];
};
stack s;

int main()
{
	int n,i,j,ll;
	long long k;
	cin>>n;
	s.top=0;   //初始化栈
	long long  max=-1,
	int ansl,ansr;     //作为答案的左端和右端
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		k=0;
		ll=i;         //最左端暂定为它自己
		while(s.top!=0&&s.num[s.top]>=a[i])     //如果非空并且栈顶端的值比它大,出栈
		{
			k+=s.sum[s.top];                 //k保存出栈的sum的总和
			ll=s.l[s.top];                  //最左端变小(向左移动了)
			if(k*s.num[s.top]>max) { max=k*s.num[s.top];ansl=s.l[s.top];ansr=i-1;}  //最大值更新
			s.top--;
		}
		s.sum[++s.top]=k+a[i];     //入栈
		s.num[s.top]=a[i];          
		s.l[s.top]=ll;
	}
     //所有入栈操作已经完结,但是单调栈内元素的还未进行过与最大值的比较,一个个出栈
	k=0;              
	while(s.top!=0)
	{
		k+=s.sum[s.top];
		if(s.num[s.top]*k>max)
		{
			max=s.num[s.top]*k;
			ansl=s.l[s.top];
			ansr=n;
		}
		s.top--;
	}
	printf("%lld\n%d %d\n",ans,ansl,ansr);    //记住lld
}


 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值