poj 2823 单调队列

本文介绍单调队列的基本概念及其实现方法,通过实例解析如何利用单调队列解决滑动窗口内的最大最小值问题,并提供了两种不同的实现代码供读者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单调队列入门题目

先附上例题解析:

最大队列保证队列中各个元素大小单调递减(即,最大元素在对头,最小的在队尾),同时每个元素的下标单调递增(按校标递增的顺序添加这点可以不用考虑)。这样便保证队首元素最大,而且更新的时候队首永远是当前最大。因此,这个队列需要在两头都可以进行删除,在队尾插入。
维护方法:在每次插入的时候,先判断队尾元素,如果不比待插入元素大就删除,不断删除队尾直到队尾元素大于待插入元素或者队空。删除的时候,判断队首,如果队首元素下标小于当前段左边界就删除,不断删除队首直到队首元素下标大于等于当前段左边界(注意:这时队列肯定不为空),队首元素就是当前段的最优解。

个人补充:

由于扫描的是n这个序列每一个连续的区间为k的最大最小值,所以,由单调队列的性质,输入的第i个值大于k的时候就要开始扫描了。如果处理完所有的输入数据之后才开始扫描的话,有些区间的元素可能会不存在于单调队列里面。

唔……代码有点冗长的说

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxx = 1e6;
struct po
{
    int sign;
    int val;
    po(int v=0, int s=0):sign(s),val(v) {}
} inc[maxx],dre[maxx];
int ans1[maxx];//k区间内最大值
int ans2[maxx];//k区间内最小值
int main()
{
//    freopen("2823.txt","r",stdin);
    int head_inc,head_dre,tail_inc,tail_dre;
    int n,k;
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        memset(ans1,0,sizeof(ans1));
        memset(ans2,0,sizeof(ans2));
        head_dre = head_inc = tail_dre = tail_inc = 1;
        for(int i=1; i<=n; i++)
        {
            int tem;
            scanf("%d",&tem);
            if(i==1)//算是初始化吧
            {
                inc[tail_inc] = po(tem,i);
                dre[tail_dre] = po(tem,i);
            }
            else//注意tail_inc 和 tail_dre 的值的变化
            {
                for(int j=tail_inc; j>=head_inc; j--)
                {
                    if(tail_inc==head_inc && tem>inc[j].val)
                    {
                        inc[tail_inc] = po(tem,i);
                        break;
                    }
                    else if(tem>inc[j].val)
                        tail_inc--;
                    else
                    {
                        inc[++tail_inc] = po(tem,i);
                        break;
                    }
                }
                for(int j=tail_dre; j>=head_dre; j--)
                {
                    if(tail_dre==head_dre && tem<dre[j].val)
                    {
                        dre[tail_dre] = po(tem,i);
                        break;
                    }
                    else if(tem<dre[j].val)
                        tail_dre--;
                    else
                    {
                        dre[++tail_dre] = po(tem,i);
                        break;
                    }
                }
            }
            if(i>=k)
            {
                for(int j = head_inc; j<=tail_inc; j++)
                    if(inc[j].sign>=i-k+1 && inc[j].sign<=i)
                    {
                        ans1[i-k] = inc[j].val;
                        break;
                    }
                    else
                        head_inc++;

                for(int j = head_dre; j<=tail_dre; j++)
                    if(dre[j].sign>=i-k+1 && dre[j].sign<=i)
                    {
                        ans2[i-k] = dre[j].val;
                        break;
                    }
                    else
                        head_dre++;
            }
        }
        int vis = 0;
        for(int i=0; i<=n-k; i++)
            if(vis==0)
            {
                printf("%d",ans2[i]);
                vis = 1;
            }
            else
                printf(" %d",ans2[i]);
        printf("\n");
        vis = 0;
        for(int i=0; i<=n-k; i++)
        {
            if(vis==0)
            {
                printf("%d",ans1[i]);
                vis = 1;
            }
            else
                printf(" %d",ans1[i]);
        }
        printf("\n");
    }
    return 0;
}

额…后来看了一下别人的代码……果然精简多了

//Name: pku_2823_Sliding_Window
//Author: longxiaozhi
//Tag: 单调队列
//Root: 最大单调队列+ 最小单调队列。
#include <iostream>
using namespace std;
const int inf = -1u>>1;
const int maxn = 1e6;
struct node
{
    int val, tag;
    node(int v=0, int t=0):val(v),tag(t) {} void pt()
    {
        printf("%d %d\n", val, tag);
    }
} q[2][maxn+1];
int h1, r1, h2, r2;
int key[maxn+1], n, k;
int a[maxn+1], b[maxn+1];
int main()
{
    freopen("in.in", "r", stdin);
    q[0][0] = node(-inf, 0);
    q[1][0] = node(+inf, 0);
    int i;
    while(scanf("%d %d", &n, &k) != EOF)
    {
        if(k > n) k = n;
        for(i = 1; i <= n; i++) scanf("%d", key+i);
        h1 = h2 = 1;
        r1 = r2 = 0;
        for(i = 1; i < k; i++)
        {
            while(h1 <= r1 && q[0][r1].val < key[i]) r1--;
            q[0][++r1] = node(key[i], i);
            while(h2 <= r2 && q[1][r2].val > key[i]) r2--;
            q[1][++r2] = node(key[i], i);
        }
        for(i = k; i <= n; i++)
        {
            while(h1 <= r1 && q[0][r1].val < key[i]) r1--;
            q[0][++r1] = node(key[i], i);
            while(h1 <= r1 && q[0][h1].tag <= i-k) ++h1;
            a[i-k] = q[0][h1].val;
            while(h2 <= r2 && q[1][r2].val > key[i]) r2--;
            q[1][++r2] = node(key[i], i);
            while(h2 <= r2 && q[1][h2].tag <= i-k) ++h2;
            b[i-k] = q[1][h2].val;
        }
        for(i = 0; i < n-k; i++) printf("%d ", b[i]);
        printf("%d\n", b[i]);
        for(i = 0; i < n-k; i++) printf("%d ", a[i]);
        printf("%d\n", a[i]);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值