单调队列小结

好久没写blog了,前几天刷了一些基础题,感觉发现了很多并没有好好学会的东西(盲生!你发现了华点),于是打算更一波:首先是单调队列。

 

Q: 单调队列是什么?

A: 顾名思义,它是一种队列内元素单调递减或单调递增的队列(因为从队头和队尾都可以出队,所以大概是双端队列)。一般使用频率不高,但在有些程序中会有非同寻常的作用,例如可以用来优化DP之类。它的作用一般是高效地维护区间内最优解等,最优选择往往在队首。

 

Q: 这东西怎么搞啊qwq?

A: 一般的原理是:在处理 ans[i] 时,去除冗杂、多余的状态,使得每个状态在队列中只会出现一次;同时维护一个能瞬间得出最优解的队列,减少重新访问的时间;在取得自己所需的值后,为后续的求解做好准备。

 

一、单调队列的性质:

 1、 队列中的元素其对应在原来的列表中的顺序必须是单调递增的。

 2、 队列中所有元素的关系必须有序。

 

二、使用单调队列就涉及到去头删尾,以区间最大值为例:

 1、队列的头一定是在一段时间前就加入了队列,现在的队列头会不会离开了我们处理的区间呢?如果它离我们正在处理的 i 太远了,我们就要把它去掉,去除冗杂的信息。

 2、为了保证队列的递减性,在从列队尾新插入元素 v 时,要考虑队列尾 p 处的值是否大于 v ,如果是,直接将 v 入队 ,此时队列递减性没有消失;如果不是,为了维护递减性,我们做如下考虑:v 是最新值,它的位置是目前最靠后的,它可成为以后的答案,必须留下;p - 1 的值与 v 大小不定,不能冒然删去它;而队列尾的值不但不是最大值,对于以后的情况又不如 v 优,因为 v 相比队列尾更靠后,而且值更大,所以删除队列尾,再同样判断 p - 1 处的值。

  3、 这样,在维护好一个区间正确、严格递减的单调递减队列后,队列头就是当前区间的最大值了。借助这里的最优解,我们还可以由此得到目前所求的最优解(通常此处插入DP方程)。

 

Example_1:[Luogu 1886] 滑动窗口

维护区间最值问题,板子,放一个代码:

 1 #include <cstdio>
 2 #include <cctype>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int maxn = 1000000 + 10;
 7 int n, k, a[maxn], q[maxn], p[maxn], tail, head;
 8 
 9 int main(int argc, const char *argv[])
10 {
11   freopen("nanjolno.in", "r", stdin);
12   freopen("nanjolno.out", "w", stdout);
13 
14   n = read(), k = read();
15   for(int i = 1; i <= n; ++i) a[i] = read();
16   
17   head = 1, tail = 0;
18   for(int i = 1; i <= n; ++i) {
19     while( head <= tail && q[tail] >= a[i] ) --tail;
20     q[++tail] = a[i], p[tail] = i;
21     while( p[head] <= i - k ) ++head;
22     if( i >= k ) printf("%d ", q[head]);
23   }
24   printf("\n");
25   
26   head = 1, tail = 0;
27   for(int i = 1; i <= n; ++i) {
28     while( head <= tail && q[tail] <= a[i] ) --tail;
29     q[++tail] = a[i], p[tail] = i;
30     while( p[head] <= i - k ) ++head;
31     if( i >= k ) printf("%d ", q[head]);
32   }
33   printf("\n");
34   
35   fclose(stdin), fclose(stdout);
36   return 0;
37 }
View Code

 

Example_2:[HAOI 2007] 理想的正方形

很有趣的一道题,二维的滑动窗口233。思路是先对原矩阵每行做维护,再对得到的新矩阵的每一列做维护,得到题意的最优解。

 1 #include <queue>
 2 #include <cstdio>
 3 #include <cctype>
 4 #include <cstring>
 5 #include <iostream>
 6 #include <algorithm>
 7 using namespace std;
 8 
 9 typedef pair<int, int> pairs;
10 
11 const int maxn = 1000 + 10;
12 int n, m, c, mut[maxn][maxn], max_tmp[maxn][maxn], min_tmp[maxn][maxn];
13 int fin_max[maxn][maxn], fin_min[maxn][maxn], q[maxn], p[maxn], head, tail;
14 
15 inline int read() {
16   register char ch = 0; register int w = 0, x = 0;
17   while( !isdigit(ch) ) w |= (ch == '-'), ch = getchar();
18   while( isdigit(ch) ) x = (x * 10) + (ch ^ 48), ch = getchar();
19   return w ? -x : x;
20 }
21 
22 int main(int argc, char const *argv[]) 
23 {
24   freopen("nanjolno.in", "r", stdin);
25   freopen("nanjolno.out", "w", stdout);
26 
27   int ans = 2147483647;
28   scanf("%d%d%d", &n, &m, &c);
29   for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) mut[i][j] = read();
30 
31   for(int i = 1; i <= n; ++i) {
32     head = 1, tail = 0;
33     for(int j = 1; j <= m; ++j) {
34       while( tail - head + 1 && q[tail] >= mut[i][j] ) --tail;
35       q[++tail] = mut[i][j], p[tail] = j;
36       while( p[head] <= j - c ) ++head;
37       if( j >= c ) min_tmp[i][j] = q[head];
38     }
39   }
40   for(int i = 1; i <= n; ++i) {
41     head = 1, tail = 0;
42     for(int j = 1; j <= m; ++j) {
43       while( tail - head + 1 && q[tail] <= mut[i][j] ) --tail;
44       q[++tail] = mut[i][j], p[tail] = j;
45       while( p[head] <= j - c ) ++head;
46       if( j >= c ) max_tmp[i][j] = q[head];
47     }
48   }
49   for(int j = 1; j <= m; ++j) {
50     head = 1, tail = 0;
51     for(int i = 1; i <= n; ++i) {
52       while( tail - head + 1 && q[tail] >= min_tmp[i][j] ) --tail;
53       q[++tail] = min_tmp[i][j], p[tail] = i;
54       while( p[head] <= i - c ) ++head;
55       if( i >= c ) fin_min[i][j] = q[head];
56     }
57   }
58   for(int j = 1; j <= m; ++j) {
59     head = 1, tail = 0;
60     for(int i = 1; i <= n; ++i) {
61       while( tail - head + 1 && q[tail] <= max_tmp[i][j] ) --tail;
62       q[++tail] = max_tmp[i][j], p[tail] = i;
63       while( p[head] <= i - c ) ++head;
64       if( i >= c ) fin_max[i][j] = q[head];
65     }
66   }
67 
68   for(int i = c; i <= n; ++i) for(int j = c; j <= m; ++j)
69     ans = min(ans, fin_max[i][j] - fin_min[i][j]);
70   printf("%d\n", ans);
71 
72   fclose(stdin), fclose(stdout);
73   return 0;
74 }
View Code

 

posted @ 2018-08-13 20:20  南條雪绘  阅读(176)  评论(0编辑  收藏  举报