队列
队列是一种先进先出的线性数据结构
一般来说,元素从右端(队尾)入队,从左端(队头)出队
可以将队头队尾相连,优化空间利用,这就是“循环队列”
一般直接用STL就好啦
模板
// 普通队列// hh 表示队头,tt表示队尾int q[N], hh = 0, tt = -1;// 向队尾插入一个数q[ ++ tt] = x;// 从队头弹出一个数hh ++ ;// 队头的值q[hh];// 判断队列是否为空if (hh <= tt){}// 循环队列// hh 表示队头,tt表示队尾的后一个位置int q[N], hh = 0, tt = 0;// 向队尾插入一个数q[tt ++ ] = x;if (tt == N) tt = 0;// 从队头弹出一个数hh ++ ;if (hh == N) hh = 0;// 队头的值q[hh];// 判断队列是否为空if (hh != tt){}
题目
小组队列
这题需要两组队列,一组队列用于维护各个小组,另外一组用于维护小组间的顺序
用哈希表存储每个人对应的小组序号
剩下的按照题意模拟即可
#include <iostream>#include <queue>#include <unordered_map>#include <string>using namespace std;const int N = 1010;unordered_map<int, int> rec;int main() {int n, no = 1;while (cin >> n, n) {printf("Scenario #%d\n", no++);for (int i = 0; i < n; i++) {int cnt;scanf("%d", &cnt);for (int j = 0; j < cnt; j++) {int x;scanf("%d", &x);rec[x] = i; // 哈希:人对应的小组编号}}queue<int> team[N];queue<int> all;string op;while (cin >> op, op != "STOP") {if (op == "ENQUEUE") {int x;scanf("%d", &x);int tid = rec[x];if (team[tid].empty()) all.push(tid);team[tid].push(x);}else {int tid = all.front();int x = team[tid].front();team[tid].pop();printf("%d\n", x);if (team[tid].empty()) all.pop();}}putchar('\n');}return 0;}
蚯蚓
这题要求维护一个集合,支持查询最大值、删除最大值、插入新的值,可以考虑使用二叉堆
对于其余蚯蚓每个长度增加q,可以转化成新增蚯蚓的长度减去q,再用一个变量维护整个过程中长度增加总和
上面是常规做法,但是这题数据量大,要求每次操作是O(1)的,这就需要挖掘题目中的规律
简单说就是,如果我们按照长度从大到小取出数,新产生的数也会按照时间递减
这样一来只要用三个队列,每个从三个队头挑出最大的那个就行
#include <iostream>#include <algorithm>#include <limits.h>using namespace std;typedef long long LL;const int N = 7e6 + 10;int a[N], l[N], r[N];int hha = 0, hhl = 0, hhr = 0, tta = -1, ttl = -1, ttr = -1;int n, m, q, u, v, t;int get_max() {int res = INT_MIN;if (tta >= hha) res = max(res, a[hha]);if (ttl >= hhl) res = max(res, l[hhl]);if (ttr >= hhr) res = max(res, r[hhr]);if (tta >= hha && res == a[hha]) hha++;else if (ttl >= hhl && res == l[hhl]) hhl++;else hhr++;return res;}int main() {// cin >> n >> m >> q >> u >> v >> t;scanf("%d%d%d%d%d%d", &n, &m, &q, &u, &v, &t);for (int i = 0; i < n; i++) {int x;scanf("%d", &x);a[++tta] = x;}sort(a, a + n);reverse(a, a + n);int delta = 0;for (int i = 1; i <= m; i++) {int x = get_max();x += delta;if (i % t == 0) printf("%d ", x);delta += q;int left = 1ll * x * u / v, right = x - left;l[++ttl] = left - delta, r[++ttr] = right - delta;}putchar('\n');for (int i = 1; i <= n + m; i++) {int x = get_max();if (i % t == 0) printf("%d ", x + delta);}return 0;}
双端队列
名叫双端队列但是关系不大的一道题
考虑这样一种情况,不幸将PQ放入同一个队列,后面出现介于PQ之间的数,直接完蛋
这启发我们从数值大小而非输入顺序去考虑
可以先把数组从小到大排序,然后根据原始下标分成尽量少的几段,每一段对应一个双端队列
双端队列对于数据的要求是满足单谷性质(先单减后单增),这样一来队列可以从中间向两边扩展(建议画图)
我们要做的就是根据原始下标大小找出最少的单谷数目
操作空间主要在于连续几个相等的数可以重新排序,这里采用贪心策略,尽量保持上一段的单调性
#include <iostream>#include <algorithm>#include <limits.h>using namespace std;typedef pair<int, int> PII;const int N = 2e5 + 10;PII a[N];int main() {int n;cin >> n;for (int i = 1; i <= n; i++) {scanf("%d", &a[i].first);a[i].second = i;}sort(a + 1, a + n + 1);int last = INT_MAX, ans = 1, dir = -1;int i = 1;while (i <= n) {int j = i;while (a[++j].first == a[i].first);int minp = a[i].second, maxp = a[j - 1].second;if (dir == -1) {if (maxp < last) {last = minp;}else {dir = 1;last = maxp;}}else {if (minp > last) {last = maxp;}else {last = minp;dir = -1;ans++;}}i = j;}cout << ans;return 0;}
