UVa 1619 Feel Good

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 day was. 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 input will contain several test cases, each of them as described below. Consecutive test cases are separated by a single blank line.


The first line of the input file contains n - the number of days of Bill's life he is planning to investigate (1$ \le$n$ \le$100000) . The rest of the file contains n integer numbers a1a2,..., an ranging from 0 to 106 - the emotional values of the days. Numbers are separated by spaces and/or line breaks.

Output 

For each test case, the output must follow the description below. The outputs of two consecutive cases will be separated by a blank line.


On the first line of the output file print the greatest value of some period of Bill's life.

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 the shortest one. If there are still several possibilities, print the one that occurs first..

Sample Input 

6 
3 1 6 4 5 2

Sample Output 

60 
3 5
 
#include <cstdio>
#include <stack>
using namespace std;
// 原数据
int array[100010];
// left[i]代表在第i个数字左边第一个比其小的数字位置
int left[100010];
// right[i]代表在第i个数字右边第一个比其小的数字位置
int right[100010];

// s[i]代表array[0]+...+array[i]
long long s[100010];

int main()
{
	int n;
	int count = 0;
	while(scanf("%d", &n) == 1)
	{
		// 读入数据
		for(int i = 0; i < n; i++)
		{
			scanf("%d", &array[i]);
			left[i] = -1;
			right[i] = n;
			if(i == 0)
				s[i] = array[i];
			else
				s[i] = s[i-1] + array[i];
		}

		// 计算每个数字的左右两边第一个比其小的数字位置
		stack<int> my_stack;

		my_stack.push(0);

		for(int i = 1; i < n; i++)
		{
			while(my_stack.size() > 0 && array[my_stack.top()] > array[i])
			{
				right[my_stack.top()] = i;
				my_stack.pop();
			}
			my_stack.push(i);
		}

		my_stack = stack<int>();

		my_stack.push(n-1);
		for(int i = n-2; i >= 0; i--)
		{
			while(my_stack.size() > 0 && array[my_stack.top()] > array[i])
			{
				left[my_stack.top()] = i;
				my_stack.pop();
			}
			my_stack.push(i);
		}			


/*		for(int i = 0; i < n; i++)
		{
			printf("(%d:left:%d right:%d sum:%lld)\n", array[i], left[i], right[i], s[i]);
		}
		printf("\n");
*/		// 检查所有元素能够得到的值
		long long max_sum = -1;
		int begin = -1;
		int end = -1;
		for(int i = 0; i < n; i++)
		{
			long long this_sum;
			if(array[i] == 0)	
			{
				this_sum = 0;
				left[i] = i-1;
				right[i] = i+1;
			}
			else
			{
				this_sum = s[right[i]-1] - s[left[i]+1] + array[left[i]+1];
				this_sum = this_sum * array[i];
			}
			if(this_sum > max_sum)
			{
				max_sum = this_sum;
				begin = left[i]+1;
				end = right[i]-1;	
			}
			else if(this_sum == max_sum)
			{
				int t_begin = left[i]+1;
				int t_end = right[i]-1;
				if((t_end-t_begin+1) < (end-begin+1))
				{
					end = t_end;
					begin = t_begin;
				}
				else if((t_end-t_begin+1) == (end-begin+1))
				{
					if(t_begin < begin)
					{
						begin = t_begin;	
						end = t_end;
					}	
				}
			}	
		}		
		if(count > 0)
			printf("\n");
	
		printf("%lld\n", max_sum);
		printf("%d %d\n", begin+1, end+1);	
		count++;
	}	
	return 0;	
}

这是一道好题。首先观察到数据的范围,n可以达到100000. 用枚举的O(n^2)做会超时。于是想能不能把复杂度降到O(n*logn).
想了半天好像没有办法。想到针对每个数,向其左右扩张,观察何时会有最大值。但是遇到比它小的数不知道是否该停止,因为再遇到其他数可能会有最大值。
这时应该换个角度,以某个数为最小值进行左右扩张。这样,并不会错过最优解,因为最优解总是以某个数为最小值的最优解。
记录每个数向左右扩张的话遇到的第一个比其小的数(对于0不需要左右扩张). 这可以通过一个栈来完成,很巧妙。O(n).
然后枚举所有数,和可以事先计算好,O(n).
总复杂度O(n).
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值