hud 3415(单调队列)

      题意:给出n个数的循环序列,要求长度不超过k的连续子序列之和的最大值。
      分析:单调队列求解。单调队列就是有单调性的队列,可以从两端删除元素,但还是只能从 队尾添加元素。可以认为这种队列前面存的是过期的值,后面存的是还有用的                       值。                       
                  单调队列一般用来求解固定区间长度内的最大值或区间内元素之和的最大值。
                  对本题而言,由于是环,要补足2*n个数(其实n+k-1个就够了),求出前i个数字之和sum[i],0<i<2*n。题目实际所求的就是max(sum[j]-sum[i]),j-i<k,那么 就需要求                     出min(sum[i])。枚举每一个区间尾i,并用一个单调递增队列保存区间内sum[i]值的下标,则对每一个区间尾i,有最优区间头q[head],于是sum[i]-sum[a[head]-1]就是                   区间(q[head],i)内元素和的最大值。
                  那么如何保证区间头q[head]是最优值呢?这就要靠删除队尾了。我们知道,对每一个队尾元素,都可能是以后某个有最大和的区间的起始下标,因此,  当我们要把一                  个区间尾i插入队尾时,sum[i-1]-sum[q[tail-1]-1]表示(q[tail-1],i-1)这个区间 的 和,如果这个值小于0,那么就说明以q[tail-1]为区间头会导致和变小,于是将其删去。                    这样就保证了q[head]是最优区间头。

代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
const int M=1010;
const int N=1000005;
const int INF=1e9;
int n,k,q[N],sum[N];
int main()
{
    int i,j,t;
    cin>>t;
    while(t--)
    {
        scanf("%d%d",&n,&k);
        for (i=1;i<=n;i++){
            scanf("%d",sum+i);
            sum[i+n]=sum[i];
        }
        int m=n+k-1;
        for (i=1;i<=m;i++) {
            sum[i]+=sum[i-1];
        }
        int head=0,tail=0,Max=-INF,a,b;
        for (i=1;i<=m;i++){
            while(head<tail && sum[q[tail-1]-1]>sum[i-1]) tail--; // sum[i]-sum[q[tail-1]-1] 表示 sum(q[tail-1],i-1) 这个区间内的和,若这个和小于0,那么就不能以q[tail-1]为区间头,将其删去。这样可以保证单调队列的队头是最优区间头q[head]
            q[tail++]=i;
            while(head<tail && i-q[head]>=k) head++; // 超出区间则删去队头
            if (sum[i]-sum[q[head]-1]>Max){  //cout<<sum[i]<<" "<<sum[q[head]-1]<<endl;
                Max=sum[i]-sum[q[head]-1];
                a=q[head]; b=i; // printf("%d %d %d\n",Max,a,b);
            }
        }
        printf("%d %d %d\n",Max,a,b>n?b-n:b);
    }
}















                                       
     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值