• 单调队列学习小结


    单调队列学习小结

    单调队列::队列中元素之间的关系具有单调性,而且,队首和队尾都可以进行出队操作,只有队尾可以进行入队操作。
    单调队列的常用操作如下:
    (1)插入:若新元素从队尾插入后会破坏单调性,则删除队尾元素,直到插入后不再破坏单调性为止,再将其插入单调队列。(这样队列整体始终保持单调,不单调的元素会被逐个删除)
    (2)获取最优(最大、最小)值:访问首尾元素。
    对于调整最值问题上我们还可以通过重载运算符来进行调整。

    我们可以看下面一组例子来加深对单调队列的理解:
    例:

    //一组数(1,3,2,1,5,6),进入单调不减队列的过程:
    1入队,得到队列(1);
    3入队,得到队列(1,3);
    2入队,这时,队尾的的元素3>2,将3从队尾弹出,新的队尾元素1<2,不用弹出,将2入队,得到队列(1,2);
    1入队,2>1,将2从队尾弹出,得到队列(1,1);
    5入队,得到队列(1,1,5);
    6入队,得到队列(1,1,5,6);
    

    根据单调队列的性质,我们很容易想到其单调性能解决的之前的最长上升子序列问题:
    我们可以把该序列中的元素全部放到该(单调递增)队列中,然后我们就可以得到最长的上升子序列,最后我们可以对其个数进行统计或者输出。

    当然我们也可以将队列的大小进行固定,这样我们就可以得到定长的每个区间,从而可以获得该定长区间的一些性质(最大值或最小值),这样我们就可以求得区间极值。这样复杂度为o(k),相比于我们用朴素算法的枚举来看(复杂度为o(nk)),要优化很多。

    我们对子序列的单调队列使用举个例子:

    连续子序列的最大和 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
    

    对于一个连续序列的和,这里可以用前缀和来处理,i —— j 的和就是sum [ j ] - sum [ i - 1 ];(如果成环的话我们可以算前缀和的时候算到2n)
    对于同一个序列终点来说(sum[j]确定),起点左边那个元素的前缀和越小,所得到的和越大。所以我们解决问题的关键就是找出[i,j]区间内的一点x使得sum[x]最小。我们结合单调队列:

    #include <stdio.h>
    #include <limits.h>
    const int M = 100001<<1;
    int sum[M],q[M];
    int main()
    {
        int z,n,k;
        scanf("%d",&z);
        while(z--)
        {
            int start,end,max;
            int head,rear;
            scanf("%d%d",&n,&k);
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&sum[i]);
                sum[i+n] = sum[i];
            }
            for(int i=2;i< n+k;i++)
                sum[i] += sum[i-1];
                    head = rear = 0;
            q[head] = 0;
            max = INT_MIN;
            for(int i=1;i< n+k;i++)                 //枚举每个区间终点
            {
                while(head<=rear && sum[i-1]<=sum[ q[rear] ])
                    rear--;
                q[++rear] = i-1;
                if(q[head]+1 < i-k+1)      //起点如果大于区间范围,删除队首
                    head++;
                if(sum[i] - sum[ q[head] ] > max)
                {
                    start = q[head]+1;
                    end   = i;
                    max   = sum[i] - sum[ q[head] ];
                }
            }
            end = end>n ? end%n : end;
            printf("%d %d %d
    ",max,start,end);
        }
        return 0;
    }
    
  • 相关阅读:
    标准MIDI文件格式
    David's MIDI Spec
    国庆长假好读书
    vim用法
    ,非你莫属,
    子宫的满足,睡觉前,
    在哪里,
    黑条,
    下单 返回的字段,
    机会啊,游戏啊,转吧,
  • 原文地址:https://www.cnblogs.com/study-hard-forever/p/12130008.html
Copyright © 2020-2023  润新知