基础数据结构3.2——队列
题目上添加了超链接,大家点一下题目就会自动跳转到Poj原题界面~~ 冲鸭冲鸭ヾ(◍°∇°◍)ノ゙。
前言:
队列相较于其它数据结构的特点是先进先出,通常也是通过数组、链表实现。
通过数组实现时,为解决伪溢出问题,常需要采用循环队列。做题时用数组实现就足以应对绝大部分题目。
常见题型有模拟、单调队列(挺难的,也是很多进阶优化算法的基础)、以及后续的搜索题....很重要,需要打好基础。
最后一道题2823尤为经典重要。相关课程我看的少,有感觉讲的特别好、特别浅显深刻的,欢迎大家留言分享(好想自己录一个)
3.2.1 Card Trick (3032)
题意:一堆牌有n张,从牌堆顶部开始,第i次将i张牌放到牌底,然后将新的牌顶的牌从牌堆中取走。求取走的n张牌在原牌堆中的位置。
小笔记:简单模拟
#include <cstdio> #include <queue> using namespace std; int main() { int t; scanf("%d", &t); while (t--) { queue<int> Q; int a[15]; //记录每张牌在原队列中的位置 int k = 1; //记录取牌顺序 int n; scanf("%d", &n); for (int i = 1; i <= n; i++) Q.push(i); //将所有牌入队列 for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { Q.push(Q.front()); Q.pop(); } a[Q.front()] = k++; Q.pop(); } for (int i = 1; i <= n; i++) printf("%d ", a[i]); printf("\n"); } return 0; }
3.2.2 Card Stacking (3629)
题意:N个玩家围成一圈打牌,牌堆共有K张牌(K为N的倍数),其中包含M=K/N张好牌和K-M张坏牌。然后和上一题差不多。
小笔记:模拟,发到手里的牌直接出列,其他牌再次入列,发到发牌人手中的牌记录这种牌的最初编号。
#include <cstdio> #include <queue> #include <algorithm> using namespace std; int main() { queue<int> Q; int a[50005]; //记录好牌的编号 int n, k, p; scanf("%d%d%d", &n, &k, &p); for (int i = 1; i <= k; i++) Q.push(i); int m; for (m = 0; m < k / n; m++) for (int i = 1; i <= n; i++) { if (i == n) a[m] = Q.front(); Q.pop(); for (int j = 0; j < p; j++) { Q.push(Q.front()); Q.pop(); } } sort(a, a + m); for (int i = 0; i < m; i++) printf("%d\n", a[i]); return 0; }
3.2.3 Printer Queue (3125)
题意:一个队列包含n个打印作业,每个作业有一个优先级(1~9),从队列头开始,如果该作业的优先级比队列中其他所有作业的优先级高,则打印该作业并出列,否则将该作业放入队列尾;打印作业花费时间为1,其他操作不花费时间。求一个队列中给定作业m,等待到打印完该作业共需要多长时间。
小笔记:模拟,每次取队列头,检查队列中是否有比它优先级高的作业,有的话将该作业放入队列尾,否则时间加1后出队列。处理到指定作业m的时候,如果队列中没有比它优先级高的作业,则时间加1,打印该作业;否则将它移到队尾,继续判断下一个作业。
#include <cstdio> #include <vector> using namespace std; struct job { int v; //作业的序号 int p; //作业的优先级 job(int a, int b) : v(a), p(b) {} }; //检查队列Q中是否有优先级比x高的项,如果有,输出真,否则输出假 bool check(vector<job> Q, int x, unsigned int i) { while (i < Q.size()) if (x < Q[i++].p) return true; return false; } int main() { int t; scanf("%d", &t); while (t--) { //使用STL中的向量vector 实现队列,利用其push_back操作向队列尾部加入数据 vector<job> Q; int n, m; scanf("%d%d", &n, &m); for (int i = 0, p; i < n; i++) { scanf("%d", &p); Q.push_back(job(i, p)); } int ans = 1; for (int k = 0; check(Q, Q[k].p, k) || Q[k].v != m; k++) { if (check(Q, Q[k].p, k)) Q.push_back(Q[k]); else ans++; } printf("%d\n", ans); } return 0; }
3.2.4 Team Queue (2259)
题意:共有t组元素进行队列操作。
入列操作:元素进入队列之前先从头检查队列中是否有同组的元素,如果有,直接排到同组元素后面,否则排到队列尾。
出列操作:直接取出队列头的元素。
小笔记:按照题意进行队列模拟,一共有 t 组队列Q[1…t], x 所在的组为T[x],用一个队列TQ记录当前元素所在组的信息。
#include <cstdio> #include <queue> using namespace std; const int N = 1001; int T[1000000]; //元素与队列号的映射 int main() { int t, k = 0; while (scanf("%d", &t) && t) { printf("Scenario #%d\n", ++k); bool v[N]; //标识是否有同组元素 for (int i = 0; i < t; i++) { int n; scanf("%d", &n); v[i] = false; while (n--) { int x; scanf("%d", &x); T[x] = i; } } queue<int> Q[N]; //存储每个组 queue<int> TQ; //存储组号 char cmd[10]; while (scanf("%s", cmd) && cmd[0] != 'S') { if (cmd[0] == 'E') { int x; scanf("%d", &x); int i = T[x]; if (!v[i]) { v[i] = true; TQ.push(i); //将元素x所在组号T[x]放入队列TQ } Q[i].push(x); // 将元素x放入队列Q[T[x]] } else if (cmd[0] == 'D') { int i = TQ.front(); //从TQ队列头找到组号i printf("%d\n", Q[i].front()); //输出Q[i]队列头元素 Q[i].pop(); if (Q[i].empty()) { TQ.pop(); v[i] = false; } } } printf("\n"); } return 0; }
3.2.5 Sliding Window (2823)
题意:数组a[1…n]有n个数字,依次计算a[1…k]到A[n-k+1…n]区间的最大值和最小值。
小笔记:这道题特别重要,十分精华。一种方法是滑动窗口、单调队列的思想应用,一定要掌握。到后面我们还会学习线段树,也是对这种反复问询区间问题的杀招。
#include <cstdio> #include <queue> using namespace std; int a[1000005]; int main() { int n, k; scanf("%d%d", &n, &k); //求区间最小值 deque<int> minQ; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); while (!minQ.empty() && a[i] < a[minQ.back()]) minQ.pop_back(); minQ.push_back(i); if (minQ.front() < i - k + 1) minQ.pop_front(); if (i >= k) printf("%d ", a[minQ.front()]); } printf("\n"); //求区间最大值 deque<int> maxQ; for (int i = 1; i <= n; i++) { while (!maxQ.empty() && a[i] > a[maxQ.back()]) maxQ.pop_back(); maxQ.push_back(i); if (maxQ.front() < i - k + 1) maxQ.pop_front(); if (i >= k) printf("%d ", a[maxQ.front()]); } printf("\n"); return 0; }