单调队列
文章参考了 http://www.cnblogs.com/Jason-Damon/archive/2012/04/19/2457889.html
文章以poj2823 http://poj.org/problem?id=2823 窗口移动来详细了解下单调队列这个东西,单调队列我感觉在竞赛中用途还是非常大的!
| Time Limit: 12000MS | Memory Limit: 65536K | |
| Total Submissions: 38322 | Accepted: 11352 | |
| 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
单调队列有两个重要的性质:
a)从队头到队尾,元素在我们所关注的指标下是递减的(严格递减,而不是非递增),比如查询如果每次问的是窗口内的最小值,那么队列中元素从左至右就应该递增,如果每次问的是窗口内的最大值,则应该递减,依此类推。这是为了保证每次查询只需要取队头元素。
b)从队头到队尾,元素对应的时刻(此题中是该元素在数列a中的下标)是递增的,但不要求连续,这是为了保证最左面的元素总是最先过期,且每当有新元素来临的时候一定是插入队尾。
满足以上两点的队列就是单调队列,首先,只有第一个元素的序列一定是单调队列。
那么怎么维护这个单调队列呢?无非是处理插入和查询两个操作。
对于插入,由于性质b,因此来的新元素插入到队列的最后就能维持b)继续成立。但是为了维护a)的成立,即元素在我们关注的指标下递减,从队尾插入新元素的时候可能要删除队尾的一些元素,具体说来就是,找到第一个大于(在所关注指标下)新元素的元素,删除其后所有元素,并将新元素插于其后。因为所有被删除的元素都比新元素要小,而且比新元素要旧,因此在以后的任何查询中都不可能成为答案,所以可以放心删除。
对于查询,由于性质b,因此所有该时刻过期的元素一定都集中在队头,因此利用查询的时机删除队头所有过期的元素,在不含过期元素后,队头得元素就是查询的答案(性质a),将其返回即可。
由于每个元素都进队出队一次,因此摊销复杂度为O(n)。
poj2823:
1 #include <algorithm> 2 #include <iostream> 3 #include <string.h> 4 #include <stdio.h> 5 #include <math.h> 6 #include <vector> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #define inf 0x7f7f7f7f 11 12 using namespace std; 13 14 const int N=1000010; 15 16 int f[N],q[N],Min[N],Max[N]; 17 int n,k; 18 19 void get_Min() 20 { 21 int l,r; 22 l=1; 23 r=0; 24 for (int i=1;i<=n;i++){ 25 while (l<=r&&f[q[r]]>=f[i]) r--; 26 q[++r]=i; 27 if (i>=k) 28 Min[i-k+1]=f[q[l]]; 29 while (l<=r&&q[l]<=i-k+1) 30 l++; 31 } 32 } 33 34 void get_Max() 35 { 36 int l,r; 37 l=1; 38 r=0; 39 for (int i=1;i<=n;i++){ 40 while (l<=r&&f[q[r]]<=f[i]) r--; 41 q[++r]=i; 42 if (i>=k) 43 Max[i-k+1]=f[q[l]]; 44 while (l<=r&&q[l]<=i-k+1) 45 l++; 46 } 47 } 48 49 void output() 50 { 51 for (int i=1;i<=n-k;i++) 52 printf("%d ",Min[i]); 53 printf("%d\n",Min[n-k+1]); 54 55 for (int i=1;i<=n-k;i++) 56 printf("%d ",Max[i]); 57 printf("%d\n",Max[n-k+1]); 58 59 } 60 61 int main() 62 { 63 while (scanf("%d%d",&n,&k)!=EOF){ 64 for (int i=1;i<=n;i++) 65 scanf("%d",&f[i]); 66 get_Min(); 67 get_Max(); 68 output(); 69 } 70 return 0; 71 }
还有一道题目,UVALive 6609 https://icpcarchive.ecs.baylor.edu/external/66/6609.pdf, 也是比较经典的
1 #include <algorithm> 2 #include <iostream> 3 #include <string.h> 4 #include <stdio.h> 5 #include <math.h> 6 #include <vector> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #define inf 0x7f7f7f7f 11 12 using namespace std; 13 14 const int N=500010; 15 16 int f[N],q[N],sum[N]; 17 int n,x; 18 19 void get_Sum() 20 { 21 int l,r,ans; 22 l=1; 23 r=0; 24 ans=inf; 25 sum[0]=0; 26 for (int i=1;i<=n;i++){ 27 sum[i]=sum[i-1]+f[i]; 28 while (l<=r && sum[i]-sum[q[l]]>=x){ 29 l++; 30 } 31 if (l>1 && i-q[l-1]<ans) 32 ans=i-q[l-1]; 33 while (l<=r && sum[i]<=sum[q[r]]) 34 r--; 35 q[++r]=i; 36 } 37 if (ans!=inf) 38 printf("%d\n",ans); 39 else 40 printf("-1\n"); 41 } 42 int main() 43 { 44 int T; 45 scanf("%d",&T); 46 while (T--){ 47 scanf("%d%d",&n,&x); 48 for (int i=1;i<=n;i++) 49 scanf("%d",&f[i]); 50 get_Sum(); 51 } 52 return 0; 53 }
浙公网安备 33010602011771号