1. 程式人生 > >連續子序列的最大和 HDU3415

連續子序列的最大和 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,最小值就是字首和。