最近几天接触了单调队列,还接触了单调栈,就总结一下。
其实单调队列,和单调栈都是差不多的数据类型,顾名思义就是在栈和队列上加上单调,单调递增或者单调递减。当要入栈或者入队的时候,要和栈头或者队尾进行比较,满足单调的性质则入队入栈,否则将当前元素删去,直到满足单调性质。
那么问题来了,单调队列,和单调栈有什么用了。最普遍的最重要的作用就是起到优化的作用。当然我目前也只知道这个所用。
先看一道例题:
求一个n序列中,所有长度不大于lmaxin,的连续子序列中,序列和最大的是多少。最容易想到的方法,效率是O(n*lmaxin);

          for(int i=0;i<n;i++){int s=0;for(int j=i;j>=max(0,i-lmaxin+1);j--){s+=a[i];ans=max(ans,s);}}

上面的代码中其实有很多都是重复比较了,所以效率低
单调队列优化的代码

        rear=head=0;for(int i=1;i<=n;i++){while(rear>=front&&s[i]<q[rear-1]){rear--;}q[rear++]=i;while(q[front]<i-m)front++;ans=max(ans,s[i]-s[q[front]]);}

s数组是前缀和,给定一个端点,求端点左边长度为m的区间的最小值就可以了。其实就相当于把原来的长度为m的for循环变成单调队列。区间长度是固定的m,单调队列求解区间的最小值,比暴力的for循环要高效率的多,因为元素只是一进一出,效率是O(n)。那么联系到单调栈,那么单调栈可以优化区间长度不断增加且左端点固定的最大值(个人联想)。

那么经常听到的单调队列优化背包的也很好理解了。给一道题目多重背包单调队列优化,hdu上面不用优化,poj需要优化
http://poj.org/problem?id=1742
hdu AC的代码

#include <iostream>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <stdlib.h>using namespace std;
#define MIN -9999
int dp[100005];
int n;
int m;
int a[100005];
int b[1005];
void ZeroOnePack(int v,int w)
{for(int i=m;i>=w;i--){if(dp[i-w]!=MIN)dp[i]=max(dp[i],dp[i-w]+v);}
}
void CompletePack(int v,int w)
{for(int i=w;i<=m;i++){if(dp[i-w]!=MIN)dp[i]=max(dp[i],dp[i-w]+v);}
}
void MultiplyPack(int v,int w,int c)
{if(c*w>m){CompletePack(v,w);return ;}int k;k=1;while(k<c){ZeroOnePack(k*v,k*w);c=c-k;k<<=1;}ZeroOnePack(c*v,c*w);
}
int main()
{int result;while(scanf("%d%d",&n,&m)!=EOF){result=0;if(n==0&&m==0)break;for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1;i<=n;i++)scanf("%d",&b[i]);for(int i=0;i<=m;i++)dp[i]=MIN;dp[0]=0;for(int i=1;i<=n;i++)MultiplyPack(a[i],a[i],b[i]);for(int i=1;i<=m;i++){if(dp[i]!=MIN)result++;}printf("%d\n",result);}return 0;}

poj 单调队列优化的POJ的代码

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>using namespace std;
#define MAX 100000
int dp[MAX+5];
int a[105];
int c[105];
int n,m;
int rear;
int front;
int queue[MAX+5];
int ans;
int main()
{while(scanf("%d%d",&n,&m)!=EOF){if(n==0&&m==0)break;ans=0;for(int i=0;i<n;i++)scanf("%d",&a[i]);for(int j=0;j<n;j++)scanf("%d",&c[j]);memset(dp,0,sizeof(dp));dp[0]=1;for(int i=0;i<n;i++){if(c[i]==1){for(int j=m;j>=a[i];j--)if(!dp[j]&&dp[j-a[i]])dp[j]=1;}else if(c[i]*a[i]>=m){for(int j=a[i];j<=m;j++)if(!dp[j]&&dp[j-a[i]])dp[j]=1;}else{for(int k=0;k<a[i];k++){rear=0;front=0;int sum=0;for(int j=k;j<=m;j+=a[i]){if((rear-front)==c[i]+1)sum-=queue[front++];queue[rear++]=dp[j];sum+=dp[j];if(!dp[j]&&sum)dp[j]=1;}}}}for(int i=1;i<=m;i++)if(dp[i]) ans++;printf("%d\n",ans);}
}

