【算法竞赛】队列

队列相关概念

队列中的数据存取方式是“先进先出”,只能向队尾插入数据,从队头移出数据.

队列的原型在生活中很常见,如食堂打饭的队伍,先到先服务.队列有两种实现方式:链队列和循环队列,如图1.2所示.
在这里插入图片描述
链队列可以看作单链表的一种特殊情况,用指针把各个节点连接起来.

循环队列是一种顺序表,使用一组连续的存储单元依次存放队列元素,用两个指针head和tail分别指示队头元素和队尾元素,当head和tail走到底时,下一步回到开始的位置,从而在这组连续空间内循环.

循环队列能解决溢出问题.如果不循环,head和tail都一直往前走,可能会走到存储空间之外,导致溢出.

队列和栈的主要问题是查找较慢,需要从头到尾一个个查找,在某些情况下可以用优先队列,让优先级最高(最大的数或最小的数)先出队列.

竞赛中一般用STLqueue或手写静态数组实现队列.

STL queue

STLqueue的主要操作如下:

(1)queue< Type >q: 定义队列,Type为数据类型,如int、float、char等.
(2)q.push(item): 把item放进队列.
(3)q.front(): 返回队首元素,但不会删除.
(4)q.pop(): 删除队首元素.
(5)q.back(): 返回队尾元素.
(6)q.size(): 返回元素个数.
(7)q.empty():检查队列是否为空.

例1.2 机器翻译
在这里插入图片描述
在这里插入图片描述
下面是STLqueue的代码,由于不用自己管理队列,代码很简洁.
注意检查内存中是否有单词的方法.如果一个一个地暴力搜索,太慢;如果用哈希算法,不仅很快,而且代码简单.

#include<bits/stdc++.h>
using namespace std;int main() {int m, n;scanf("%d %d", &m, &n);queue<int> mem;int Hash[1003] = {0};int cnt = 0;while (n--) {int en;scanf("%d", &en);if (!Hash[en]) {++cnt;mem.push(en);Hash[en] = 1;while (mem.size() > m) {Hash[mem.front()] = 0;mem.pop();}}}printf("%d\n", cnt);return 0;
}

手写循环队列

下面是循环队列的手写模板.

代码中给出了静态分配空间和动态分配空间两种方式(动态分配实现放在注释中).

竞赛中一般用静态分配.

