单调队列

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

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

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

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

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

将数组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
原文地址:https://www.cnblogs.com/justPassBy/p/4518345.html