描述:
Time Limit: 12000MS | Memory Limit: 65536K | |
Total Submissions: 55081 | Accepted: 15858 | |
Case Time Limit: 5000MS |
Description
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
Output
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
Source
思路:
给定一个大小已知的数组以及一个大小已知的滑动窗口,窗口每个时刻向后移动一位,求出每个时刻窗口中数字的最大值和最小值。
思路:
这个题是单调队列的入门题。复杂度为O(n)
求最大值:建立一个单调递减队列,元素从左到右依次入队,入队之前必须从队列尾部开始删除那些比当前入队元素小或者相等的元素,直到遇到一个比当前入队元素大的元素,或者队列为空为止。若此时队列的大小超过窗口值,则从队头删除元素,直到队列大小小入窗口值为止。然后把当前元素插入队尾。
求最小值:建立一个单调递增队列,元素从左到右依次入队,入队之前必须从队列发问开始删除那些比当前入队元素大或者相等的元素,直到遇到一个比当前入队元素小的元素,或者队列为空为止。若此时队列的大小超过窗口值,则从队头删除元素,直到队列大小小入窗口值为止。然后把当前元素插入队尾。
设窗口大小为k,本题解法为建立两个单调队列,先从原数组中取前k个元素按上述要求入队,然后获取队头元素,再把下一个元素放入队中,再获取头元素,这样一直到最后一个元素为止。最后把答案从头到尾依次输出即可。
细节方面,比如下标的处理,仔细一点就能过了。
不知道为什么我找了网上的代码都T掉了。。。
当然也可以用线段树做,复杂度为O(nlogn)
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e6+10;
int a[maxn];//输入数组
int ans[maxn];
int deq[maxn];//单调队列 记录下标
int n,k;
void minq(){
int s=0,t=0;
for(int i=0; i<n; i++){
//在队列末尾加入i
while(s<t && a[deq[t-1]]>=a[i])t--;
deq[t++]=i;
if(i-k+1>=0){
ans[i-k+1]=a[deq[s]];
if(deq[s]==i-k+1)s++;//从队列头部删除元素
}
}
for(int i=0; i<=n-k; i++){
printf("%d%c",ans[i],i==n-k? '\n':' ');
}
}
void maxq(){
int s=0,t=0;
for(int i=0; i<n; i++){
//在队列末尾加入i
while(s<t && a[deq[t-1]]<=a[i])t--;
deq[t++]=i;
if(i-k+1>=0){
ans[i-k+1]=a[deq[s]];
if(deq[s]==i-k+1)//从队列头部删除元素
s++;
}
}
for(int i=0; i<=n-k; i++){
printf("%d%c",ans[i],i==n-k? '\n':' ');
}
}
int main(){
while(~scanf("%d%d",&n,&k)){
for(int i=0; i<n; i++){
scanf("%d",&a[i]);
}
minq();
maxq();
}
return 0;
}