#include<bits/stdc++.h>
// 定义队列大小
#define N 1003 
// 用哈希检查内存中有没有单词
int Hash[N]={0}; // 定义循环队列结构体
struct myqueue
{int data[N];// 如果动态分配,可以这样写: int * data;int head;int rear;// 初始化队列bool init(){// 如果动态分配,这样写: Q.data=(int*) malloc(N * sizeof(int));data = new int[N];if (!data) return false;head = rear = 0;return true;}// 返回队列长度int size() {return (rear - head + N) % N;//重点}// 判断队列是否为空bool empty() {return size() == 0;}// 队尾插入新元素bool push(int e) {if ((rear + 1) % N == head) return false;data[rear] = e;rear = (rear + 1) % N;return true;}// 删除队头元素,并返回它bool pop(int &e) {if (head == rear) return false;e = data[head];head = (head + 1) % N;//重点-循环链表的表征return true;}// 返回队首,但不删除int front() {return data[head];}
};int main() {Queue Q;Q.init();int m, n;std::cin >> m >> n;int cnt = 0;while (n--) {int en;std::cin >> en;if (!Hash[en]) {// 如果内存中没有这个单词++cnt;Q.push(en);Hash[en] = 1;while (Q.size() > m) {int tmp;Q.pop(tmp);Hash[tmp] = 0;}}}std::cout << cnt << std::endl;return 0;
}

双端队列和单调队列

概念

前面的队列很“规矩”,队列的元素都是“先进先出”,队头只能弹出,队尾只能进入.

有没有不那么规矩的队列呢?

这就是双端队列:双端队列是一种具有队列和栈性质的数据结构,它能在两端进行插入和删除,而且也只能在两端插入和删除.

更可靠的编码可以用STL的双端队列deque,它的用法如下。

(1)dq[i]:返回队列中下标为i的元素。
(2)dq.front():返回队头。
(3)dq.back():返回队尾。
(4)dq.pop_back():删除队尾,不返回值。
(5)dq.pop_front():删除队头,不返回值。
(6)dq.push_back(e):在队尾添加一个元素e。
(7)dq.push_front(e):在队头添加一个元素e。

双端队列的经典应用是单调队列。
单调队列中的元素是单单调有序的,且元素在队列中的顺序和原来在序列中的顺序一致;单调队列的队头和队尾都能入队和出队。

提示:
灵活运用单调队列能够使很多问题的求解获得优化。

其原理可以简单地概括为:
序列中的几个元素,用单调队列处理时,每个元素只需要进出队列一次,复杂度为O(n)。

单调队列与滑动窗口

下面介绍单调队列的基本应用,了解如何通过单调队列获得优化。
注意队列中"删头、去尾、窗口"的操作。

例1.3 滑动窗口

在这里插入图片描述
本题用暴力法很容易编程:从头到尾扫描,每次检查处个数,一共检查O(nk)次。
暴力法显然会超时。

下面用单调队列求解,它的复杂度为O(n)。

在本题中,单调队列有以下特征:
(1)队头的元素始终是队列中最小的。根据需要输出队头,但是不一定弹出。
(2)元素只能从队尾进入队列,从队头、队尾都可以弹出。
(3)序列中的每个元素都必须进入队列。
例如,x进入队尾时,和原队尾y比较,如果x<=y,就从队尾弹出y;一直到弹出队尾所有比x大的元素,最后x进入队尾。这个入队操作保证了队头元素是队列中最小的。

单调队列的时间复杂度:每个元素最多入队一次、出队一次,且出入队时间都为O(1),
因此总时间为O(n)。
因为题中需要逐一处理所有几个数,所以O(n)已经是能达到的最优复杂度。

从以上过程可以看出,单调队列有两个重要操作:删头、去尾。
(1)删头:如果队头的元素脱离了窗口,这个元素就没用了,弹出它。
(2)去尾:如果新元素进队尾时,原队尾的存在破坏了队列的单调性,就弹出它。
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;int main() {int n, m;scanf("%d%d", &n, &m);int a[N];deque<int> q;// 读取序列元素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");// 清空队列,准备用于求最大值while (!q.empty()) q.pop_front();// 输出每个窗口中的最大值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");return 0;
}

单调队列与最大子序和问题

首先说明什么是子序和:
给定长度为n的整数序列A,它的"子序列"定义为A中非空的一段连续的元素。
例如:
序列(6,-1,5,4,一7),前4个元素的子序和为6+(-1)+5+4=14。

最大子序和问题按子序列有无长度限制分为两种:

问题(1):不限制子序列的长度。
在所有可能的子序列中找到一个子序列,该子序列和最大。

问题(2):限制子序列的长度。
给定一个限制长度m,找出一段长度不超过m的连续子序列,使它的子序和最大。

问题(1)比较简单,用贪心法或动态规划(Dynamie Programming,DP)算法,复杂度都为O(n)。
问题(2)用单调队列,复杂度也为O(n)。通过这个例子,读者可以理解为什么单调队列能用于DP优化。

问题(1)不是本节的内容,不过为了参照,下面也给出是题解:

问题(1)的求解

用贪心法或DP,在O(n)时间内求解。下面是例题。
在这里插入图片描述
题解1: 贪心法
逐个扫描序列中的元素并累加。加一个正数时,子序和会增加;加一个负数时,子序和会减小。
如果当前得到的和变成了负数,这个负数在接下来的累加中会减少后面的求和,所以抛弃它,从下一位置开始重新求和。

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x7fffffff;int main() 
{int t;cin >> t;// 处理每个测试用例for (int i = 1; i <= t; i++) {int n;cin >> n;// 最大子序和,初始化为极小负数int maxsum = -INF;// 起点、终点、扫描位置int start = 1, end = 1, p = 1;int sum = 0;// 遍历输入的序列for (int j = 1; j <= n; j++) {int a;cin >> a;sum += a;// 如果当前子序和大于最大子序和,更新最大子序和及起点和终点位置if (sum > maxsum) {maxsum = sum;start = p;end = j;}// 如果当前子序和小于 0,从下一个位置重新开始求和if (sum < 0) {sum = 0;p = j + 1;}}// 输出当前测试用例的结果printf("Case %d:\n", i);printf("%d %d %d\n", maxsum, start, end);// 如果不是最后一个测试用例,输出一个空行if (i!= t) cout << endl;}return 0;
}

题解2:动态规划
定义状态dp[i],表示以a[i]为结尾的最大子序和.

dp[i]的计算有两种情况:
(1)dp[i]只包括一个元素,就是a[i];
(2)dp[i]包括多个元素,从前面某个a[v]开始,v<i,到a[i]结束,即dp[i-1]+a[i]。
取两者的最大值,得到状态转移方程dp[i]=max(dp[i-1]+a[i],a[i])。
在所有dp[i]中,最大值就是题目的解。

#include <bits/stdc++.h>
using namespace std;int main() 
{int t;cin >> t;for (int k = 1; k <= t; k++) {int n;cin >> n;int dp[100005];for (int i = 1; i <= n; i++) {cin >> dp[i];}//重点--------int start = 1, end = 1, p = 1;int maxsum = dp[1];for (int i = 2; i <= n; i++){// 状态转移方程:dp[i]=max(dp[i-1]+a[i], a[i])if (dp[i - 1] + dp[i] >= dp[i]) {dp[i] = dp[i - 1] + dp[i];} else {p = i;}if (dp[i] > maxsum) {maxsum = dp[i];start = p;end = i;}}//重点--------printf("Case %d:\n", k);printf("%d %d %d\n", maxsum, start, end);if (k!= t) cout << endl;}return 0;
}
问题(2)的求解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1538276.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Docker Registry API best practice 【Docker Registry API 最佳实践】

文章目录 1. 安装 docker2. 配置 docker4. 配置域名解析5. 部署 registry6. Registry API 管理7. 批量清理镜像8. 其他 &#x1f44b; 这篇文章内容&#xff1a;实现shell 脚本批量清理docker registry的镜像。 &#x1f514;&#xff1a;你可以在这里阅读&#xff1a;https:/…

Android 15 正式发布至 AOSP

Google官方宣布&#xff0c;将于近期发布了 Android 15&#xff0c;而在早些时候&#xff0c;Google已经将其源代码推送至 Android 开源项目 (AOSP)。未来几周内&#xff0c;Android 15 将在受支持的 Pixel 设备上正式推出&#xff0c;并将于今年晚些时候在三星、Honor、iQOO、…

终于有人把 Jmeter 工具的 CSV 参数化讲清楚啦!

在性能测试和接口测试中&#xff0c;参数化是让测试更贴近真实场景的关键步骤&#xff0c;尤其是使用JMeter进行测试时&#xff0c;CSV文件的参数化功能能够让我们模拟大量用户输入&#xff0c;但很多测试人员对其理解不够透彻。今天&#xff0c;我们终于来详细讲清楚如何通过J…

JVM(HotSpot):JVM简单介绍

文章目录 一、什么是JVM二、优点三、比较四、学习路线 一、什么是JVM 定义&#xff1a;java程序的运行环境 首先&#xff0c;我们要知道&#xff0c;JVM是一套规范&#xff0c;运行java程序的一套规范。 那么&#xff0c;我们学习过java的人都知道&#xff0c;接口规范的实现类…

突破空间限制:4个远程控制电脑的办法

如何远程操作另一台电脑&#xff0c;高效完成工作任务&#xff1f; 今天这篇文章&#xff0c;就来分享4种高效且实用的远程控制电脑方法&#xff0c;这些方法不仅能够帮助我们跨越地域的空间界限&#xff0c;还能极大地提升我们的工作效率和灵活性。无论是远程汇报还是数据共享…

【多模态大模型】社招秋招实习 -- 快手招聘!

快手垂搜(多模态搜索&AI)团队 多模态 & 大模型 & Agent 正式员工(*3) & 实习生招聘 (*5&#xff0c;实习时间>3个月) 1、团队介绍 我们是快手垂搜大模型团队&#xff0c;致力于构建视觉大模型、多模态搜索、User Agent新系统&#xff0c;应用于多种电商场…

前后端分离Vue美容店会员信息管理系统o7grs

目录 技术栈介绍具体实现截图系统设计研究方法&#xff1a;设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取 技术栈介绍 本课题的研究方法和研究步骤基本合理&#xff0c;难度适中&#xff0c;本选题是学生所学专业知识的延续&#xff0c;符合…

uniapp富文本editor输入二次扩展兼容微信小程序

在uni-app中开发富文本输入功能&#xff0c;并使其兼容微信小程序&#xff0c;需要注意一些特定的限制和解决方案。由于微信小程序本身对HTML的支持有限&#xff0c;直接在小程序中实现像Web那样完整的富文本编辑功能&#xff08;如使用CKEditor、Quill等&#xff09;是不可能的…

2024年【电气试验】试题及解析及电气试验模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【电气试验】试题及解析及电气试验模拟考试题&#xff0c;包含电气试验试题及解析答案和解析及电气试验模拟考试题练习。安全生产模拟考试一点通结合国家电气试验考试最新大纲及电气试验考试真题汇总&#xff0…

【强化学习系列】Gym库使用——创建自己的强化学习环境2:拆解官方标准模型源码/规范自定义类+打包自定义环境

目录 一、 官方标准环境的获取与理解 二、根据官方环境源码修改自定义 1.初始化__init__() 2.重置环境 reset() 三、打包环境 1.注册与创建自定义环境 2.环境规范化 在本文的早些时候&#xff0c;曾尝试按照自己的想法搭建自定义的基于gym强化学习环境。 【强化学习系列】Gy…

IEEE-754 32位十六进制数 转换为十进制浮点数

要将 IEEE-754 32位十六进制数 转换为 十进制浮点数&#xff0c;可以使用LabVIEW中的 Type Cast 函数。以下是一些具体步骤&#xff0c;以及相关实例的整理&#xff1a; 实现步骤&#xff1a; 输入十六进制数&#xff1a;在LabVIEW中&#xff0c;首先需要创建一个输入控制器&am…

剃(磨)前插齿刀设计计算开发第二步:

从刀具上的各段齿形计算加工出的齿轮端面齿形&#xff0c;下一步进行细化处理[开心]&#xff0c;去掉一些线头&#xff0c;增加一些关键参数的计算及标准&#xff0c;例如&#xff1a;SAP、UnderCut、EAP、Chamfer等等&#xff0c;祝我好运吧&#xff0c;谢谢&#xff01;

MySQL系列—11.Redo log

1.简介 概念 redo log用于记录事务操作变化&#xff0c;记录的是数据被修改之后的值&#xff0c;&#xff08;tbs space id page no action&#xff09;。 作用 尚未完成的DML,数据库崩溃则用log恢复。保证事务持久性。 ( 1 ) 在页面修改完成之后&#xff0c;脏页刷入磁盘之…

ZYNQ FPGA自学笔记~点亮LED

一 ZYNQ FPGA简介 ZYNQ FPGA主要特点是包含了完整的ARM处理系统&#xff0c;内部包含了内存控制器和大量的外设&#xff0c;且可独立于可编程逻辑单元&#xff0c;下图中的ARM内核为 ARM Cortex™-A9&#xff0c;ZYNQ FPGA包含两大功能块&#xff0c;处理系统Processing System…

路由原理介绍

定义与过程 定义&#xff1a;是指导IP报文发送的路径信息 过程&#xff1a; 检查数据包的目的地确定信息源发现可能的路径选择最佳路径验证和维护路由信息 路由来源 直连路由&#xff1a;不需配置&#xff0c;路由器配置IP后自动生效 静态路由&#xff1a;手动配置 ip r…

Ubantu LLaMA-Factory实战

一、Ubantu LLaMA-Factory实战安装&#xff1a; CUDA 安装 CUDA 是由 NVIDIA 创建的一个并行计算平台和编程模型&#xff0c;它让开发者可以使用 NVIDIA 的 GPU 进行高性能的并行计算。 首先&#xff0c;在 https://developer.nvidia.com/cuda-gpus 查看您的 GPU 是否支持CU…

壹嘉情,中国与世界经济文化交流的新桥梁

壹嘉情正在全球华商领域迅速崛起。作为意大利华商总会的中国分部&#xff0c;壹嘉情承载着推动两岸及全球华商深度合作、实现资源共享和互利共赢的使命。它的成立标志着意大利华商总会在全球战略布局上的重要一步&#xff0c;同时也昭示了全球化浪潮中&#xff0c;华人企业正加…

苹果电脑也可以清除垃圾吗?苹果电脑清理垃圾用什么软件哪个好?

相对于Windows电脑&#xff0c;目前专注于苹果电脑清理的软件不算多&#xff0c;那么&#xff0c;苹果电脑垃圾清理软件哪个好&#xff1f;本文经过对比给大家推荐几款好用的软件。另外&#xff0c;我们还会进行苹果电脑垃圾清理方法盘点&#xff0c;让大家更了解电脑的清理方法…

从零开始讲DDR(0)——DDR的前世今生

一、计算机组成 计算机组成结构&#xff08;Computer Architecture&#xff09;是计算机系统的核心&#xff0c;它定义了计算机的基本工作原理和设计模式。计算机的组成可以分成以下3大类&#xff1a;中央处理器&#xff08;CPU&#xff09;、存储器和输入/输出子系统。 1.1 中…

达梦数据库DM8使用介绍

达梦数据库DM8使用介绍 达梦数据库DM8使用介绍一、安装达梦数据库二、初始化数据库实例三、SQL 分类DML(Data Mannipulation Language)数据操纵语言&#xff1a;DDL(Data Definition Language)数据定义语言&#xff1a;DCL(Data Control Language)数据控制语言&#xff1a;TCL(…