第一个是倍增方法,第二个是单调队列优化的方法,其实不用分成0 1背包和完全背包,直接用单调队列就可以。多重背包优化,要把根据余数把体积分成几类
参考这个资料的

之间用倍增法写的多重背包都可以用单调队列优化
http://acm.hdu.edu.cn/showproblem.php?pid=2191
可以试试用单调队列进行优化

//
//  main.cpp
//  单调队列优化1
//
//  Created by 陈永康 on 16/1/16.
//  Copyright © 2016年 陈永康. All rights reserved.
//#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>using namespace std;
int c[105];
int w[105];
int v[105];
int dp[105];
int n,m;
int b[105];
int a[105];
int rear,front;
int main()
{int t;scanf("%d",&t);while(t--){memset(dp,0,sizeof(dp));scanf("%d%d",&m,&n);for(int i=0;i<n;i++)scanf("%d%d%d",&w[i],&v[i],&c[i]);for(int i=0;i<n;i++){for(int j=0;j<w[i];j++){rear=front=0;for(int k=0;k<=(m-j)/w[i];k++){int x=k;int y=dp[k*w[i]+j]-k*v[i];while(front<rear&&y>=b[rear-1])rear--;a[rear]=x;b[rear++]=y;while(a[front]<k-c[i])front++;dp[k*w[i]+j]=b[front]+k*v[i];}}}printf("%d\n",dp[m]);}return 0;
}

单调队列不仅可以优化多重背包,可以优化别的DP问题
一道好题目
这是时间超限的代码

//
//  main.cpp
//  单调队列优化3
//
//  Created by 陈永康 on 16/1/19.
//  Copyright © 2016年 陈永康. All rights reserved.
//#include <iostream>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>using namespace std;
#define MAX 1<<30
int dp[2005][2005];
int w,t,MaxP;
int buy[2005];
int sell[2005];
int MaxB[2005];
int MaxS[2005];
int main()
{int cas;scanf("%d",&cas);while(cas--){scanf("%d%d%d",&t,&MaxP,&w);for(int i=1;i<=t;i++)scanf("%d%d%d%d",&buy[i],&sell[i],&MaxB[i],&MaxS[i]);for(int i=1;i<=2000;i++)for(int j=1;j<=2000;j++)dp[i][j]=-MAX;for(int i=0;i<=MaxB[1];i++)dp[1][i]=0-buy[1]*i;for(int i=2;i<=t;i++){for(int j=0;j<=MaxP;j++){for(int k=1;k<=MaxP;k++){if(i-w-1<=0||j-k>MaxB[i]||j<=k)continue;if(dp[i-w-1][k]==MAX)continue;dp[i][j]=max(dp[i][j],dp[i-w-1][k]-buy[i]*(j-k));}for(int k=1;k<=MaxP;k++){if(i-w-1<=0||k<=j||k-j>MaxS[i])continue;if(dp[i-w-1][k]==MAX)continue;dp[i][j]=max(dp[i][j],dp[i-w-1][k]+sell[i]*(k-j));}dp[i][j]=max(dp[i][j],dp[i-1][j]);}}int ans=0;for(int j=0;j<=MaxP;j++)ans=max(ans,dp[t][j]);printf("%d\n",ans);}return 0;
}

状态转移方程不难想到,但是写出来就是时间超限的,用单调队列优化的代码

