POJ2823 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/p/2645383.html