单调队列 POJ 2823+单调栈 HDU 1506
来源:互联网 发布:ug汽车大模具编程刀路 编辑:程序博客网 时间:2024/05/18 02:34
转自:http://blog.csdn.net/jane_jxr/article/details/68962534
单调队列就是用数组拉模拟队列的性质,保证队列里的元素是单调递增或单调递减,时间复杂度O(n)
单调队列解决的是区间最小(最大)值。实现方法是:求区间最小(最大)值,就维护一个递增的双端队列,队中保存原始序列的标号,当即将入队的元素的值比队尾的元素的值小(大)的时候就不断弹掉队尾,知道出现比它更小的值,当即将入队的元素队首元素的跨度(即将入队元素的序号到队首元素序列的区间)大于规定区间时就不断弹掉队首,直到跨度小于或等于所规定的区间。如此可保证队首元素为最小(最大)值,(但不能保证队尾就是原始序列中的最大(最小)值),并维护区间长度。PS:借鉴别人的描述,大概就是这个道理
找区间最大最小值也能用RMQ,区间最值查询来写,但没有单调栈简单,复杂度低。
POJ 2823(单调队列):http://poj.org/problem?id=2823
eg:8 3(n个数,找区间范围为k的最大值和最小值)1 3 -1 -3 5 3 6 7
每次进出队列的是(数字,下标),下标是用来限制区间范围,删除队列有、头部无用的元素,下面来模拟这个过程
找一段区间的最小值,维护单调递增的队列
(1,1)
(1,1)(3,2)
(-1,3)
(-3,4)
(-3,4)(5,5)
(-3,4)(3,6)
(3,6)(6,7)//其中(-3,4)已经超过当前要找的区间范围了(7-3+1=5,当前要找的范围为【5,7】),属于无用元素,删除
(3,6)(5,7)(7,8)
找一段区间的最大值,原理是一样的,维护单调递减的队列,由于队列不能删除队尾元素,所以我们用数组来模拟的
自己可以手动模拟一边,这样才会更深的了解
#include<stdio.h>#include<stdlib.h>#include<iostream>#include<algorithm>#include<math.h>#include<map>#include<queue>#include<stack>#include<string.h>typedef long long LL;using namespace std;#define INF 0x3f3f3f3fconst int maxn=1000005;const double eps=0.0001;struct node{ int x,pos;} v[maxn];int mx[maxn],mn[maxn],n,m,a[maxn];void getmin()//维护单调递增的队列,找区间最小值{ int head=1,tail=0,i; for(i=1; i<m; i++) { while(head<=tail&&v[tail].x>a[i]) tail--; v[++tail].x=a[i],v[tail].pos=i; } for(; i<=n; i++) { while(head<=tail&&v[tail].x>a[i]) tail--; v[++tail].x=a[i],v[tail].pos=i; while(v[head].pos<i-m+1) head++; mn[i-m+1]=v[head].x; }}void getmax()//维护单调递减的队列,找区间最大值{ int head=1,tail=0,i; for(i=1; i<m; i++) { while(head<=tail&&v[tail].x<a[i]) tail--; v[++tail].x=a[i],v[tail].pos=i; } for(; i<=n; i++) { while(head<=tail&&v[tail].x<a[i]) tail--; v[++tail].x=a[i],v[tail].pos=i; while(v[head].pos<i-m+1) head++; mx[i-m+1]=v[head].x; }}int main(){ scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) scanf("%d",&a[i]); getmin(); for(int i=1; i<n-m+1; i++) cout<<mn[i]<<" "; cout<<mn[n-m+1]<<endl; getmax(); for(int i=1; i<n-m+1; i++) cout<<mx[i]<<" "; cout<<mx[n-m+1]<<endl;}
HDU 1506:http://acm.hdu.edu.cn/showproblem.php?pid=1506
单调栈:顾名思义,就是单调递增或单调减的栈,跟单调队列差不多,但它只用到一端。时间复杂度O(n)
性质:1、单调栈里的元素具有单调性
2、元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除
3、使用单调栈可以找到元素向左遍历第一个比他小的元素(单调递增的栈),也可以找到元素向左遍历第一个比他大的元素(单调递减的栈)。
单调栈解决的是以某个值为最小(最大)值的最大区间。实现方法是:求最小值(最大值)的最大区间,维护一个递增(递减)的栈,当遇到一个比栈顶小的值的时候开始弹栈,弹栈停止的位置到这个值的区间即为此值左边的最大区间;同时,当一个值被弹掉的时候也就意味着比它更小(更大)的值来了,也可以计算被弹掉的值得右边的最大区间。PS:借鉴别人的描述,大概就是这个道理
#include<stdio.h>#include<stdlib.h>#include<iostream>#include<algorithm>#include<math.h>#include<map>#include<queue>#include<stack>#include<string.h>typedef long long LL;using namespace std;#define INF 0x3f3f3f3fconst int maxn=100005;const double eps=0.0001;struct node{ LL h,w;//h表示高度,w是不在栈里,但最小高度是h的有多少个,即:宽度} c[maxn];stack<node>s;int main(){ int n; while(~scanf("%d",&n)&&n) { LL ans=0,cnt=0; while(!s.empty()) s.pop(); c[0].h=0; c[0].w=0; s.push(c[0]); for(int i=1; i<=n; i++) { scanf("%d",&c[i].h); c[i].w=1; } for(int i=1; i<=n; i++) { if(c[i].h>s.top().h) { c[i].w=1; s.push(c[i]); } else { cnt=0; while(!s.empty()&&s.top().h>c[i].h) { ans=max(ans,(cnt+s.top().w)*s.top().h); cnt+=s.top().w;//cnt表示要弹出的这个高度作用的范围(包括同一个栈中,已经弹出宽度的总和) //栈底的元素肯定能作用整个栈中所有高度的宽度总和(这点要好好理解),因为栈是单调递增的 s.pop(); } c[i].w=cnt+1; s.push(c[i]); } } cnt=0; while(!s.empty()) { ans=max(ans,(cnt+s.top().w)*s.top().h); cnt+=s.top().w; s.pop(); } printf("%lld\n",ans); } return 0;}
0 0
- 单调队列 POJ 2823+单调栈 HDU 1506
- 单调队列 POJ 2823+单调栈 HDU 1506
- POJ-2823 && POJ-3250 (单调队列 && 单调栈)
- Poj 2823 (单调队列)
- poj 2823【单调队列】
- POJ 2823 单调队列
- POJ-2823单调队列
- POJ 2823 单调队列
- poj 2823 单调队列
- 单调队列 POJ 2823
- poj 2823 单调队列
- POJ 2823 单调队列
- poj 2823 单调队列
- poj 2823 单调队列
- POJ 2823 单调队列
- poj 2823 单调队列
- POJ 2823 单调队列
- POJ 2823 //单调队列
- 最长上升子序列
- Linux 查看端口占用
- 在进行USB CDC类开发时,无法发送64整数倍的数据(续)
- 学习HTML语法的过程——第一章
- 动态规划:采药
- 单调队列 POJ 2823+单调栈 HDU 1506
- <Android 开源库> FlycoTabLayout 从头到脚
- poj 3009 Curling 2.0
- hihocoder 1142 三分法求极值
- 如何控制android中ImageView的位置
- python 将对象设置为可迭代有两种实现方式
- Google数据分析
- 小白训练Day3
- 私人笔记