//
//  main.cpp
//  单调队列优化3
//
//  Created by 陈永康 on 16/1/19.
//  Copyright © 2016年 陈永康. All rights reserved.
//#include <iostream>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>using namespace std;
#define MAX 1<<30
int dp[2005][2005];
int w,t,MaxP;
int buy[2005];
int sell[2005];
int MaxB[2005];
int MaxS[2005];
int a[2005];
int b[2005];
int rear;
int front;
int main()
{int cas;scanf("%d",&cas);while(cas--){scanf("%d%d%d",&t,&MaxP,&w);for(int i=1;i<=t;i++)scanf("%d%d%d%d",&buy[i],&sell[i],&MaxB[i],&MaxS[i]);for(int i=1;i<=2000;i++)for(int j=1;j<=2000;j++)dp[i][j]=-MAX;for(int i=1;i<=t;i++)for(int j=0;j<=min(MaxP,MaxB[i]);j++)dp[i][j]=0-buy[i]*j;for(int i=2;i<=t;i++){for(int j=0;j<=MaxP;j++)dp[i][j]=max(dp[i][j],dp[i-1][j]);if(i-w-1<=0)continue;rear=front=0;b[rear]=dp[i-w-1][0];a[rear]=0;for(int j=1;j<=MaxP;j++){int x=j;int y=dp[i-w-1][j];while(front<=rear&&b[rear]-(j-a[rear])*buy[i]<y)rear--;b[++rear]=y;a[rear]=x;while(front<=rear&&a[front]+MaxB[i]<j)front++;dp[i][j]=max(dp[i][j],b[front]-(j-a[front])*buy[i]);}rear=front=0;b[rear]=dp[i-w-1][MaxP];a[rear]=MaxP;for(int j=MaxP-1;j>=0;j--){int x=j;int y=dp[i-w-1][j];while(front<=rear&&b[rear]+(a[rear]-j)*sell[i]<y)rear--;b[++rear]=y;a[rear]=x;while(front<=rear&&a[front]-MaxS[i]>j)front++;dp[i][j]=max(dp[i][j],b[front]+(a[front]-j)*sell[i]);}}int ans=0;for(int j=0;j<=MaxP;j++)ans=max(ans,dp[t][j]);printf("%d\n",ans);}return 0;
}

说了这么多单调队列的,就说一下单调栈的应用
http://poj.org/problem?id=2082
题目的意思就是给你一系列矩形,求最大的矩形面积
这道题目可以用暴力的方法O(n^2),用单调栈的话就是O(n);
单调栈是递增的,这个单调栈在入栈的时候要合并矩形,合并之后再入栈。
暴力ac的代码

#include <iostream>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>using namespace std;
#define MAX 50000
struct Node
{int w;int h;
}a[MAX+5];
int n;
int ans;
int sum;
int main()
{while(scanf("%d",&n)!=EOF){if(n==-1)break;ans=0;sum=0;for(int i=0;i<n;i++)scanf("%d%d",&a[i].w,&a[i].h);for(int i=0;i<n;i++){sum=0;for(int j=i+1;j<n;j++){if(a[j].h>=a[i].h)sum+=a[i].h*a[j].w;elsebreak;}for(int p=i-1;p>=0;p--){if(a[p].h>=a[i].h)sum+=a[i].h*a[p].w;elsebreak;}sum+=a[i].w*a[i].h;ans=max(ans,sum);}printf("%d\n",ans);}return 0;}

单调栈优化的代码

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <stack>using namespace std;
#define MAX 50000
int n;
struct Node
{int x,y;
}a[MAX+5];
stack<Node> Stack;
int ans;
int main()
{while(scanf("%d",&n)!=EOF){if(n==-1)break;while(!Stack.empty())Stack.pop();ans=0;for(int i=0;i<n;i++)scanf("%d%d",&a[i].x,&a[i].y);Stack.push(a[0]);for(int i=1;i<n;i++){int sum=0;Node term=Stack.top();while(term.y>a[i].y){sum+=term.x;ans=max(ans,sum*term.y);Stack.pop();if(Stack.empty())break;term=Stack.top();}Node temp;temp.x=sum+a[i].x;temp.y=a[i].y;Stack.push(temp);}int sum=0;while(!Stack.empty()){Node term=Stack.top();sum+=term.x;ans=max(ans,sum*term.y);Stack.pop();}printf("%d\n",ans);}
}

这里并不能看出单调栈的求区间最大值的功能,反而是运用了单调栈单调的特性,进行求解。这也是单调栈,单调队列这种数据结构有魅力的地方吧

