POJ-2823 Sliding Window 单调队列

本文介绍了一种使用单调队列实现的滑动窗口算法,用于解决给定序列中固定长度子序列的最大值和最小值问题。通过维护两个单调队列,分别处理最大值和最小值的查询,有效利用了元素的存活周期特性。

这题是给定了一个长度为N的串,问一个固定区域内的最小值和最大值,这题没办法通过DP来求解,因为单纯保留最值的信息是行不通。详见代码:

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 1000005
using namespace std;

int N, M, seq[MAXN], que[MAXN], front, tail;

/* 
 此题就是要求出给定序列的某一段区间内的最小值和最大值
 一个长为N的序列,每次选取M长的区间进行滑动
 由于框的存在,每个元素有一个存活周期,这里就有一个性质:
 1.每个元素进来时的存活周期为M(框的长度),因为其最多作为M次最优解
 2.每插入一个元素,前面元素的存活周期集体减去1,当一个元素的生存周期为0时,应弃置该元素 
 3.当前插入值的存活周期一定大于以前的元素
 由以上的性质,我们就能够构造一个单调队列,使其满足按照解的优先级排序,如果遇到相同优先级的
 的元素,就按照其生存周期排序,也就是说所有的解的优先级小于等于该元素生存周期小于当前点的元素
 是要进行删除操作,因为当前值足以替代其余值并且拥有更加长的生存周期,所以我们就可以对一个队列
 进行下面的操作,首先找到该号元素的位置,由于当前元素的生存周期最长,所以直接删除其后面的点
 将该元素更新到指定的地方,每次都取出队首元素,做一次生存周期判定,如果不合法,则选取次优的元素
 这个次优的元素也就是生存周期允许的情况下的最优解
*/

int main()
{
    while (scanf("%d %d", &N, &M) == 2) {
        for (int i = 1; i <= N; ++i) {
            scanf("%d", &seq[i]);
        }
        front = 1, tail = 0;
        for (int i = 1; i < M; ++i) {
            while (front <= tail && seq[i] <= seq[ que[tail] ]) --tail;
            que[++tail] = i; // 保留编号即可,且这样能进行生存周期的判定
        }
        for (int i = M; i <= N; ++i) {
            while (front <= tail && seq[i] <= seq[ que[tail] ]) --tail;
            que[++tail] = i;
            while (que[front] < i-M+1) ++front; // 如果生存周期不合法
            if (i != M) printf(" %d", seq[ que[front] ]);
            else printf("%d", seq[ que[front] ]);
        }
        puts("");
        front = 1, tail = 0;
        for (int i = 1; i < M; ++i) {
            while (front <= tail && seq[i] >= seq[ que[tail] ]) --tail;
            que[++tail] = i;
        }
        for (int i = M; i <= N; ++i) {
            while (front <= tail && seq[i] >= seq[ que[tail] ]) --tail;
            que[++tail] = i;
            while (que[front] < i-M+1) ++front;
            if (i != M) printf(" %d", seq[ que[front] ]);    
            else printf("%d", seq[ que[front] ]);
        }
        puts("");
    }
    return 0;    
}

转载于:https://www.cnblogs.com/Lyush/archive/2012/08/18/2645383.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值