单调队列,就是队列里的元素是单调递增或者单调递减的。
那就有人问了,这和优先队列有什么区别。
单调队列里的单调递增(递减)不止是值的单调递增(递减),下标也是单调递增的。
我们来看单调队列怎么维护的,就知道单调队列是什么东西了。
这里以单调递增队列为例。
将数组a[1->n]里面的元素依次入队列。 如果要入队的元素大于队尾元素,那么就将该元素入队列, 如果该元素小于队伍元素,
那么就将队尾元素不断地出队列,直到队伍元素大于该元素,或者队列为空为止。
所以单调递增队列里面的元素不止值是递增的,下标也是递增的
要我们求队列中的最大值,因为队列是无序的,所以每次查询的复杂度都是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\n",q[head].value); 65 else 66 puts("-1"); 67 } 68 } 69 } 70 return 0; 71 }
烽火传递
描述 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\n",Min); 51 } 52 53 return 0; 54 }
从中可以看出,求解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\n",Min); 69 } 70 71 return 0; 72 }