基础数据结构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;
}

  

 
posted @ 2021-06-03 15:25  anyiya  阅读(47)  评论(0编辑  收藏  举报