单调队列

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

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

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

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

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

将数组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\n",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\n",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\n",Min);

69    }

70 

71     return 0;

72 }
View Code

 

你可能感兴趣的:(队列)