队列专题精讲附例题

队列的定义
队列就是允许在一端进行插入,在另一端进行删 除的线性表允许插入的一端称为队尾,通常用一个 队尾指针r指向队尾元素,即r总是指向最后被插入的 元素;允许删除的一端称为队首,通常也用一个队首 指针f指向排头元素的前面初始时f=r=0
在这里插入图片描述后进先出
队列的基本操作:

(1)初始化队列 queuevis,定义一个队列
(2)入队 vis.push(x)
(3)出队 vis.pop()
(4)判断队列是否为空 vis.empty()
(5)判断队列中元素的数量vis.size()
(6)得到队列的队首元素 vis.front()
综上: #include
用<bits/stdc++.h>则无需考虑头文件。
例题·
例1
假设在周末舞会上,男士们和女士们进入舞厅时,各自排 成一队。跳舞开始时,依次从男队和女队的队头上各出一人 配成舞伴。规定每个舞曲能有一对跳舞者。若两队初始人数 不相同,则较长的那一队中未配对者等待下一轮舞曲。现要 求写一个程序,模拟上述舞伴配对问题。
第 1 行两个正整数,表示男士人数 m 和女士人数 n,1≤m,n≤1000;
第 2 行一个正整数,表示舞曲的数目 k,k≤1000。
输出共 k 行,每行两个数,之间用一个空格隔开,表示配对舞伴的序号,男士在前,女士在后。
分析:设计两个队列分别存放男士和女士。每对跳舞 的人一旦跳完后就回到队尾等待下次被选。
Sample Input
2 4
6
Sample Output
1 1
2 2
1 3
2 4
1 1
2 2

#include <bits/stdc++.h>
using namespace std;
int m,n,k,tmp1,tmp2;
queue<int>q1,q2;//定义两个队列
int main()
{
    cin>>m>>n>>k;
    for(int i=1;i<=m;i++)
        q1.push(i);//入男队
    for(int i=1;i<=n;i++)
        q2.push(i);//入女队
    while(k--)//曲子数
    {
        tmp1=q1.front();//得到男队队首,要跳舞的人
        q1.pop();//出队
        q1.push(tmp1);//重新归队,因为是后进,所以男1被排到了队尾
        tmp2=q2.front();//得到女队队首
        q2.pop();
        q2.push(tmp2);//同理
        printf("%d %d\n",tmp1,tmp2);
    }
    return 0;
}

例2
报数-队列-约瑟夫环
Description
n个小朋友们坐成一个圆圈,编号分别为1,2,3…n;第1个小朋友从1开始报数,报到m的小朋友离开座位;然后下一个小朋友从1接着报数;直到剩下最后一个小朋友为止;
Input
输入2个数字n和m;(1<=n,m<=1000)
Output
输出最后一个小朋友的编号!
Sample Input
10 5
Sample Output
3

#include <bits/stdc++.h>
using namespace std;
queue<int>q;
int main()
{
    int n,m,tmp,cnt;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        q.push(i);
    cnt=0;
    while(q.size()>1)//测量长度
    {
        cnt++;
        tmp=q.front();//获取队首tmp
        q.pop();//连续出队
      if(cnt%m!=0)//除第m以外全部入队
           q.push(tmp);
    }
    printf("%d\n",q.front());
    return 0;
}

大部分用队列能做的用队列数组都可做

#include <stdio.h>
int head,tail,a[1000001];
int main()
{
    int n,m,tmp,cnt;
    scanf("%d%d",&n,&m);
    tail=0;
    for(int i=1;i<=n;i++)
        a[++tail]=i;
    cnt=0;head=1;
    while(head<tail)//队首到队尾
    {
        cnt++;
        tmp=a[head];
        head++;
        if(cnt%m!=0)
        a[++tail]=tmp;
    }
    printf("%d\n",a[head]);
    return 0;
}

例3
酒桌游戏-队列约瑟夫环
n个人围成一个圆桌,按照顺时针的顺序1,2,…n进行编号;某一个人开始报一个数字,然后顺时针的下一个人会报数+1;当某个人报的数字含有7或是7的倍数时,这个人退出游戏,其他人接着报数!直到剩下一个人为止!
Input
输入n,m,t;n代表人数,m代表开始报数的人的编号t表示开始报数的人报出的数字是t;
然后接下来有n行,是这n个人的名字!
Output
输出最后一个人的名字!
Sample Input
5 3 20
liming
wangze
gongxiangjun
wangming
chenzhen
Sample Output
chenzhen

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int num;
    string name;
}p[1001];//用结构体存储人名
queue<node>q;//注意如何定义
bool judge(int x)
{
    if(x%7==0)
    return 1;//7的倍数
    while(x)
    {
        if(x%10==7)
        return 1;
        x=x/10;//处理含有7
    }
    return 0;
}
int main()
{
    int n,m,t;
    string name;
    cin>>n>>m>>t;
    for(int i=1;i<=n;i++)
    {
        cin>>p[i].name;
        p[i].num=i;//人名配上顺序
    }
    for(int i=1;i<=n;i++)
        q.push(p[i]);//第一遍入队
    for(int i=1;i<=m-1;i++)//从m开始重新入队出队
   {
    q.push(p[i]);
    q.pop();
    }
    t--;
    while(q.size()>1)
    {
        t++;
        node tmp=q.front();
        q.pop();
        if(!judge(t))//判断数字,同例2
        q.push(tmp);
    }
    printf("%s\n",q.front().name.c_str());//输出
    return 0;
}

