POJ2823 Sliding Window
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.
The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window position | Minimum value | Maximum value |
---|---|---|
[1 3 -1] -3 5 3 6 7 | -1 | 3 |
1 [3 -1 -3] 5 3 6 7 | -3 | 3 |
1 3 [-1 -3 5] 3 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | -3 | 5 |
1 3 -1 -3 [5 3 6] 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7] | 3 | 7 |
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
【题目大意】:给出一组数据,和一个一定长度的窗,这个窗可以移动,求在这个窗口内,对应数据的最大最小值,数据范围:0~1000000
【感想】:刚开始做的时候用JAVA一直超时。。。。后来看了别人的思路,改进了代码,可是还是TLE。。最后改用C++就A了,时间竟然达到5329ms。。。。真是给跪了,一模一样的代码。。。效率果然还是C++高。。。
【思路】:
单调队列,顾名思义,就是(严格)单调(递增或递减)的队列。下面以单调递减队列为例。
单调递减队列:
1.单调递减的队列,队首元素总是最大的。
2.元素只能从队尾入队,但可以从队尾或队首出队。若待入队的元素>=队尾元素,队尾元素出队,直到待入队的元素<队尾元素,或队列为空,然后待入队的元素从队尾入队;若新入队的元素与队首元素的距离>=窗口宽度,说明队首元素已不在窗内,队首元素出队。
3.单调队列有两个单调性:(1)元素的值是严格单调的,这里是严格单调递减;(2)元素的下标总是严格单调递增的。
举个例子,有8个元素1, 3, -1, -3, 5, 3, 6, 7,窗口的宽度是3,沿着排列滑动,输出每个窗口的最大值。
例如,1, [3, -1, -3,] 5, 3, 6, 7,窗口中的三个元素是3, -1, -3,最大值是3。
于是,输出的值依次是3, 3, 5, 5, 6, 7。
题目是给定n个元素,窗口宽度是m,随着窗口的滑动,依次输出窗中元素的最大值和最小值。
我的算法是,首先对前m个元素由小到大排序,于是最小值是排序后数列的头元素,最大值是尾元素。移动窗口至下一格,排序好的数列插入一个元素,删除一个元素,仍然保持单调性,于是最小值仍然是头元素,最大值仍然是尾元素。搜索的时间复杂度是lgm(二分查找),插入/删除的时间复杂度是1(memcpy),进行n-m+1次,于是算法的时间复杂度是O(nlgm)。
下面用单调递减队列解决,仍然以1, 3, -1, -3, 5, 3, 6, 7为例。
队列为空,1直接入队。1
下一个元素3,大于1,1出队,3入队。3
下一个元素-1,小于3,-1入队。3, -1
下一个元素-3,小于-1,-3入队。3, -1, -3
下一个元素5,依次与队尾元素比较直到队尾元素>5或队空。于是-3, -1, 3依次出队,5入队。5
下一个元素3,小于5,入队。5, 3
下一个元素6,3, 5依次出队,6入队。6
下一个元素7,6出队,7入队。7
考察单调队列队首元素,可得3, 3, 5, 5, 6, 7。
下面是单调递减队列的代码。
#include <iostream>
#include <cstring>
#include <stdio.h>
using namespace std;
//参考资料:http://www.jiancool.com/article/4582637302/
#define MXN 1000010
int n, m, rear = -1, front = 0;
int inp[MXN];
int indx[MXN];
int mx[MXN];
int mi[MXN];
int main()
{
while(scanf("%d %d",&n,&m)!=EOF)
{
for (int i = 0; i < n; i++)
scanf("%d",&inp[i]);
for (int i = 0; i < n; ++i)
{
while (rear >= front&&inp[i] <= inp[indx[rear]] )
--rear;
indx[++rear] = i;
if (i - indx[front] == m)
++front;
mi[i] = inp[indx[front]];
}
memset(indx,0,sizeof(indx));
rear=-1;
front=0;
for(int i=0; i<n; i++ )
{
while(rear>=front&&inp[i]>=inp[indx[rear]])--rear;
indx[++rear]=i;
if(i-indx[front]==m)++front;
mx[i]=inp[indx[front]];
}
for(int i=m-1; i<n; i++)
{
if(i!=n-1)
printf("%d ",mi[i]);
else
printf("%d\n",mi[i]);
}
for(int i=m-1; i<n; i++)
{
if(i!=n-1)
printf("%d ",mx[i]);
else
printf("%d",mx[i]);
}
}
return 0;
}
参考:http://www.jiancool.com/article/4582637302/