单调队列,单调栈总结相关推荐

  1. POJ - 3250 Bad Hair Day(单调队列/单调栈)

    题目链接:点击查看 题目大意:给出n只牛,高度参差不齐,所有的牛都朝向右边,他们可以看到右边所有没有遮挡并且比自己低的牛,问每只牛可以看到的牛的数量总和是多少 题目分析:这个题目让求每只牛看到的牛的数 ...

  2. 数据结构录 之 单调队列单调栈。

    队列和栈是很常见的应用,大部分算法中都能见到他们的影子. 而单纯的队列和栈经常不能满足需求,所以需要一些很神奇的队列和栈的扩展. 其中最出名的应该是优先队列吧我觉得,然后还有两种比较小众的扩展就是单调 ...

  3. 入门经典_Chap08_题解总结:极角扫描法 滑动窗口 单调队列 单调栈

    总结  本章主要关注一个重要的问题 – 单调队列和单调栈的使用  同时还有一些其他的问题,如扫描法,递归的思想, 构造, 分治, 二分等 知识点 单调队列 和 单调栈 题目 UVA - 1606 Am ...

  4. 【数据结构】单调栈和单调队列 详解+例题剖析

    算法:单调栈和单调队列 一.单调栈和单调队列 二.单调栈例题 1.模板题入门 2.不懂不要急,看这道题 三.单调队列例题 1.入门 2.进阶 一.单调栈和单调队列 单调栈和单调队列与普通的栈,队列不同 ...

  5. 算法学习12: 单调队列和单调栈

    算法学习12: 单调队列和单调栈 单调队列 单调队列解决的问题: 窗口内最大/最小值的更新结构 单调队列的结构和操作 单调队列的应用 题目一: 生成窗口最大值数组[leetcode 239](http ...

  6. 【C++】单调队列 详解

    今天我们来讲一下单调队列与栈. 这两种数据结构虽然没有在c++的stl中有直接的实现,但是在做题过程中,很容易有单调队列(栈)的使用,尤其是在一些比较难的题目中. 目录 单调队列 1.1 单调队列介绍 ...

  7. tyvj1305 最大子序和 【单调队列优化dp】

    描述 输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大. 例如 1,-3,5,1,-2,3 当m=4时,S=5+1-2+3=7 当m=2或m=3时,S=5+1=6 输 ...

  8. 瑰丽华尔兹--单调队列

    看到这道题,的标签 [我csdn的博客] (https://blog.csdn.net/qq_42421714/article/details/84963779) 我洛谷的博客 说明啊,它可以用单调队 ...

  9. 算法竞赛入门与进阶 (二)单调队列、单调栈

    栈(stack)和队列( queue ) 1.栈的定义:栈是限定仅在表头进行插入和删除操作的线性表(先进后出) 2.队列的定义:队列是一种特殊的线性表,特殊之处在于 它只允许在表的前端(front)进 ...

  10. 单调队列 Monotonic Queue / 单调栈 Monotonic Stack

    2018-11-16 22:45:48 一.单调队列 Monotone Queue 239. Sliding Window Maximum 问题描述: 问题求解: 本题是一个经典的可以使用双端队列或者 ...

最新文章

  1. 找出现有Vector或ArrayList或数组中重复的元素给现有Vector或ArrayList或数组去重
  2. [python]练习之递归和循环实现斐波拉契数列
  3. vuejs 和 element 搭建的一个后台管理界面【收藏】
  4. 压力与动力是否成正比?
  5. 【ElasticSearch】ElasticSearch 中使用衰减函数来完美你的搜索结果
  6. 批处理bat脚本自动配置java的jdk环境变量
  7. Linux下Hadoop分布式系统配置
  8. 被暴击了!22岁本科生开源的后台管理系统,太实用!
  9. 从零开始,学习web前端之HTML5
  10. java info()方法_Java Provider getInfo()用法及代码示例
  11. Echart图实现tooltips循环轮播(方法)
  12. 国际象棋棋盘 java_java绘制国际象棋与中国象棋棋盘
  13. 在网页前端调用exe程序并传参
  14. 网页设计常用色彩搭配表 - 配色表
  15. COPU副主席刘澎:中国开源在局部领域已接近或达到世界先进水平
  16. 数据库基础(常见面试题)
  17. 解决cvxpy报错The solver GLPK_MI is not installed
  18. Oracle Partner Levels and Diamond Partners List
  19. 电子技术基础(三)__电路分析基础__电容元件
  20. html 引入网页title的图标,生成 .ico 图标

热门文章

  1. autodesk系列产品无法安装解决方案
  2. 无货源开网店,淘宝,京东,拼多多,哪个最好?
  3. 云媒体服务器设置在哪个文件夹,安装和使用Jellyfin配置Linux媒体服务器
  4. 计算机科学的本质与操作系统中十条哲学原理
  5. anycast隧道_隧道服务器是什么意思
  6. 利用Python制作第一人称射击小游戏 含源代码
  7. 程序员应知必会的思维模型之 25 普特定律 (Putt‘s Law)
  8. 近红外光激发的上转换核壳微米棒的发光机理
  9. markdown之mermaid
  10. 开源代码基于深度学习的超分辨率如何让大脑显微镜成像去除毛刺