正文
一、特殊的队列
1. 双端队列
字面意思,可以对两端进行 pop 和 push 操作的队列
(1)手写双端队列简述
const int N = 1e6 + 10;— 队列大小int que[N], head, tail;— 队列,队首队尾指针head++;— 弹出队首que[--head] = data;— 数据data从队首入队que[head];— 读取队首数据tail--;— 弹出队尾que[++tail] = data;— 数据data从队尾入队
(2)STL双端队列
#include <deque>
dq[i]— 访问元素dq.front()— 返回队首dq.back()— 返回队尾dq.pop_back()— 弹出队尾dq.pop_front()— 弹出队首dq.push_back(e)— 元素e从队尾入队dp.push_front(e)— 元素e从队首入队
2. 单调队列与滑动窗口
洛谷P1886
题目解析
给定一串序列以及一个窗口大小,要求输出窗口中的最大值与最小值
思路分析
使用单调队列,分两次遍历该序列,时间复杂度
O
(
2
N
)
O(2N)
O(2N),第一次输出最小值,第二次输出最大值
输出最小值:
- 使用双端队列从队尾存入元素
- 当存入元素比队尾元素小的时候,就将队尾元素pop
- 直到该元素不再比队尾元素小时,再push进入该队列
- 存入数组下标,方便判断是否还在窗口中
- 从队首判断是否属于窗口范围,不属于就pop掉
- 最后输出队首即可,该元素就是 当前窗口中 的 最小值
输出最大值:
具体思路同上,不过在第二步第三步中得到比大小,要选取更大的值
AC代码
#include <bits/stdc++.h>
using namespace std;
#define ONLINE_JUDGE
const int N = 1000010;
int n, m;
int a[N];
deque<int> q;
inline void problem() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) {
while (!q.empty() && a[q.back()] > a[i]) {
q.pop_back();
}
q.push_back(i);
if (i >= m) {
while (!q.empty() && q.front() <= i - m) q.pop_front();
printf("%d ", a[q.front()]);
}
}
printf("\n");
q.clear();
for (int i = 1; i <= n; ++i) {
while (!q.empty() && a[q.back()] < a[i]) q.pop_back();
q.push_back(i);
if (i >= m) {
while (!q.empty() && q.front() <= i - m) q.pop_front();
printf("%d ", a[q.front()]);
}
}
printf("\n");
}
int main()
{
// ios::sync_with_stdio(0);
// cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
problem();
return 0;
}

3. 单调队列与最大子序列问题
hdu 1003
题目解析
求某序列中,和最大的子序列
思路分析
使用贪心, O ( N ) O(N) O(N) 时间复杂度,逐个扫描元素求和,如果和变为负数,在后面的求和过程中就会对和起到减小的作用,而这个负数区间内,必定存在该区间的最大值。当该区间和为负数之后,取下一坐标重新计算sum。该过程的所有最大和用maxsum记录下来,同时记录区间坐标
AC代码
#include <bits/stdc++.h>
using namespace std;
#define ONLINE_JUDGE
const int N = 100010;
const int INF = 0x7fffffff;
int a[N];
int n;
inline void solve() {
scanf("%d", &n);
int st = 1, ed = 1, p = 1;
int sum = 0;
int maxsum = -INF;
for (int i = 1; i <= n; ++i) {
int a;
scanf("%d", &a);
sum += a;
if (sum > maxsum) {
maxsum = sum;
st = p;
ed = i;
}
if (sum < 0) {
sum = 0;
p = i + 1;
}
}
printf("%d %d %d", maxsum, st, ed);
}
inline void problem() {
int t;
scanf("%d", &t);
int i = 1;
while (t--) {
printf("Case %d:\n", i);
++i;
solve();
if (t != 0) printf("\n\n");
else printf("\n");
}
}
int main()
{
// ios::sync_with_stdio(0);
// cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
problem();
return 0;
}

其它
还有其他的方法,动态规划,单调队列优化的动态规划,在后面的动态规划章节中再来学习
本文介绍了双端队列的概念,包括手写双端队列的基本操作和STL实现,并通过洛谷P1886题解详细阐述了单调队列在滑动窗口问题中的应用,以及如何利用单调队列解决hdu 1003的最大子序列和问题。
--- 洛谷P1886、hdu1003&spm=1001.2101.3001.5002&articleId=127870587&d=1&t=3&u=4676346cc81b444f80c9b6c66ab75bb5)

被折叠的 条评论
为什么被折叠?



