POJ 2823 Sliding Window 单调队列+输入输出外挂

题解:来自http://hi.baidu.com/wzyjerry/blog/item/3838f48af40e40cafd1f1089.html


题目大意:给你一个长度为n的数组,求从左到右的每个相邻的长度为k的区间内数的最大值和最小值。
解题方法:这道题数据较大(n最大为10^6)所以用纯模拟O(n*k)是铁定挂掉的,写的好的线段树可以卡时间过去。这里我们选择更为合适的单调队列解决。STL里有类似的优先队列。单调队列就是在一个队列中保证数据的单调性,可以很方便的求制定区间极值的一种队列。每个元素只会进队一次,所以复杂度为O(n),可以有效地降低时间复杂度。下面具体地说明一下单调队列。
1.数据结构:理论上来说应当是两个数组,一个存元素,一个存这个元素在原队列中的下标。实际上如果保留原队列的话,只需要记录下标即可。这样简化为只开一个长度为n的数组Q,减少了一半的空间开销。另外要记录队首s和队尾t。
2.初始化:将队首赋值为第一个元素。
3.维护过程(这里以建立单增队列求最大值为例):依次枚举剩下的N-1个元素,并且将当前未入队的第一个元素和队尾元素比较,当且仅当队列为非空并且队尾元素的值小于当前未入队的元素时,将队尾元素删除(也就是队尾指针-1),因为当前的元素比队尾元素大,所以在区间内队尾元素不会是最大值了。重复这个过程直到队列空或者队尾元素比当前元素大,一次插入结束。

4.由于单调队列是求指定长度区间的最值,所以需要控制区间长度。当插入结束后,需要用队尾元素下标和队首元素下标计算是否在一个区间内。如果不在,队首元素出队(队首指针+1)。重复这个过程直到控制到区间内,一次最大值寻找完毕。最大值出现在队首。

补充:本题输出输出量巨大,需要使用输入输出外挂,并且是能处理负数的。

/*
ID: sdj22251
PROG: subset
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define LOCA
#define MAXN 1005*1005
#define INF 100000000
#define eps 1e-7
using namespace std;
int a[MAXN];
int q[MAXN];
int in()
{
    int flag = 1;
    char ch;
    int a = 0;
    while((ch = getchar()) == ' ' || ch == '\n');
    if(ch == '-') flag = -1;
    else
    a += ch - '0';
    while((ch = getchar()) != ' ' && ch != '\n')
    {
        a *= 10;
        a += ch - '0';
    }
    return flag * a;
}
void out(int a)
{
    if(a < 0) {putchar('-'); a = -a;}
    if(a >= 10)out(a / 10);
    putchar(a % 10 + '0');
}
int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++)
        a[i] = in();
    int head = 0, tail = 0;
    q[++tail] = 1;
    for(int i = 1; i <= n; i++)
    {
        while(head <= tail && a[q[tail]] >= a[i])
            tail--;
        q[++tail] = i;
        while(q[tail] - q[head] + 1 > k)
        head++;
        if(i == k)
        out(a[q[head]]);
        else if(i > k)
        {
            putchar(' ');
            out(a[q[head]]);
        }
    }
    putchar('\n');
    head = 0, tail = 0;
    q[++tail] = 1;
    for(int i = 1; i <= n; i++)
    {
        while(head <= tail && a[q[tail]] <= a[i])
            tail--;
        q[++tail] = i;
        while(q[tail] - q[head] + 1 > k)
        head++;
        if(i == k)
        out(a[q[head]]);
        else if(i > k)
        {
            putchar(' ');
            out(a[q[head]]);
        }
    }
    putchar('\n');
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值