小学生玩ACM----单调队列

  昨天才把单调队列做完第一个例题,怎么说咧,就是运用双向队列的性质对数据进行操作。

  还是先说说什么是单调队列吧,广义的说法都是队列中元素呈现单调性,也就是说队列里面的家伙们根据某原则存在,听起来玄乎玄乎的,我当时就纳闷了,我第一个想到的就是“难道还要排序?”,其实是这样的,每个元素在入队之前会进行比较,从队尾开始,如果插入当前的中国元素会破环单调性,那么把用来跟他比较的队尾元素出队处理,然后再比较,注意,这是用来比较的队尾元素是现在队尾元素的前面那个,因为先前那个被剔除了,直到当前元素压入队列也满足单调性,这是就结束筛选,把当前元素压入队列

  最好的是举个例子来,比如a[10]={1,3,5,7,9,0,2,4,6,8}那么我用16行来模拟每次的队列里面装的元素,****下面为单调递增

1、NULL          一开始队列内为空

2、1           把空当作无穷小就好了,1比无穷小大,所以1入队

3、1 3           因为3比1大,所以3入队

4、1 3 5           因为5比3大,所以5入队

5、1 3 5 7            因为7比5打,所以7入队

6、1 3 5 7 9           因为9比7大,所以9入队

7、1 3 5 7           这时因为0比9小,所以9出队

8、1 3 5           继续用队尾元素跟0比,因为7还是比0大,所以7出队

9、1 3           同上,因为0比5小,所以5出队

10、1           同上,因为0比3小,所以3出队

11、NULL           同上,因为0比1小,所以1出队,此时队列又回到空的状态栏

12、0           因为队列为空,所以0入队

13、0 2           因为2比0大,所以入队

14、0 2 4           因为4比2大,所以4入队

15、0 2 4 6           因为6比4大,所以6入队

16、0 2 4 6 8           因为8比6大,所以8入队

如果你好好看了一下,那发现了所谓的单调性只是队列中呈现的有木有?哦耶,恭喜恭喜,你已经知道单调队列的精华了,下面给你弄个例题,你就知道怎么去用它了

 

 

 

                嘿嘿,no好意思,我也是初学,所以只做了一个题,但是别狗眼看人低,认识不同的哦~亲,没有领略真谛可不能出来吓人呢

 

Max Sum of Max-K-sub-sequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4883    Accepted Submission(s): 1780


Problem Description
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

 

 

用两种写法弄过,一种调用STL双向队列,一种是直接数组模拟,感觉模拟麻烦点但是耗时很少,双向队列每次都是调用的函数,所以耗时多。

STL双向队列:(734MS)

#include <iostream>
#include <queue>
using namespace std;
int
a[222222],sum[222222];  //sum[n]记录从a[1]加到a[n]的和
deque<int> q;
int
main (void)
{

    int
t,n,m,i,head,end,s;
    cin>>t;
    while
(
t--&&cin>>n>>m)
    {

        sum[0]=0;
        for
(
i=1;i<=n;i++)
        {

            cin>>a[i];
            sum[i]=sum[i-1]+a[i];
        }

        for
(
i=n+1;i<=n+m;i++)
            sum[i]=sum[i-1]+a[i-n];   
        s
=-1e10;head=end=0;  //最大值初始化为-1e10
        q.clear();  //把队列弄干净,初始化双向队列,清空
        for(i=1;i<n+m;i++)
        {

            while
(!
q.empty()&&sum[i-1]<sum[q.back()])  //如果队列不是空的,而且当前数值压入队列会破坏队列元素单调
                q.pop_back();  //那么把队尾元素出队
            while(!q.empty()&&i-q.front()>m)  //如果队列不是空的,而且当前记录的子串长度超过规定范围
                q.pop_front();  //队首元素出队
            q.push_back(i-1);  //把当前数值压入队尾
            if(sum[i]-sum[q.front()]>s)  //当前元素满足条件的最大值大于记录的最大值时进行记录操作
            {
                s=sum[i]-sum[q.front()];
                head=q.front()+1;end=i;
            }
        }

        if
(
head>n)head-=n;
        if
(
end>n)end-=n;
        cout<<s<<" "<<head<<" "<<end<<endl;
    }

    return
0;
}

用数组模拟双向队列:(187MS)


#include <iostream>
#include <cstdio>
#include <cstring>
using namespace
std;
int
a[222222],sum[222222]={0},que[222222];  //que代替队列起记录作用,sum还是一样记录从1到n的和
int
main (void)
{

    int
t,n,m,s,i,j,k,l,max,aa,ss,qian,hou;
    scanf("%d",&t);
    while
(
t--&&scanf("%d%d",&n,&m))
    {

        for
(
i=1;i<=n;i++)
            scanf("%d",&a[i]),a[n+i]=a[i];
        for
(
i=1,sum[0]=0;i<=n+m;i++)
            sum[i]=sum[i-1]+a[i];
        max=-1000000,aa=ss=qian=hou=0;
        for
(
i=1;i<n+m;i++)
        {

            while
(
qian<hou&&sum[i-1]<sum[que[hou-1]])hou--;  //hou即是队尾
            que[hou++]=i-1;  //压入队列数组
            while
(
qian<hou&&i-que[qian]>m)qian++;  /qian即是队首
            if
(
sum[i]-sum[que[qian]]>max)
            {

                max=sum[i]-sum[que[qian]];
                aa=que[qian]+1;ss=i;
            }
        }

        if
(
aa>n)aa-=n;
        if
(
ss>n)ss-=n;
        printf("%d %d %d\n",max,aa,ss);
    }

    return
0;
}


 

有木有发现队列处理的是他们的和?你要明白sum[2]=a[0]+a[0]+a[2],    sum[1]=a[0]+a[1],于是有sum[2]-sum[1]=a[2]     因为题目是要求最长连续的子序列,所以用这个可以相减直接得到咯,同时我再保证队列里面的跨度不超过m就OK了撒,看清楚了,队列里面不是装的数组a的值,也不是sum的,是下标,所以计算当前长度就直接用i减去队首就行了,里面有俩while循环,上面那个是用来保持队列中单调性的,也就是直到加入i之后会破坏单调性的最后一个队尾元素滚出去之后就结束,这是就毫不小气地把i请入茅房,第二个while循环是用来保持快读不超过m的,题目是限制了最长长度的,所以这个得注意,然后就是if判断了,只要sum[i]-sum[队首元素]得到的更大,那么就取代

 

还要明确一点,就是用STL里面的双向队列和自己用数组模拟双向队列,前者的入队出队等所有操作都是调用的函数,所以时间问题你懂的,嘿嘿,后者的话就注意模拟数组的大小就行了,你想啊,队首出队一个那队首下标就要后移一个,要是移动多了...你也懂的,嘿嘿,经过我疯狂的文字教育,亲~还木有懂的请举手

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值