队列
队列是一种先进先出的线性数据结构
一般来说,元素从右端(队尾)入队,从左端(队头)出队
可以将队头队尾相连,优化空间利用,这就是“循环队列”
一般直接用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;
}