连续子序列的最大和 HDU3415

                                   Max Sum of Max-K-sub-sequence

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

题目要求长度不超过K的连续子序列的最大和,可以成环;

对于一个连续序列的和,这里可以用前缀和来处理一下,i —— j 的和 就是sum [ j ] - sum [ i - 1 ];

如果成环的话只需要算前缀和的时候算到2n就行了;

下面是关键:已经知道了题目要求 sum [ j ] - sum [ i - 1 ] 最大,其中( j - i + 1<=k) ,所以现在就是要求区间 [ i, j ] 的最小值 sum【x】 x \epsilon   【i ,j 】(sum【j】是定值的情况下) ;

所以可以枚举所有的 j ,找到相应区间的最小的 sum【x】,动态维护这个最小值,这里就用到单调增序列了;注意这里的增序列是非递减的,因为当你新加入一个val时如果和前面的值相等,肯定希望保留这个值(因为下标大)。

另外队头的位置,应该是  0  ;因为前缀和的左端点要减1;假设你的结果是 1-3 这个区间 ,前缀和是 sum[ 3 ] - sum[ 0 ]……

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=2e6+7;
int n,m;
struct node
{
    int id;
    int val;
}q[maxn];
struct hh
{
    int ans;
    int s,e;
};
int ansmin[maxn];
int ansmax[maxn];
int a[maxn];
int sum[maxn];
int head,tail;
int s,e;

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]),a[i+n]=a[i];
        for(int i=1;i<=2*n;++i)
            sum[i]=sum[i-1]+a[i];
        ///维护一个单调增的前缀和队列
        head=0;
        tail=0;
        q[0].val=0;
        q[0].id=0;
        int i;
        int ans=-INF;
        for(int i=1;i<=n+m;++i)
        {
            if(ans<sum[i]-q[head].val)
            {
                ans=sum[i]-q[head].val;
                s=q[head].id+1;
                e=i;
                
                if(s>n) s-=n;
                if(e>n) e-=n;
            }
            while(head<=tail&&sum[i]<=q[tail].val)
                --tail;
            q[++tail].val=sum[i];
            q[tail].id=i;
            while(i-q[head].id+1>m) ++head;
            
        }
        cout<<ans<<' '<<s<<' '<<e<<endl;
    }
    return 0;
}

结合slide window 那一题可以体会到 单调队列的用法,当一个连续的区间(窗户)在滑动的时候,单调队列可以动态维护这个宽度区间(窗户)的最大值,最小值。

这个题,窗户宽度就是k,最小值就是前缀和。

猜你喜欢

转载自blog.csdn.net/codetypeman/article/details/81514036
今日推荐