hdu3415Max Sum of Max-K-sub-sequence(单调队列)

->题目请戳这里<-

题目大意:给n个数,这n个数首尾相连。求一个长度为不超过k的连续子序列最大和。并求出起始和终止位置。

题目分析:求某段连续区间和的最大值,单调队列解决。具体做法是:先求出sum[1]到sum[n],我们考虑第i个数,用一个单调队列维护sum[i - k]到sum[i - 1]的最小值,设这个最小值是sum[j],那么sum[i] - sum[j]就是j到i的这个区间的最大连续和,如果比已知最大和大,更新最大值sum[i] - sum[j],最新区间j 到i。详情请见代码:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1000005;
int lcm[N + N];
int n,k;

int que[N + N];
int head,tail;
void init()
{
    head = tail = 0;
}

void push(int id)
{
    while(head < tail && lcm[id] < lcm[que[tail - 1]])
        tail --;
    que[tail ++] = id;
}

void pop(int id)
{
    while(head < tail && que[head] <= id - k)
        head ++;
}

int main()
{
    int i,t;
    scanf("%d",&t);
    while(t --)
    {
        scanf("%d%d",&n,&k);
        memset(lcm,0,sizeof(lcm));
        lcm[0] = 0;
        for(i = 1;i <= n;i ++)
        {
            scanf("%d",&lcm[i]);
            lcm[i + n] = lcm[i];
            lcm[i] += lcm[i - 1];
        }
        for(i = n + 1;i <= n + n;i ++)
            lcm[i] += lcm[i - 1];// + lcm[i - n];
        init();
        /*for(i = 0;i <= n + n;i ++)
            printf("%d ",lcm[i]);
        printf("\n");
        system("pause");*/
        int ans = -0x3f3f3f3f;
        int s,e;
		push(0);
        for(i = 1;i <= n + n;i ++)
        {
            if(lcm[i] - lcm[que[head]] > ans)
            {
                ans = lcm[i] - lcm[que[head]];
                s = que[head] + 1;
                e = i;
            }
			push(i);
            pop(i);
        }
        printf("%d %d %d\n",ans,s,e>n?e - n:e);
    }
    return 0;
}
//687MS	8948K
/*
92
5 1
1 2 3 4 5
1 1
4
6 6
0 1 2 3 4 5
6 6
1 2 3 -4 5 6
6 6
2 0 -4 5 6 7
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

*/

PS:顺便吐槽一下hdu的数据,太坑了,明明说好了n是100000内的数,我一开始数组开了100005,一直非法访问,代码检查了半天没检查出问题,结果一狠心数组加了10倍,就过了。。。。

你可能感兴趣的:(单调队列)