【单调队列】HDU 3415 Max Sum of Max-K-sub-sequence

KIDx的解题报告

 

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3415

 

题意:给出一个有n个数字的环状序列(其中每个数在-1000到1000之间,且n<=100000),求一个和最大的连续子序列。(要求这个连续序列长度小于等于K)  

 

单调队列基本模型:保持队列中元素单调递增(或递减),可以两头删除,只能从队尾插入新元素,队首q[head]为当前最优区间头,队列存的是下标。

 

基本思路:由于是环,补足2*n个数, 预处理出前2*n项和,枚举区间尾i, 如果当前区间尾是i,有最优区间头q[head],那么sum[i]-sum[q[head]-1]就是区间[q[head], i]的和。

 

 

为何对于当前区间尾i最优区间头是q[head]?这就是删除队列尾的功劳了:i显然可能是当前或者以后的最优区间头,所以需要插入,而q[tail-1]是队列最后一个元素,如果区间[q[tail-1], i-1]的和小于0,要让q[tail-1]作为区间头的话,必然让和更小了,所以i是更好的区间头,于是把所有劣解q[tail-1]去掉,也就是出队列,反之q[tail-1]必然比i更优,所以说队首就是当前区间的最优的区间头了

 

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
#define M 200005
#define inf 0x3fffffff

int q[M], sum[M];

int cal (int a, int b) { return sum[b] - sum[a-1]; }

int main()
{
    int t, n, k, i;
    scanf("%d", &t);
    while (t--)
    {
        scanf ("%d%d", &n, &k);
        for (i = 1; i <= n; i++)
            scanf ("%d", sum+i);
        for (i = n+1; i <= 2*n; i++)
            sum[i] = sum[i-n];
        for (i = 2; i <= 2*n; i++)			//预处理前2*n项和(其实n+k即可)
            sum[i] += sum[i-1];
        int head = 0, tail = 0, maxs = -inf, a,  b;
        for (i = 1; i <= 2*n; i++)			//枚举区间尾i
        {
			//把劣解出队列
            while (tail > head && cal(q[tail-1], i-1) < 0) --tail;
            q[tail++] = i;

			//当前最优区间头q[head],计算区间[q[head], i]的和
            int tp = cal(q[head], i);
            if (tp > maxs)
            {
                maxs = tp;
                a = q[head];
                b = i;
            }

			//超出长度,q[head]已经不能为下一个区间尾所用就出队列
            while (tail > head && i - q[head] + 1 >= k) ++head;
        }
        if (b > n) b -= n;		//b可能大于n而不满足题意
        printf ("%d %d %d\n", maxs, a, b);
    }
    return 0;
}

 

你可能感兴趣的:(编程,C++,算法,ACM,KIDx)