zoukankan      html  css  js  c++  java
  • 单调队列小结

    好久没写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("
    ");
    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("
    ");
    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
    ", ans);
    71 
    72   fclose(stdin), fclose(stdout);
    73   return 0;
    74 }
    View Code
  • 相关阅读:
    final关键字的用法
    多态的理解
    5.13会话技术Cookie---Session
    5.13Junit单元测试-反射-注解
    5.13redis的相关基础
    5月13号
    5.13redis图形化工具---idea中配置redis密码
    5.13谢谢原文博主
    5.13微信登录维护态与获取用户信息思想
    5.12redis
  • 原文地址:https://www.cnblogs.com/nanjoqin/p/9470673.html
Copyright © 2011-2022 走看看