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

    单调队列,就是队列里的元素是单调递增或者单调递减的。

    那就有人问了,这和优先队列有什么区别。

    单调队列里的单调递增(递减)不止是值的单调递增(递减),下标也是单调递增的。

    我们来看单调队列怎么维护的,就知道单调队列是什么东西了。

    这里以单调递增队列为例。

    将数组a[1->n]里面的元素依次入队列。   如果要入队的元素大于队尾元素,那么就将该元素入队列, 如果该元素小于队伍元素,

    那么就将队尾元素不断地出队列,直到队伍元素大于该元素,或者队列为空为止。

    所以单调递增队列里面的元素不止值是递增的,下标也是递增的

    foj1894

    要我们求队列中的最大值,因为队列是无序的,所以每次查询的复杂度都是O(N),这肯定是不行的

    所以我们只要维护一个单调递减队列就行了(因为单调递减队列可以保留最大值,而单调递增队列不行,比如 1 2 4 3  如果用单调递增队列,那么当3插入时,队列中只有3)

    对于Q命令,如果队列不为空,我们只要输出队首元素即可。

    对于G命令,将cnt2++, 然后将队列中id<=cnt2的元素出队列

    对于C命令, 即维护一个单调队列

    为什么当加入一个元素时,当队尾元素小于插入元素时,队尾元素可以删去呢。 因为插入的元素比队尾元素大,且序号比队尾元素更靠后,

    所以当该元素存在时,是不会选取队尾元素的,即该元素是更优的,所以可以删除队尾元素

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include <algorithm>
     5 #include <iostream>
     6 #include <queue>
     7 #include <stack>
     8 #include <vector>
     9 #include <map>
    10 #include <set>
    11 #include <string>
    12 #include <math.h>
    13 using namespace std;
    14 #pragma warning(disable:4996)
    15 typedef long long LL;                   
    16 const int INF = 1<<30;
    17 /*
    18 
    19 */
    20 struct node
    21 {
    22     int value;
    23     int id;
    24 };
    25 node q[1000000+10];
    26 int head,tail;
    27 int main()
    28 {
    29    int t,i,rp,cnt,cnt2;
    30    char str[111];
    31    scanf("%d",&t);
    32    while(t--)
    33    {
    34         scanf("%s",str);
    35         head = tail = cnt = cnt2 = 0;
    36         while(true)
    37         {
    38             scanf("%s",str);
    39             if(str[0]=='E')
    40                 break;
    41             if(str[0]=='C')
    42             {
    43                 cnt++;
    44                 scanf("%s%d",str,&rp);
    45                 
    46                 while(tail>head && q[tail-1].value < rp)
    47                 {
    48                     tail--;
    49                 }
    50                 q[tail].id = cnt;
    51                 q[tail++].value = rp;
    52                 
    53                 
    54             }
    55             else if(str[0]=='G')
    56             {
    57                 cnt2++;
    58                 while(head<tail && q[head].id<=cnt2)
    59                     head++;
    60             }
    61             else
    62             {
    63                 if(head!=tail)
    64                     printf("%d
    ",q[head].value);
    65                 else
    66                     puts("-1");
    67             }
    68         }
    69    }
    70     return 0;
    71 }
    View Code

     烽火传递

    描述 Description  

        烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情。在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价。为了使情报准确的传递,在m个烽火台中至少要有一个发出信号。现输入n、m和每个烽火台发出的信号的代价,请计算总共最少需要话费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递!!!

    输入格式 Input Format

             第一行有两个数n,m分别表示n个烽火台,在m个烽火台中至少要有一个发出信号。

             第二行为n个数,表示每一个烽火台的代价。

    输出格式 Output Format     

            一个数,即最小代价。       

    样例

    5 3

    1 2 5 6 2

    4

    dp[i] 表示点燃第i个烽火台所需要的最小代价

    那么dp[i] = min{dp[j] | i-m<=j <i } + a[i]

    那么代码如下

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include <algorithm>
     5 #include <iostream>
     6 #include <queue>
     7 #include <stack>
     8 #include <vector>
     9 #include <map>
    10 #include <set>
    11 #include <string>
    12 #include <math.h>
    13 using namespace std;
    14 #pragma warning(disable:4996)
    15 typedef long long LL;                   
    16 const int INF = 1<<30;
    17 /*
    18 
    19 */
    20 const int N = 1000 + 10;
    21 int a[N];
    22 int dp[N];
    23 void solve(int n, int m)
    24 {
    25     for(int i=0; i<=n; ++i)
    26         dp[i] = INF;
    27     dp[0] = 0;
    28     /*时间复杂度是n*m*/
    29     for(int i=1; i<=n; ++i)
    30         for(int j=i-m; j<i; ++j)
    31         {
    32             if(j<0) continue;
    33             dp[i] = min(a[i]+dp[j],dp[i]);
    34         }
    35 }
    36 int main()
    37 {
    38    int n,m,i;
    39    while(scanf("%d%d",&n,&m)!=EOF)
    40    {
    41         for(i=1; i<=n; ++i)
    42             scanf("%d",&a[i]);
    43         solve(n,m);
    44         int Min = INF;
    45         for(i=1; i<=n; ++i)
    46             printf("%d ",dp[i]);
    47         puts("");
    48         for(i=(n-m+1); i<=n; ++i)
    49             Min = min(Min, dp[i]);
    50         printf("%d
    ",Min);
    51    }
    52 
    53     return 0;
    54 }
    View Code

     从中可以看出,求解dp[i]的时候,我们需要求解出区间[i-m,i)的最小值, 所以这个最小值可以用单调递增队列来维护, 

    求区间最小值需要O(n)的时间复杂度,使用单调递增队列来维护,那么只需要O(1)的复杂度, 所以这就是为什么说使用单调队列,能够将时间复杂度降低1维

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include <algorithm>
     5 #include <iostream>
     6 #include <queue>
     7 #include <stack>
     8 #include <vector>
     9 #include <map>
    10 #include <set>
    11 #include <string>
    12 #include <math.h>
    13 using namespace std;
    14 #pragma warning(disable:4996)
    15 typedef long long LL;                   
    16 const int INF = 1<<30;
    17 /*
    18 
    19 */
    20 const int N = 1000 + 10;
    21 struct node
    22 {
    23     int value;
    24     int id;
    25 }q[N];
    26 int head,tail;
    27 int a[N];
    28 int dp[N];
    29 void solve(int n, int m)
    30 {
    31     head = tail = 0;
    32     for(int i=0; i<=n; ++i)
    33         dp[i] = INF;
    34     dp[0] = 0;
    35     q[tail].value = 0;
    36     q[tail++].id = 0;
    37     for(int i=1; i<=n; ++i)
    38     {
    39         /*for(int j=i-m; j<i; ++j)
    40         {
    41             if(j<0) continue;
    42             dp[i] = min(a[i]+dp[j],dp[i]);//这里要得到dp[j]的最小值,我们可以用单调递增队列来维护,哈哈哈
    43         }*/
    44         while(head<tail && q[head].id < i-m)
    45             head++;
    46         dp[i] = q[head].value + a[i];
    47         while(head<tail && q[tail-1].value > dp[i])
    48             tail--;
    49         q[tail].value = dp[i];
    50         q[tail++].id = i;
    51         
    52     }    
    53 }
    54 int main()
    55 {
    56    int n,m,i;
    57    while(scanf("%d%d",&n,&m)!=EOF)
    58    {
    59         for(i=1; i<=n; ++i)
    60             scanf("%d",&a[i]);
    61         solve(n,m);
    62         int Min = INF;
    63         for(i=1; i<=n; ++i)
    64             printf("%d ",dp[i]);
    65         puts("");
    66         for(i=(n-m+1); i<=n; ++i)
    67             Min = min(Min, dp[i]);
    68         printf("%d
    ",Min);
    69    }
    70 
    71     return 0;
    72 }
    View Code
  • 相关阅读:
    LeetCode 623. Add One Row to Tree
    LeetCode 894. All Possible Full Binary Trees
    LeetCode 988. Smallest String Starting From Leaf
    LeetCode 979. Distribute Coins in Binary Tree
    LeetCode 814. Binary Tree Pruning
    LeetCode 951. Flip Equivalent Binary Trees
    LeetCode 426. Convert Binary Search Tree to Sorted Doubly Linked List
    LeetCode 889. Construct Binary Tree from Preorder and Postorder Traversal
    LeetCode 687. Longest Univalue Path
    LeetCode 428. Serialize and Deserialize N-ary Tree
  • 原文地址:https://www.cnblogs.com/justPassBy/p/4518345.html
Copyright © 2011-2022 走看看