D. DS队列----银行单队列多窗口模拟
题目
假设银行有K个窗口提供服务,窗口前设一条黄线,所有顾客按到达时间在黄线后排成一条长龙。当有窗口空闲时,下一位顾客即去该窗口处理事务。当有多个窗口可选择时,假设顾客总是选择编号最小的窗口。
本题要求输出前来等待服务的N位顾客的平均等待时间、最长等待时间、最后完成时间
输入
输入第1行给出正整数N(≤1000),为顾客总人数;随后N行,每行给出一位顾客的到达时间T和事务处理时间P,并且假设输入数据已经按到达时间先后排好了顺序;最后一行给出正整数K(≤10),为开设的营业窗口数。
输出
在一行中输出平均等待时间(输出到小数点后1位)、最长等待时间、最后完成时间,之间用1个空格分隔,行末不能有多余空格。
解析
面对这一道题,一开始我的思路是设置一个全局变量time,无限循环time++来模拟当前时间,以time来选择时间最少的窗口,但是这样会让资源占用过多,万一有一个到达时间很大的顾客,就会导致time一直++才能到这个顾客到达的时间。
后面舍弃了time++的方案,我们创建一个customer顾客类/结构体,有成员变量t(到达时间点)、q(处理业务时长)、end(处理结束后的时间点)、wait(等待时长),我们注意时间点和时长的区别。
struct customer{
int t = 0, p = 0, wait = 0, end = 0;
// t到达时间点 p处理所需时间 wait等待时间 end当前客户完成业务的’时间点‘
}
接收完输入的顾客信息(customer)我们保存在一个队列(queue)q中,并且创建k个处理窗口window,每一个窗口均为一个队列,并且初始化每一个窗口中有一个数值均为0的顾客。
queue<customer> windows[k]; // k个处理窗口
for(int i=0;i<k;i++){
customer c;
windows[i].push(c);
} // 每个窗口都为一个队列,初始化每个窗口有一位所有数值都为0的顾客
后我们开始循环取出队列第一个顾客,开始插入windows窗口中处理,我们创建min(最短时间)、time(当前队列的时间)、index(最短时间的队列下标)变量,若当前队列的最后一个顾客的end(处理完的时间点)最小,即将当前顾客推进push到当前队列,并计算当前顾客处理完的时间点end与等待时间wait为多少。
注意到:我们计算当前顾客end时,我们需要结合上一位顾客的end时间点判断,假如我们的到达时间点t若晚于>上一个顾客处理完的时间点end,那我们的end自然时t(到达时间点)+p(处理完所需时长);假如我们到达时间的时候上一个顾客还没处理完(到达时间点t若早于<上一个顾客处理完的时间点end),我们就需要等到他处理完才能开始处理我们的事务,所以当前end等于上一个顾客的end + 我们处理事务的时间 (即 end = 上一个end + p)。
while (!q.empty()){ // 开始循环顾客队列插入处理窗口
int min = windows[0].back().end, time = 0, index = 0;
// 判断哪一个窗口队列最后一个顾客的结束时间点end最小,就记录下当前窗口下标
for(int i=0;i<k;i++){
time = windows[i].back().end;
if(time < min){
min = time;
index = i;
}
}
q.front().end = q.front().t >= windows[index].back().end ? q.front().t+q.front().p : windows[index].back().end+q.front().p;
// 计算当前顾客的结束时间点,要是当前顾客的到达时间晚于上一个顾客的end,就end = 到达时间 + 处理时间
// 否则当前的end必须要从前一个顾客的end开始才可以处理,所以当前end = 上一个end + 处理时间
q.front().wait = q.front().end - q.front().p - q.front().t;
// 当前顾客的等待时间就是 当前结束时间点 减去 到达时间点 和 处理时间
windows[index].push(q.front());
q.pop();
}
代码整体
#include <iostream>
#include <queue>
#include <iomanip>
using namespace std;
struct customer{
int t = 0, p = 0, wait = 0, end = 0;
// t到达时间点 p处理所需时间 wait等待时间 end当前客户完成业务的’时间点‘
};
int main() {
int n, t, p, k; // n客户数量 t和p用来暂时储存每个顾客的 到达时间 和 处理所需时间 k为处理窗口数量
queue<customer> q; // 顾客队列
cin >> n;
for(int i=0;i<n;i++){
cin >> t >> p;
struct customer cus = {t, p};
q.push(cus);
}
cin >> k;
queue<customer> windows[k]; // k个处理窗口
for(int i=0;i<k;i++){
customer c;
windows[i].push(c);
} // 每个窗口都为一个队列,初始化每个窗口有一位所有数值都为0的顾客
while (!q.empty()){ // 开始循环顾客队列插入处理窗口
int min = windows[0].back().end, time = 0, index = 0;
// 判断哪一个窗口队列最后一个顾客的结束时间点end最小,就记录下当前窗口下标
for(int i=0;i<k;i++){
time = windows[i].back().end;
if(time < min){
min = time;
index = i;
}
}
q.front().end = q.front().t >= windows[index].back().end ? q.front().t+q.front().p : windows[index].back().end+q.front().p;
// 计算当前顾客的结束时间点,要是当前顾客的到达时间晚于上一个顾客的end,就end = 到达时间 + 处理时间
// 否则当前的end必须要从前一个顾客的end开始才可以处理,所以当前end = 上一个end + 处理时间
q.front().wait = q.front().end - q.front().p - q.front().t;
// 当前顾客的等待时间就是 当前结束时间点 减去 到达时间点 和 处理时间
windows[index].push(q.front());
q.pop();
}
int f = 0, max = 0, end = 0;
double wait = 0;
while (!windows[f].empty()){
customer cus = windows[f].front();
// cout << "到达时间:" << windows[f].front().t << " 所需时间:" << windows[f].front().p << " 等待时间:" << windows[f].front().wait << " 结束时间:" << windows[f].front().end << " || ";
wait += cus.wait;
max = max < cus.wait ? cus.wait : max;
end = end < cus.end ? cus.end : end;
windows[f].pop();
if(windows[f].empty()&&f!=k-1){
f++;
}
}
cout << fixed << setprecision(1) << wait/n << " ";
cout << max << ' ' << end << endl;
return 0;
}