POJ 2796:Feel Good 单调栈经典题

Feel Good
Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 11626   Accepted: 3212
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

题意是给出一个序列,要求的是一个区间,这个区间的最小值乘以这个区间数字的和 是最大值。求这个最大值与这个区间。

看了一晚上,终于算是弄懂了这个代码。实际上这个题目就是要对每一个节点进行扩展,这样扩展的话,复杂度是O(n^2)。减少时间复杂度要用单调栈,单调栈处理的问题就是对每一个节点进行扩展的问题,这个题目要维护的是一个单调递减栈,即从栈顶元素到栈底元素,值是单调递减的,即栈顶元素的值始终是栈的最大值。然后每一个值有属于自己的区间,这个区间目的是为了记录之后的元素向前延伸的用处。

向后延伸就靠从1到n扫描元素,(维护单调递减栈)这样当扫描的元素大于栈顶元素时,直接入栈。

当扫描的元素等于栈顶元素时,不记录,只将区间延伸到后面。

当扫描的元素小于栈顶元素时,这时要计算栈内当前的值。因为扫描的元素时小于栈顶元素的,要求的是一个区间的最小值,所以栈内那些大于该元素的值你会发现没有用处了,只需要将它们的那些区间留下来就对了,这就是向前扩展。

拿题目的sample举例子:

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(7,7)后面加一个最小值,为了最后计算栈内元素使用。

先是3入栈。栈内元素 3(1,1)

1<3,首先计算一下栈内元素的值,记录下来。然后要把栈内大于1的全部弹出来,但是把它们的区间留下,栈内就变成了1(1,2)。实际上此时就会知道(1,2)这段区间之内的最小值是1。

6>1,直接入栈,栈内元素变为1(1,2),6(3,3)。

4<6,将6弹出,弹出之前计算值。然后栈内就变为1(1,2),4(3,4)。

5>4,直接入栈。栈内元素是1(1,2),4(3,4),5(5,5)。会发现因为5没有办法向前扩展了所以会知道5只能够在(5,5)的区间内最小,所以说站内元素是在自己区间的左端点与栈顶元素的右端点,这段区间之内满足着最小值的关系。1是在(1,5)这段区间内最小,4是在(3,5)这段区间内最小。这些值都会在碰到扫描的元素小于该元素时计算,记录下来,就是这样单调栈完成了对每一个元素进行左右扩展的目的。

2<5,2<4。要把5(5,5) 4(3,4)分别弹出,它们走之前要计算各自区间的值。

最后是-1,目的就是要将栈内所有元素弹出,计算每一个元素左右扩展的值。


单调栈的知识还有很多,自己还是有很多要去努力理解的。先写这么多,后面在继续添加理解。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#pragma warning(disable:4996)
using namespace std;

#define N 100001

int a[N], lef[N], stack[N], top;
long long sum[N];

int main()
{
	//freopen("i.txt","r",stdin);
	//freopen("o.txt","w",stdout);

	long long ans = -1, tmp;
	int i, j, n;
	int ll, rr;

	scanf("%d", &n);
	memset(sum, 0, sizeof(sum));
	for (i = 1; i <= n; i++)
	{
		scanf("%d", a + i);
		sum[i] = sum[i - 1] + a[i];
	}
	a[++n] = -1;
	top = 0;
	for (i = 1; i <= n; i++)
	{
		if (top == 0 || a[i] > a[stack[top - 1]])
		{
			stack[top++] = i;
			lef[i] = i;
			continue;
		}
		if (a[i] == a[stack[top - 1]])
			continue;
		while (top >= 1 && a[i] < a[stack[top - 1]])
		{
			--top;
			tmp = 1LL*a[stack[top]] * (sum[i - 1] - sum[lef[stack[top]] - 1]);

			if (tmp > ans)
			{
				ll = lef[stack[top]];
				rr = i - 1;
				ans = tmp;
			}
		}
		lef[i] = lef[stack[top]];
		stack[top++] = i;
	}

	printf("%lld\n%d %d\n", ans, ll, rr);
	//system("pause");
	return 0;
}





版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://www.cnblogs.com/lightspeedsmallson/p/4899517.html

你可能感兴趣的:(POJ 2796:Feel Good 单调栈经典题)