poj 2823 Sliding Window(单调队列)

Sliding Window
Time Limit: 12000MS Memory Limit: 65536K
Total Submissions: 30677 Accepted: 9159
Case Time Limit: 5000MS

Description

An array of size n ≤ 10 6 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example:
The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window positionMinimum valueMaximum value
[1 3 -1] -3 5 3 6 7-13
1 [3 -1 -3] 5 3 6 7-33
1 3 [-1 -3 5] 3 6 7-35
1 3 -1 [-3 5 3] 6 7-35
1 3 -1 -3 [5 3 6] 736
1 3 -1 -3 5 [3 6 7]37

Your task is to determine the maximum and minimum values in the sliding window at each position.

Input

The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line.

Output

There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values.

Sample Input

8 3
1 3 -1 -3 5 3 6 7

Sample Output

-1 -3 -3 -3 3 3
3 3 5 5 6 7
 
 
题意:给出一个序列,一个窗口能括住k个元素,窗口每次移动一个位置,问每次移动后窗口里的最大和最小的元素各是什么。
 
思路:维护最大长度为k的一个递减单调队列和一个递增单调队列。以递减单调队列为例:它的头元素一直是队列当中的最大值,而且队列中的值是按照递减的顺序排列的。我们可以从队列的末尾插入一个元素,可以从队列的两端删除元素。
插入操作:在插入元素v的时候,要将队尾的元素和v比较,如果队尾的元素不大于v,则删除队尾的元素,然后继续将新的队尾的元素与v比较,直到队尾的元素大于v,这个时候我们才将v插入到队尾。(确定元素的插入位置时可以用二分优化)
删除队首元素操作:因为窗口的size为k,当队列中元素数量多于k时,则删去对首元素。
单调队列与队列唯一的不同就在于它不仅要保存元素的值,而且要保存元素的索引(当然在实际应用中我们可以只需要保存索引,而通过索引间接找到当前索引的值)

同样的代码用G++提交TLE,C++提交AC。。。。
AC代码:
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <iostream>

using namespace std;
const int maxn=1000005;
int n,k,first,last;
int sequence[maxn];  //保存原始序列
int max_ans[maxn];   //保存每插入一个元素,递减单调队列中的最大值
int min_ans[maxn];   //保存每插入一个元素,递增单调队列中的最小值
int index[maxn];     //保存单调队列中每个元素在原始序列中的下标

void decrease()      //维护单调递减队列
{
    memset(index,0,sizeof(index));
    last=first=1;
    index[last++]=1;
    max_ans[1]=sequence[1];
    for(int i=2;i<=n;i++)
    {
        while(first<last&&sequence[index[last-1]]<sequence[i])   //将要插入的元素与队尾元素比较
        last--;
        index[last++]=i;                                         //插入队尾元素
        if(index[first]<i-k+1) first++;                          //删除队首元素
        max_ans[i]=sequence[index[first]];                       //保存当前的队列中的最大值
    }
}
void increase()     //维护单调递增队列
{
    memset(index,0,sizeof(index));
    last=first=1;
    index[last++]=1;
    min_ans[1]=sequence[1];
    for(int i=2;i<=n;i++)
    {
        while(first<last&&sequence[index[last-1]]>sequence[i])
        last--;
        index[last++]=i;
        if(index[first]<i-k+1) first++;
        min_ans[i]=sequence[index[first]];
    }
}
int main()
{
   scanf("%d%d",&n,&k);
   for(int i=1;i<=n;i++)
   scanf("%d",&sequence[i]);
   decrease();
   increase();
   for(int i=k;i<=n;i++)
   printf("%d ",min_ans[i]);
   printf("\n");
   for(int i=k;i<=n;i++)
   printf("%d ",max_ans[i]);
   printf("\n");
   return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值