【2022.11.15】2. 队列(2)--- 洛谷P1886、hdu1003

本文介绍了双端队列的概念,包括手写双端队列的基本操作和STL实现,并通过洛谷P1886题解详细阐述了单调队列在滑动窗口问题中的应用,以及如何利用单调队列解决hdu 1003的最大子序列和问题。

正文

一、特殊的队列

1. 双端队列

字面意思,可以对两端进行 poppush 操作的队列

(1)手写双端队列简述
  1. const int N = 1e6 + 10; — 队列大小
  2. int que[N], head, tail; — 队列,队首队尾指针
  3. head++; — 弹出队首
  4. que[--head] = data; — 数据 data 从队首入队
  5. que[head]; — 读取队首数据
  6. tail--; — 弹出队尾
  7. que[++tail] = data; — 数据 data 从队尾入队
(2)STL双端队列

#include <deque>

  1. dq[i] — 访问元素
  2. dq.front() — 返回队首
  3. dq.back() — 返回队尾
  4. dq.pop_back() — 弹出队尾
  5. dq.pop_front() — 弹出队首
  6. dq.push_back(e) — 元素 e 从队尾入队
  7. dp.push_front(e) — 元素 e 从队首入队

2. 单调队列与滑动窗口

洛谷P1886

洛谷P1886

题目解析

给定一串序列以及一个窗口大小,要求输出窗口中的最大值与最小值

思路分析

使用单调队列,分两次遍历该序列,时间复杂度 O ( 2 N ) O(2N) O(2N),第一次输出最小值,第二次输出最大值
输出最小值:

  1. 使用双端队列从队尾存入元素
  2. 当存入元素比队尾元素小的时候,就将队尾元素pop
  3. 直到该元素不再比队尾元素小时,再push进入该队列
  4. 存入数组下标,方便判断是否还在窗口中
  5. 从队首判断是否属于窗口范围,不属于就pop掉
  6. 最后输出队首即可,该元素就是 当前窗口中最小值

输出最大值:
具体思路同上,不过在第二步第三步中得到比大小,要选取更大的值

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

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;
}

在这里插入图片描述

其它

还有其他的方法,动态规划,单调队列优化的动态规划,在后面的动态规划章节中再来学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值