Feel Good (前缀和+单调队列)

来源:互联网 发布:基础设施即服务 云计算 编辑:程序博客网 时间:2024/05/19 08:24

Feel Good

Time Limit : 6000/3000ms (Java/Other)   Memory Limit : 131072/65536K (Java/Other)
Total Submission(s) : 82   Accepted Submission(s) : 21
Special Judge
Problem 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<sup>6</sup> - 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
63 1 6 4 5 2
 

Sample Output
603 5
 

Source
PKU

题意:求一串数字中某一段连续和乘于这一段最小的数的乘积的最大值。

思路:

单调队列,运用前缀和来解决,分别把某个数看做最小,然后分别找出左右两边大于他的数,记录位置。注意一个细节就是 i~j的和用前缀和表示是sum[j]-sum[i-1];。还有就是用cin会超时。

代码:

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>#include <cmath>#include <string>#include <map>#include <stack>#include <vector>#include <set>#include <stdio.h>#include <queue>#include <iomanip>#define maxn 100010#define mod 1000000007#define INF 0x3f3f3f3f#define exp 1e-6#define pi acos(-1.0)using namespace std;long long int a[maxn],sum[maxn],q[maxn],l[maxn],r[maxn];int main(){int n;int tail;while(cin>>n&&n){    memset(sum,0,sizeof(sum));    int flag=0;        for(int i=1;i<=n;i++){scanf("%I64d",&a[i]);l[i]=i;r[i]=i;sum[i]=sum[i-1]+a[i];if(flag==0)            {                if(sum[i]>0)                    flag=1;            }}if(flag==0) //全为0时的情况。        {            printf("0\n1");            printf(" %d\n",n);            continue;        }tail=0;for(int i=1;i<=n;i++)   //在左边找{while(tail && a[i]<=a[q[tail]])tail--;                l[i]=tail==0?1:q[tail]+1;   //q[tail]存的数值肯定小于a[i],所以应该在向由移一位                q[++tail]=i;}tail=0;for(int i=n;i>=1;i--)  //在右边找{while(tail && a[i]<=a[q[tail]])tail--;             r[i]=tail==0?n:q[tail]-1;//q[tail]存的数值肯定大于a[i],所以应该在左由移一位            q[++tail]=i;}long long int maxx=-1;int ll=0,rr=0;for(int i=1;i<=n;i++)        {            if((sum[r[i]]-sum[l[i]-1])*a[i]>maxx){                maxx=(sum[r[i]]-sum[l[i]-1])*a[i];                ll=l[i];                rr=r[i];            }        }printf("%I64d\n",maxx);printf("%d %d\n",ll,rr);}return 0;}


相似题目:

Max Sum of Max-K-sub-sequence

地址:http://acm.hdu.edu.cn/showproblem.php?pid=3415

Max Sum of Max-K-sub-sequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8435    Accepted Submission(s): 3096


Problem Description
Given a circle sequence A[1],A[2],A[3]......A[n]. Circle sequence means the left neighbour of A[1] is A[n] , and the right neighbour of A[n] is A[1].
Now your job is to calculate the max sum of a Max-K-sub-sequence. Max-K-sub-sequence means a continuous non-empty sub-sequence which length not exceed K.
 

Input
The first line of the input contains an integer T(1<=T<=100) which means the number of test cases.
Then T lines follow, each line starts with two integers N , K(1<=N<=100000 , 1<=K<=N), then N integers followed(all the integers are between -1000 and 1000).
 

Output
For each test case, you should output a line contains three integers, the Max Sum in the sequence, the start position of the sub-sequence, the end position of the sub-sequence. If there are more than one result, output the minimum start position, if still more than one , output the minimum length of them.
 

Sample Input
46 36 -1 2 -6 5 -56 46 -1 2 -6 5 -56 3-1 2 -6 5 -5 66 6-1 -1 -1 -1 -1 -1
 

Sample Output
7 1 37 1 37 6 2-1 1 1
 

Author
shǎ崽@HDU
 

Source
HDOJ Monthly Contest – 2010.06.05
 

Recommend
lcy   |   We have carefully selected several similar problems for you:  3423 3417 3418 3419 3421



题意:给你一个循环序列{An},让你找出长度不大于K的连续子序列,使这个子序列和最大。

解题思路:由于子序列连续,所以它的和可以通过两个前缀和作差得到。(即sum[i~j]=sum[j]-sum[i-1](j-i+1<=k))。而对于同一个序列终点来说,起点左边那个元素的前缀和越小,所得到的和越大。所以,题目又变成了求滚动区间的最小值问题。

代码:

#include <stdio.h>#include <limits.h>const int M = 100001<<1;int sum[M],q[M];int main(){    int z,n,k;    scanf("%d",&z);    while(z--)    {        int start,end,max;        int head,rear;        scanf("%d%d",&n,&k);        for(int i=1;i<=n;i++)        {            scanf("%d",&sum[i]);            sum[i+n] = sum[i];        }        for(int i=2;i< n+k;i++)            sum[i] += sum[i-1];        head = rear = 0;        q[head] = 0;        max = INT_MIN;        for(int i=1;i< n+k;i++)  //枚举每个区间终点        {            while(head<=rear && sum[i-1]<=sum[ q[rear] ])                rear--;            q[++rear] = i-1;            if(q[head]+1 < i-k+1)//如果大于区间范围,删除队首                head++;            if(sum[i] - sum[ q[head] ] > max)            {                start = q[head]+1;                end   = i;                               max   = sum[i] - sum[ q[head] ];            }        }        end = end>n ? end%n : end;        printf("%d %d %d\n",max,start,end);    }    return 0;}