例4洛谷海港(有点难)此处借鉴大佬优秀代码
把每个乘客都分别以结构体的形式即进 入队列(是每一个乘客),结构体{时间,国籍},然后使用 桶排序的思想记录就可以了, 也可以用map来代替桶排序! 当一艘船进入海港时,先压入队列(每个人); 然后在从队 列头开始检查是否满足条件(出栈时计算总的国籍数是否变 化)
struct sa {
int t;//进港时间
int x;//国家
};
本题的难点就在于如何存储本题的数据,把每个人分别入队列就解决了船的问题,有点不好想。
难点:num[x]是桶排序的数组,ans是国家的数量。
1、根据num[x]==0 来判断是增加或减少一个国家。
2、每次新来的船,都先入队列,并统计国家数。
3、用刚新来船的时间和队列头部的船只时间进行比较
4、时间大于等于86400,就出队列,并计算国家数。
5、输出当前的国家数。

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int t,x;//t为时间,x为国籍
};
queue<node>q;
int t,k,n,x,ans,a[300010];
inline int read()//快读,洛谷运行时间从389ms提速到205ms
{
    register int x=0,f=1;//register int存到寄存器中可以加快变量的读写速度
    char c=getchar();
    while(c<'0'||c>'9')
    {
    if(c=='-')
    f=-1; 
    c=getchar();
    }
    while(c>='0'&&c<='9')
    {
    x=(x<<3)+(x<<1)+(c^48);
    c=getchar();
    }
    return x*f;
}
int main()
{
    n=read();
    while(n--)
    {
        t=read();k=read();//读入
        while(k--)
        {
            x=read();
            q.push({t,x});
            if(a[x]==0)
            ans++;
          a[x]++;
        }
        while(t-q.front().t>=86400&&!q.empty())
        {
            node tmp=q.front();q.pop();
            a[tmp.x]--;
            if(a[tmp.x]==0)ans--;
        }
        printf("%d\n",ans);
    }
    return 0;
}

下面来解释一下什么叫快读(注意:用快读只能读入整数
都知道scanf比cin快,cin加 ios::sync_with_stdio(false) 速度和scanf一样快。但是getchar比scanf快,所以,来个快读模板

inline int read(){
    int sgn = 1;
    int cnt = 0;   //sgn表示正负号 cnt表示读取的数字
    char ch = getchar();
    while (ch < '0' || ch > '9') {     
     //读取非数字的字符,保留负号,忽略其余无关符号
        if(ch == '-')
            sgn = -sgn;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') 
    {
        cnt = cnt*10 + (ch-'0');
        ch = getchar();
    }
    return  sgn*cnt;
}
 
/*调用:
int n;
n = read();
*/

void read(T& x) {
    int f = 1; x = 0;
    char ch = getchar();

    while (ch < '0' || ch > '9')   
    {
    if (ch == '-') f = -1; 
    ch = getchar();
    }
    while (ch >= '0' && ch <= '9') 
    {
    x = x * 10 + ch - '0';
     ch = getchar();
     }
    x *= f;
}

例5
大数学家高斯小时候偶然间发现一种有趣的自然数集合 Blash ,对应以 a 为基的集合 Ba 定义如下:
(1)a 是集合 Ba 的基,且 a 是 Ba 的第一个元素。
(2)如果 x 在集合 Ba 中,则 2x+1 和 3x+1 也都在集合 Ba 中。
(3)没有其他元素在集合 Ba 中了。
现在小高斯想知道如果将集合 Ba 中元素按照升序排列,第 n 个元素会是多少?
Input
一行输入包括两个数字,集合的基 a 以及所求元素序号 n 。(1<=a<=50,1<=n<=100000)
Output
输出第n个元素的值!
Sample Input
1 100
Sample Output
418
Hint
样例2:
输入: 28 5437
输出:900585

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;  
int n,p2,p3,a[N];
int main()
{
    ios::sync_with_stdio(false);
    cin>>a[1]>>n;
    p2=1,p3=1;
    for(int i=2;i<=n;i++)//直接用队列数组做
    {
        a[i]=min(2*a[p2]+1,3*a[p3]+1);//将一个表生成
        if(a[i]==2*a[p2]+1)
        p2++;
        if(a[i]==3*a[p3]+1)
        p3++;
    }
    printf("%d\n",a[n]);
    return 0;
}
  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值