[ACM] hdu 3415 Max Sum of Max-K-sub-sequence (单一队列)

[ACM] hdu 3415 Max Sum of Max-K-sub-sequence (单调队列)

Max Sum of Max-K-sub-sequence

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


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

 

4 6 3 6 -1 2 -6 5 -5 6 4 6 -1 2 -6 5 -5 6 3 -1 2 -6 5 -5 6 6 6 -1 -1 -1 -1 -1 -1
 


 

Sample Output

 

7 1 3 7 1 3 7 6 2 -1 1 1
 


 

Author

 

shǎ崽@HDU
 


 

Source

 

HDOJ Monthly Contest – 2010.06.05

 

解题思路:

求长度不超过K的最大子段和。这道题看了很多解题报告,折腾了快两天,终于理解得差不多了。自己后来写了一遍代码,一次AC。

参考:http://www.cnblogs.com/neverforget/archive/2011/10/13/ll.html

维护一个单调递增的队列,队首元素一定是最小的,sum[j]-sum[i]j是一定的,为区间尾,就是找最小的sum[i],而这个sum[i]一定是存在队首的。之前看过一个结题报告说,本题为了维护单调,在插入元素前要删除队尾元素,在被删除的元素其实并不是真正的被删除,还是存在队列中,后来想想,也是这样回事,因为虽然删除了元素,但长度K是一定的,这个长度K是不能根据当前队列里有多少元素来算,而是要根据下标,而这些下标就包括队尾已经被删除的元素(这种情况被插入的元素不是队首),如果把尾节点全部删除了,当前插入的元素为队首,那么那些删除的元素可以当做已经不存在了,区间长度由当前队首元素(下标)来计算。队列里最多只能有K个元素,这里元素都是指的下标。一开始被插入的元素为下标0.

Sum[j]- sum[i] =sum[i+1]+sum[i+2]+…..+sum[j]

比如数据 

Num:  2   -1   8  -5      K=3

Sum :  2   1    9  4       sum[0]=0

下标   1   2    3  4

I=1        插入  0

I=2        插入  1  单调递增 sum[0]<sum[1]

I=3        sum[2]<sum[1],删除1 ,插入2 ,这时候可以算出前三个数的和,这也是第一个长度为K 的序列和。

I=4        因为坐标限制0<4-K,删除首元素,插入3,这时候队列里有2 3,想想这时候为什么这时候要删除1那个元素,sum[4]-sum[1]= 2  sum[4]-sum[2]= 3 ,后面那个是最优的,所以这也是优先队列首元素为最小值的用处所在。队列里面不可能出现4,因为自己减自己没意义。

 

代码:

#include <iostream>
#include <queue>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxn=100005;
const int inf=0x7fffffff;
int num[maxn];
int sum[maxn*2];
int n,k;

int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        sum[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&num[i]);
            sum[i]=sum[i-1]+num[i];
        }
        for(int i=1;i<k;i++)
            sum[n+i]=sum[n+i-1]+num[i];
        int temp=n;
        n=n+k-1;
        deque<int>q;
        int ans=-inf;
        int start,end;
        for(int i=1;i<=n;i++)
        {
            while(!q.empty()&&sum[i-1]<sum[q.back()])//维护队列的单调递增,首元素为最小值
                q.pop_back();
            while(!q.empty()&&q.front()<i-k)
                q.pop_front();//区间长度为K这个限制
            q.push_back(i-1);
            if(sum[i]-sum[q.front()]>ans)
            {
                ans=sum[i]-sum[q.front()];//ans为num[q.front()+1]+num[q.front()+2]+....num[i]的和
                start=q.front()+1;
                end=i;
            }
        }
        printf("%d %d %d\n",ans,start,(end>temp?end%temp:end));
    }
}