Max Sum of Max-K-sub-sequence HDU - 3415 (单调队列 好题)

在这里插入图片描述

题意: 给定n长度的序列, 求其中连续且长度不超过k的子序列的最大和

题解: 很常见的一类题, 和这道题非常类似, 一个是求子序列最大长度, 一个是求子序列最大和, 下面来说一下这道题
首先运用一个预处理的思想, 定义sum[i] = 1 i s i \sum_1^is_i
如此一来, maxK[i, j] = sum[j] - min{sum[i] | 1<=i<j}, 也就是说对于求i -> j的最大子序列和, 只用将sum[j] 减去j之前最小的sum[i]即可, 很显然, 区间求最值, 维护单调栈即可, 由于题中还对长度作了要求, 所以不能使用priority_queue, 要自己来维护一个.
而且题目要求是环形, 我们需要在n之后将1->n的所有数再复制一遍来模拟环形, 对于下标也要加以判断

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const LL maxn = 1e6+10;
const LL inf = 1<<30;

int n, k, s[maxn>>1], sum[maxn>>1]; //储存下标
deque<int> q; //sum[j]的单调队列
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&k);
        for(int i = 1; i <= n; i++){
            scanf("%d",&s[i]);
            sum[i] = sum[i-1]+s[i];
        }
        for(int i = n+1; i < n+k; i++)
            sum[i] = sum[i-1]+s[i-n]; //模拟环形

        int ans[3] = {-inf};
        q.clear();
        for(int i = 1; i < n+k; 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[0]){
                ans[0] = sum[i]-sum[q.front()];
                ans[1] = q.front()+1;
                ans[2] = i;
            }
        }
        if(ans[2]>n) ans[2]%=n;
        printf("%d %d %d\n",ans[0],ans[1],ans[2]);//
    }

	return 0;
}

猜你喜欢

转载自blog.csdn.net/a1097304791/article/details/86822053