单调队列或单调栈的学习及认识
来源:互联网 发布:俄国构成主义知乎 编辑:程序博客网 时间:2024/04/28 00:03
单调队列或单调栈的学习及认识
。。。顾名思义,数据是具有某方面的单调性质的(单增或单减等)。单调队列一般是用于优化动态规划方面问题的一种特殊数据结构,且多数情况是与定长连续子区间问题相关联。
一、数据结构的认识
1.双端队列deque
双端队列是一种线性表,遵守先进先出的原则。其支持下面四种操作:(1)从队首删除(2)从队尾删除(3)从队尾插入(4)查询线性表中任一元素的值
2.单调队列
单调队列是种特殊双端队列,其内部元素具有单调性。最常用的是最大队列与最小队列,其内部元素分别单增或单减。有如下操作:(1)插入:若新元素从队尾插入会破坏单调性,则删除队尾元素,直到不在破坏单调性为止,再将其加入单调队列尾。(2)获取最优(max,min)值:访问队首元素。
二、例题
1.自愿者选拔
题目大意:有一群自愿者陆陆续续的来排队,队首的人在某些时刻才会离开,每个人有一个int型的人品值,现在就问在当前队列中人品最大的是多少,没有的话输出-1.
1.C name rp 名字为name人品为rp的人排到队尾2.G 队首的人出队3.Q 询问当前队列中rp最高的
分析:这个就是一个连续区间最大值的问题。这时单调队列里面的人出了先后顺序是单增的,其rp是单减的(head to tail)。用一个out变量记录目前出队了多少人,in记录进来了多少了,
当某次询问时,如果out <= in时,显然输出-1,不然就输出队首的人的rp值。
因为队列里面做调整的时候,对首的人原位置并不是第一,所以要记录些这个每个人的原位置。这样就可以O(n)的解决这个问题了。
const int maxn = 1e6 + 10;struct node { char *name;//名字 int rp;//rp值 int pos;//原队列中的位置 node() {} node(char c[], int rp,int pos) { this->name = c; this->rp = rp; this->pos = pos; } }p[maxn];int main(int argc, const char * argv[]){ // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int t; cin >> t; char name[10]; int rp; while(t--) { char op[10]; int head = 0, tail = 0; int out = 0; int pos = 0; while(scanf("%s", op) != EOF) { if (strcmp(op, "START") == 0) continue; if (op[0] == 'C') { //这里做插入,在先后顺序不受影响的情况下,删除rp值下的 scanf("%s %d", name, &rp); while(head < tail && p[tail-1].rp < rp) tail--; p[tail++] = node(name, rp, ++pos); }else if (op[0] == 'G') { //这个地方做删除,原队列中是删除了,但是单调队中是否 //删除对手元素必须观察你在原队列中的所属位置 //并且删除的值队后面的询问不会产生影响的 out++; if (p[head].pos == out) head++; }else if (op[0] == 'Q'){ if (head == tail) printf("-1\n"); else printf("%d\n", p[head].rp); }else if (op[0] == 'E') break; } } return 0;}
2.Sliding Windows
题目大意:给定n,k值和n个数字,问数列中从左到右每连续k个数的最大最小值。
分析:这又是一个定长区间的最值问题。对于区间[L, R]和[L + 1, R + 1],当上一个区间访问完后,删掉a[L]是不会对后面区间的访问产生影响的,这时位置和值都呈现出单调性了。当访问R时,对于队首的元素,如果不在[L,R]范围内的话就剔除队列,对于尾部元素则是小于a[R]的剔除队列。
const int maxn = 1e6 + 10;int n, m;struct node { int pos, value; node() {} node(int pos,int value) { this->pos = pos; this->value = value; }}p[maxn];int a[maxn];void get_max() { int head = 0, tail = 0; for (int i = 1;i <= n;++i) { while(head < tail && i - p[head].pos >= m) head++; while(head < tail && a[i] > p[tail - 1].value) tail--; p[tail++] = node(i,a[i]); if (i >= m) printf("%d%c", p[head].value, i == n?'\n':' '); }}void get_min() { int head = 0, tail = 0; for (int i = 1;i <= n;++i) { while(head < tail && i - p[head].pos >= m) head++; while(head < tail && a[i] < p[tail - 1].value) tail--; p[tail++] = node(i, a[i]); if (i >= m) printf("%d%c", p[head].value, i == n?'\n':' '); }}int main(int argc, const char * argv[]){ // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); while(scanf("%d %d", &n, &m) != EOF) { for (int i = 1;i <= n;++i) scanf("%d", &a[i]); get_min(); get_max(); } return 0;}
3.Max Sum of Max-K-sub-sequence
题目大意:给出一个循环数列a[1]…a[n],a[1]与a[n]相邻。然后问数列中连续不超过k个元素的最大和是多少并打印出Maxsum始末位置。
分析:这题先得变换下表达方式,a[j+1] + … + a[i] = sum[i] - sum[j],sum[i]表示a[1]到a[i]的和。对于给定的i而言,ans = max(sum[i] - sum[j]) && (i - j <= k - 1),这时sum[i]是个定值,求ans的最大等同于求sum[j]的最小值i - k <= j <= i。就相当于维护定长区间的最小值问题了,每次更新答案的时候记录下始末位置就好了。
#include <stdio.h>#include <stdlib.h>const int maxn = 1e5 + 10;const int oo = -2099900000;int q[maxn<<1];int s[maxn<<1];int nu[maxn<<1];int n, k;int main(int argc, const char * argv[]){ int t; scanf("%d", &t); int i; int ans, first, second; int head, tail; while(t--) { scanf("%d %d", &n, &k); s[0] = 0; for (i = 1;i <= n;++i) { scanf("%d", &nu[i]); s[i] = s[i-1] + nu[i]; } for (i = n + 1;i <= n + k - 1;++i) s[i] = s[i - 1] + nu[i - n]; head = 0, tail = 0; ans = oo; for (i = 1;i <= n + k - 1;++i) { //对与i而言,我们需要把其会用到的状态先加入到队列 //s[i-1],队列中的元素值,必须是当前考虑状态能用到的 while(head < tail && s[i - 1] < s[q[tail - 1]]) tail--; while(head < tail && i - q[head] > k) head++; q[tail++] = i - 1; if (s[i] - s[q[head]] > ans) { ans = s[i] - s[q[head]]; first = q[head] + 1; second = i; } } if (second > n) second -= n; printf("%d %d %d\n", ans, first, second); } return 0;}
- 单调队列或单调栈的学习及认识
- 单调队列的学习
- 单调栈 单调队列
- 单调队列的学习 pku2823
- 单调队列 单调栈总结
- 单调队列与单调栈
- 单调队列,单调栈总结
- zjnu1735BOB (单调队列,单调栈)
- 单调栈/单调队列/RMQ
- 单调栈以及单调队列
- 单调队列 和 单调栈
- 单调队列 和 单调栈
- 单调栈与单调队列
- 单调栈及单调队列基础与运用
- 单调队列学习 PKU2823
- 单调队列学习
- 单调队列学习
- 单调队列学习小结
- Apache+tomcat的整合
- opencv2.4.9在vs2013中丢失opencv_core249d.dll问题及解决【转】
- Android进阶之自定义View实战(一)仿iOS UISwitch控件实现
- STL容器总结之deque
- 内存分析工具MAT的使用
- 单调队列或单调栈的学习及认识
- STL容器总结之stack和queue
- mvc控制器的action参数问题
- POJ-1308-Is It A Tree?(并查集 判断树)
- 基础级 - sweetAlert
- docker
- c++中指针和引用注意点整理
- LeetCode:Pascal's Triangle II
- MySQL 主从