DS5 队列及应用【DS 队列基础习题 C++】
文章目录
前言
这周做了oj上的基础习题,emmm拖更了,有点忙,题目还算简单,当时一边写一边看wbg打edg,所以写得比较慢,这次主要是通过4道oj习题来学习队列的一些性质。
A. DS队列之银行排队
题目描述
行营业大厅共服务3种客户,类型为A\B\C,大厅分别设置了3个窗口分别服务三种客户,即每个窗口只服务一种客户。现有一批客户来银行办理业务,每个客户都有类型和办理业务时间。每个窗口按照客户到来的顺序进行服务。
编程实现它们的办理流程,请使用C++自带的queue必须使用队列实现,其他方法0分!
队列queue的用法如下:
1.包含头文件:#include
2.定义一个整数队列对象:queue myQe;
3.定义一个整数队列对象数组:queue myQA[10];
4.入队操作:myQe.push(itemp); //把整数itemp进入队列
5.出队操作:myQe.pop(); //把队头元素弹出队列,注意本操作不获取队头元素
6.获取队头元素: itemp = myQe.front(); // 把队头元素放入itemp中,注意本操作不弹出元素
7.判断队列是否为空:myQe.empty();//队列空则返回true,不空则返回false
输入
第一行输入先输入n表示客户数量
第二行输入每个客户的类型,数据之间用用空格隔开
第三行输入每个客户的办理时间,数据之间用用空格隔开
输出
第一行输出A类客户的平均办理时间
第二行输出B类客户的平均办理时间
第三行输出C类客户的平均办理时间
输入样例
8
A B C B C A A A
10 20 30 40 50 60 70 80
输出样例
55
30
40
提示
平均办理时间也应定义为整数类型。
思路
简单模拟即可,使用对列存储客户的类型,然后在读取时间的时候,弹出客户类型,对应计算类型的时间加和即可。
code(C++)
#include<iostream>
#include<queue>
using namespace std;
int main(int argc, char** argv)
{
int n;
cin >> n;
// 客户队列
queue<char> customers;
// 存入对列
for (int i = 0; i < n; ++i)
{
char ch;
cin >> ch;
customers.push(ch);
}
// 三种类型的总时间
int num, totalA = 0, totalB = 0, totalC = 0;
// 三种类型用户的数量
int numA = 0, numB = 0, numC = 0;
for (int i = 0; i < n; ++i)
{
cin >> num;
// 获取队首的用户类型
char ch = customers.front();
// 对应加和即可
if (ch == 'A')
{
totalA += num;
numA++;
}
else if (ch == 'B')
{
totalB += num;
numB++;
}
else
{
totalC += num;
numC++;
}
customers.pop();
}
// 输出答案
cout << totalA / numA << endl
<< totalB / numB << endl
<< totalC / numC << endl;
return 0;
}
B. DS队列+堆栈–数制转换
题目描述
对于任意十进制数转换为k进制,包括整数部分和小数部分转换。整数部分采用除k求余法,小数部分采用乘k取整法例如x=19.125,求2进制转换
整数部分19, 小数部分0.125 19 / 2 = 9 … 1 0.125 * 2 = 0.25 … 0 9 / 2 = 4 … 1 0.25 * 2 = 0.5 … 0 4 / 2 = 2 … 0 0.5 * 2 = 1 … 1 2 / 2 = 1 … 0 1 / 2 = 0 … 1
所以整数部分转为 10011,小数部分转为0.001,合起来为10011.001
提示整数部分可用堆栈,小数部分可用队列实现
注意:必须按照上述方法来实现数制转换,其他方法0分
输入
第一行输入一个t,表示下面将有t组测试数据。
接下来每行包含两个参数n和k,n表示要转换的数值,可能是非整数;k表示要转换的数制,1<k<=16
输出
对于每一组测试数据,每行输出转换后的结果,结果精度到小数点后3位
输出小数点后几位的代码如下:
#include
#include
using namespace std;int main()
{
double r = 123.56789;
cout<<fixed<<setprecision(4)<<r<<endl; //输出小数点后4return 0;
}
输入样例
2
19.125 2
15.125 16
输出样例
10011.001
F.200
提示
例如:十进制数254.3879转换为6进制数。
整数部分254,
254 / 6 = 42 … 2
42 / 6 = 7 … 0
7 / 6 = 1 … 1
1 / 6 = 0 … 1
小数部分0.3879,
0.3879 * 6 = 2.3274 … 2
0.3274 * 6 = 1.9644 … 1
0.9644 * 6 = 5.7864 … 5
所以整数部分转为 1102,小数部分转为0.215,转换后的6进制数合起来为1102.215
思路
按照题目来就行了,对于小数由于最终只需要精确到小数点3位,所以对于小数,我们只需存储3个数字即可。
同时,由于存在进制转换到10以上的进制,所以我们需要使用字符或者字符串来表示,而也是因为这个原因,我们其实可以不用考虑四舍五入的情况。
code(C++)
#include <iostream>
#include <iomanip>
#include<stack>
#include<queue>
using namespace std;
/// <summary>
/// 进制转换——对列+栈
/// </summary>
/// <param name="num"></param>
/// <param name="bs"></param>
void tranBS(double num, int bs)
{
// 获取整数位部分
int intNum = int(num);
// 计算小数部分
num -= intNum;
// 栈存储整数
stack<char> numStack;
// 计算进制转换后的部分
while (intNum)
{
int re = intNum % bs;
// 判断是否需要使用字母,下同
if (re >= 10)
{
numStack.push((char)(re + 'A' - 10));
}
else
{
numStack.push((char)(re + '0'));
}
intNum /= bs;
}
// 小数对列
queue<char> numQue;
// 计算小数部分
for (int i = 0; i < 3; ++i)
{
num *= bs;
int re = num;
if (re >= 10)
{
numQue.push((char)(re - 10 + 'A'));
}
else
{
numQue.push((char)(re + '0'));
}
num -= re;
}
// 输出结果
while (!numStack.empty())
{
cout << numStack.top();
numStack.pop();
}
cout << ".";
while (!numQue.empty())
{
cout << numQue.front();
numQue.pop();
}
cout << endl;
}
int main()
{
int t;
cin >> t;
while (t--)
{
double num;
int bs;
cin >> num >> bs;
tranBS(num, bs);
}
return 0;
}
C. DS队列–组队列
题目描述
组队列是队列结构中一种常见的队列结构,在很多地方有着广泛应用。组队列是是指队列内的元素分组聚集在一起。组队列包含两种命令:
1、 ENQUEUE,表示当有新的元素进入队列,首先会检索是否有同一组的元素已经存在,如果有,则新元素排在同组的最后,如果没有则插入队列末尾。
2、 DEQUEUE,表示队列头元素出队
3、 STOP,停止操作
建议使用C++自带的队列对象queue,编程更方便
输入
第1行输入一个t(t<=10),表示1个队列中有多少个组
第2行输入一个第1组的元素个数和数值
第3行输入一个第2组的元素个数和数值
以此类推输入完t组以定义同组元素之后,开始输入多个操作命令(<200),对空的组队列进行操作,例如输入ENQUEUE 100,表示把元素100插入队列
输出
DEQUEUE出队的元素
输入样例
2
3 101 102 103
3 201 202 203
ENQUEUE 101
ENQUEUE 201
ENQUEUE 102
ENQUEUE 202
ENQUEUE 103
ENQUEUE 203
DEQUEUE
DEQUEUE
DEQUEUE
STOP
输出样例
101 102 103
提示
3 101 102 103
3 201 202 203
3 301 302 303
ENQUEUE 201 ==>>> | 201 |
ENQUEUE 301 ==>>> | 201 | 301 |
ENQUEUE 102 ==>>> | 201 | 301 | 102 |
ENQUEUE 101 ==>>> | 201 | 301 | 102 101 |
ENQUEUE 203 ==>>> | 201 203 | 301 | 102 101 |
ENQUEUE 302 ==>>> | 201 203 | 301 302 | 102 101 |
可以看到队列分组,先入队的组在队列中靠前,出队也靠前:
DEQUEUE
输出 201,队列变为| 203 | 301 302 | 102 101 |
…
思路
我们在插入数字的时候,需要判断是否和队列中的数字是否一组,我们可以使用哈希表<数字,组号>来存储同一组的数字。
而由于输出的原因,我们不能立刻将数字输出,而是应该存入数组之类的容器。
code(C++)
#include<iostream>
#include<queue>
#include<vector>
#include<unordered_map>
using namespace std;
int main(int argc, char** argv)
{
int n;
cin >> n;
// 哈希表记录组号
unordered_map<int, int> hash;
for (int i = 0; i < n; ++i)
{
int t1;
cin >> t1;
for (int j = 0; j < t1; ++j)
{
int num;
cin >> num;
hash[num] = i + 1;
}
}
// 组队列
vector<queue<int>> VQ;
// 操作命令
string op;
cin >> op;
// 输出数字的数组
vector<int> out;
// 循环获取命令
while (op != "STOP")
{
// 入队
if (op == "ENQUEUE")
{
int num;
cin >> num;
// 判断是否在原有的队列中, true----不在
bool flag = true;
for (int i = 0; i < VQ.size(); ++i)
{
// 如果队列非空且当前队列是数字的同一组
if ((!VQ[i].empty()) && hash[VQ[i].front()] == hash[num])
{
VQ[i].push(num);
flag = false;
}
}
if (flag)
{
queue<int> temp;
temp.push(num);
VQ.push_back(temp);
}
}
// 需要出队
else
{
for (int j = 0; j < VQ.size(); ++j)
{
if (VQ[j].empty())
{
continue;
}
// 记录出队的数字
out.push_back(VQ[j].front());
VQ[j].pop();
break;
}
}
cin >> op;
}
// 输出结果
for (int i = 0; i < out.size() - 1; ++i)
{
cout << out[i] << " ";
}
cout << out[out.size() - 1];
cout << endl;
return 0;
}
D. DS队列----银行单队列多窗口模拟
题目描述
假设银行有K个窗口提供服务,窗口前设一条黄线,所有顾客按到达时间在黄线后排成一条长龙。当有窗口空闲时,下一位顾客即去该窗口处理事务。当有多个窗口可选择时,假设顾客总是选择编号最小的窗口。
本题要求输出前来等待服务的N位顾客的平均等待时间、最长等待时间、最后完成时间。
输入
输入第1行给出正整数N(≤1000),为顾客总人数;随后N行,每行给出一位顾客的到达时间
T
和事务处理时间P
,并且假设输入数据已经按到达时间先后排好了顺序;最后一行给出正整数K(≤10),为开设的营业窗口数。
输出
在一行中输出平均等待时间(输出到小数点后1位)、最长等待时间、最后完成时间,之间用1个空格分隔,行末不能有多余空格。
输入样例
9
0 20
1 15
1 61
2 10
10 5
10 3
30 18
31 25
31 2
3
输出样例
6.2 17 62
思路
我们可以使用一个一维数组来存储窗口的空闲时间,初始都为0。使用一个pair队列存储用户的起始和办理时间,然后在出队时遍历数组找出空闲时间最早的窗口,对应计算相关值,即可。
code(C++)
#include<iostream>
#include<queue>
#include<utility>
#include<vector>
#include<iomanip>
#include<algorithm>
using namespace std;
int main(int argc, char** argv)
{
int n;
cin >> n;
// 用户队列
queue<pair<int, int>> customer;
for (int i = 0; i < n; ++i)
{
int time1, time2;
cin >> time1 >> time2;
customer.push({ time1, time2 });
}
int k;
cin >> k;
// 初始化窗口
vector<int> windows(k, 0);
// 总时间,完成时间,和最大等待时间
int totalWaitTime = 0, finishTime = 0, maxWaitTime = 0;
while (!customer.empty())
{
int arriveTime = customer.front().first;
int takenTime = customer.front().second;
// 默认最早空闲的窗口的最小下标
int minIndex = 0;
for (int i = 0; i < k; ++i)
{
// 窗口在到达时间之前空闲
if (windows[i] <= arriveTime)
{
minIndex = i;
break;
}
// 动态更新下标
if (windows[i] < windows[minIndex])
{
minIndex = i;
}
}
// 最早空闲的窗口先于到达时间
if (windows[minIndex] <= arriveTime)
{
windows[minIndex] = arriveTime + takenTime;
}
// 否则则需要等待
else
{
int wait = windows[minIndex] - arriveTime;
windows[minIndex] += takenTime;
totalWaitTime += wait;
maxWaitTime = max(maxWaitTime, wait);
}
// 完成时间取最大值
finishTime = max(finishTime, windows[minIndex]);
customer.pop();
}
cout << fixed << setprecision(1) << (double)(totalWaitTime * 1.0 / n)
<< " " << maxWaitTime << " " << finishTime << endl;
return 0;
}
后话
其实D使用优先队列会好一些,降低时间复杂度。