Feel Good(单调栈(比之前更加完善的模板))

79 篇文章 0 订阅
73 篇文章 0 订阅


Link:http://poj.org/problem?id=2796


Feel Good
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 11341 Accepted: 3125
Case Time Limit: 1000MS Special Judge

Description

Bill is developing a new mathematical theory for human emotions. His recent investigations are dedicated to studying how good or bad days influent people's memories about some period of life. 

A new idea Bill has recently developed assigns a non-negative integer value to each day of human life. 

Bill calls this value the emotional value of the day. The greater the emotional value is, the better the daywas. Bill suggests that the value of some period of human life is proportional to the sum of the emotional values of the days in the given period, multiplied by the smallest emotional value of the day in it. This schema reflects that good on average period can be greatly spoiled by one very bad day. 

Now Bill is planning to investigate his own life and find the period of his life that had the greatest value. Help him to do so.

Input

The first line of the input contains n - the number of days of Bill's life he is planning to investigate(1 <= n <= 100 000). The rest of the file contains n integer numbers a1, a2, ... an ranging from 0 to 10 6 - the emotional values of the days. Numbers are separated by spaces and/or line breaks.

Output

Print the greatest value of some period of Bill's life in the first line. And on the second line print two numbers l and r such that the period from l-th to r-th day of Bill's life(inclusive) has the greatest possible value. If there are multiple periods with the greatest possible value,then print any one of them.

Sample Input

6
3 1 6 4 5 2

Sample Output

60
3 5

Source



题意:给出一个区间,选择这段区间的某个子区间,使得在这段子区间内的元素最小值*这段区间所有元素之和最大。

编程思想:单调栈思想的经典应用。


AC code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#define PI acos(-1.0)
#define LINF 1000000000000000000LL
#define eps 1e-8
#define LL long long
#define MAXN 1000010 
using namespace std;
const int INF=0x3f3f3f3f;
struct Rec{
	LL h;
	LL cnt;//向左向右扩展的宽度 
	int l;//最左边位置 
	int r; //最右边位置 
	int id;
}r[MAXN];//r[i]表示以第i条柱状图能向左向右扩展的最大矩形 
Rec  st[MAXN];//单调递增栈 
int top,lid,rid;
LL ans;
LL sum[MAXN];
int main()
{
	int i,j,n,ni;
	while(scanf("%d",&n)!=EOF)
	{
		if(n==0)
			break;
		top=0;//栈顶指针(栈的大小) 
		sum[0]=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&ni);
			sum[i]=sum[i-1]+ni;
			r[i].h=ni;
			r[i].id=i;
			j=0;
			while(top!=0&&r[i].h<=st[top].h)//要入栈的元素不满足单调栈严格递增,删除栈中元素直到满足,
			{								//同时这些出栈的元素能向右扩展的宽度也确定下来了,可以直接更新 
				r[st[top].id].cnt+=j;//j表示能向右扩展的宽度 
				r[st[top].id].r=r[st[top].id].l+r[st[top].id].cnt-1;//更新最右边位置 
				j=r[st[top].id].cnt;
				top--;//出栈 
			}
			r[i].cnt=j+1;//更新当前矩形能向左扩展的宽度(因为是一直向右走,所以每次都能更新向左扩展的宽度) 
			r[i].l=i-r[i].cnt+1;//确定最左边位置 
			st[++top]=r[i];//入栈 
		}
		j=0;
		while(top!=0)
		{
			r[st[top].id].cnt+=j;//j表示能向右扩展的宽度 
			r[st[top].id].r=r[st[top].id].l+r[st[top].id].cnt-1;//更新最右边位置 
			j=r[st[top].id].cnt;
			top--;//出栈 
		}
		ans=-INF;
		for(i=1;i<=n;i++)
		{
			//r[i].r=r[i].l+r[i].cnt-1;
			if(ans<(sum[r[i].r]-sum[r[i].l-1])*r[i].h)
			{
				ans=(sum[r[i].r]-sum[r[i].l-1])*r[i].h;
				lid=r[i].l;
				rid=r[i].r;
			}
		}
		printf("%lld\n%d %d\n",ans,lid,rid);
	}
    return 0;
}


下面附上该解题思想的详细讲解(来自博客:http://www.cnblogs.com/ziyi--caolu/p/3151556.html


样例解释:

例如:

6
3 1 6 4 5 2

以4为最小值,向左右延伸,6 4 5  值为60.......

思路:解决完为这道题目,我才真正明白了单调栈的原理,它就是以某一个值为最小(最大)值,向这个值的两侧延伸,遇到大于它(小于它)的值,就将它延伸的范围扩大,当然,一般来说,要这样做的算法复杂度为o(n^2),但是借助栈这个玩意,维护其单调增(减),就可以在o(n)的时间复杂度解决这个问题。将一元素加入栈时,先判断它是否大于(小于)栈顶元素,若是大于(小于)栈顶元素,加入栈。(从这里开始只讲维护单调增栈)否则,将栈顶元素出栈,直到栈顶元素小于要加入栈的元素,在此过程中,需要维护向前延伸和向后延伸的问题,当要加入栈的元素之前有n个栈元素出栈,那么说明这n个出栈的元素都会大于或者等于要入栈的元素,此时,我们需要维护入栈元素可以向前延伸多少个元素(相当于记录它的前面有多少个元素比它大),而每个栈顶元素要向出栈了的元素延伸,因为在出栈了的元素一定是比它的大的元素(根据我维护的是单调增栈)......这样,就在o(n)的时间复杂度内解决了上述问题.........

例如:3 1 6 4 5 2

(3,1,1)  (1,2,2)  (6,3,3)  (4,4,4)  (5,5,5)  (2,6,6)

首先每个元素自己本身的前后延伸都为1,把3加入栈,1<3,把3出栈,用1的前延伸加上3的前延伸,如此变为(1,1,2),6<1,入栈,变成(1,1,2)(6,3,3),

4<6,将6出栈,4向前延伸,1向后延伸变成(1,1,3) (4,3,4) 

5>4,入栈,变成(1,1,3)(4,3,4)(5,5,5)

2<5,5出栈,2向前延伸,4向后延伸,变成(1,1,3)(4,3,5)                   2还未入栈(2,5,6)

2<4,4出栈,2向前延伸,1向后延伸,变成(1,1,5) (2,3,6).....

一次类推,会发现最大的结果在(4,3,5)这里这意味着,以4为最小值的区间范围为3————5,也就是6 4 5  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林下的码路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值