首页 > 代码库 > POJ2823 Sliding Window (单调队列)
POJ2823 Sliding Window (单调队列)
POJ2823
Sliding Window
Description An array of size n ≤ 106 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.
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 31 3 -1 -3 5 3 6 7 Sample Output -1 -3 -3 -3 3 33 3 5 5 6 7 Source POJ Monthly--2006.04.28, Ikki |
大意:
看题目中那个表格很容易懂,就是有个滑动区间,每次我们要找到这个区间中的最大值、最小值。
题解:
单调队列。
单调队列是DP优化的一种,能实现O(VN)的多重背包。这题虽然不是DP,不过能怒用一发单调队列。
单调队列是用一个单调的队列来存储必要的元素,并不存储无用的元素。在这题的求最小值的过程中:
元素按顺序读入队列q,当队列为空时直接读入;当队列非空时,若队尾的元素不小于当前要入队的元素,则踢出队尾,直到队尾元素小于要入队的元素或者队为空为止。
再开一个数组p,存储队列中的元素在原数据中的下标。
像这样:
(读元素a[i])
1 while(l<r && q[r-1]>=a[i]) r--;2 p[r]=i;3 q[r++]=a[i];
然后检查队首,如果p存储的队首的元素下标表示该元素已经滑出区间,则将其踢出,像这样:
(检查队首、踢出过期元素)
while(p[l]<i-k+1) l++;///若队首的下标表示它已过期(滑出了区间),弹出队首
经过这2个操作后,q[l]就是所求的当前区间的最小值。然后i++,再进行这2个操作,就能得到下一个最小值。换一个符号就能求最大值了。
这两个操作有什么深意呢?
读取时那样的操作,保证了队列中存储了递增的最小若干个数,在队首能立即得到当前区间最小的数(若那个数已经滑出了区间,则它会被踢出),
当那个数滑出区间时能立即找到下一个最小的数。
就这两个简单的操作,用极低的复杂度,就能完成找区间滑动到每个地方的最小值/最大值!我就问你碉不碉!
代码:
1 #include<cstdio> 2 #include<cmath> 3 #include<iostream> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #include<map> 8 #include<set> 9 using namespace std;10 #define ll __int6411 #define usint unsigned int12 #define mz(array) memset(array, 0, sizeof(array))13 #define minf(array) memset(array, inf, sizeof(array))14 #define REP(i,n) for(int i=0;i<(n);i++)15 #define FOR(i,x,n) for(int i=(x);i<=(n);i++)16 #define WR(x) printf("%d ",(x))17 #define FR freopen("1.in","r",stdin)18 #define FW freopen("1.out","w",stdout)19 20 const int maxn=1111111;21 22 int n,k;23 int a[maxn];///存储数据24 int q[maxn];///队列25 int p[maxn];///存储a[i]的下标i26 27 void gankmin()28 {29 int i,l=0,r=0;///l,r为队列头尾30 for(i=0;i<k-1;i++)///先将前k-1个入队31 {32 while(l<r && q[r-1]>=a[i]) r--;///队列有元素时,要保证队尾小于要入队的元素,否则弹出队尾33 ///意义在于存储递增的最小若干个数,能立即得到当前区间最小的数,34 ///当那个数滑出区间时能立即找到下一个最小的数35 p[r]=i; ///记录a[i]的下标36 q[r++]=a[i];///a[i]加入队列37 }38 for(;i<n;i++)39 {40 while(l<r && q[r-1]>=a[i]) r--;41 p[r]=i;42 q[r++]=a[i];43 44 while(p[l]<i-k+1) l++;///若队首的下标表示它已过期(滑出了区间),弹出队首45 WR(q[l]);46 }47 }48 49 void gankmax()50 {51 int i,l=0,r=0;52 for(i=0;i<k-1;i++)53 {54 while(l<r && q[r-1]<=a[i]) r--;55 p[r]=i;56 q[r++]=a[i];57 }58 for(;i<n;i++)59 {60 while(l<r && q[r-1]<=a[i]) r--;61 p[r]=i;62 q[r++]=a[i];63 64 while(p[l]<i-k+1) l++;65 WR(q[l]);66 }67 }68 69 int main()70 {71 while(scanf("%d%d",&n,&k)!=EOF)72 {73 REP(i,n)74 scanf("%d",&a[i]);75 gankmin();76 puts("");77 gankmax();78 puts("");79 }80 return 0;81 }
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。