7-5 银行排队问题之单队列多窗口加VIP服务 (8分)
假设银行有K个窗口提供服务,窗口前设一条黄线,所有顾客按到达时间在黄线后排成一条长龙。当有窗口空闲时,下一位顾客即去该窗口处理事务。当有多个窗口可选择时,假设顾客总是选择编号最小的窗口。
有些银行会给VIP客户以各种优惠服务,例如专门开辟VIP窗口。为了最大限度地利用资源,VIP窗口的服务机制定义为:当队列中没有VIP客户时,该窗口为普通顾客服务;当该窗口空闲并且队列中有VIP客户在等待时,排在最前面的VIP客户享受该窗口的服务。同时,当轮到某VIP客户出列时,若VIP窗口非空,该客户可以选择空闲的普通窗口;否则一定选择VIP窗口。
本题要求输出前来等待服务的N位顾客的平均等待时间、最长等待时间、最后完成时间,并且统计每个窗口服务了多少名顾客。
输入格式:
输入第1行给出正整数N(≤1000),为顾客总人数;随后N行,每行给出一位顾客的到达时间T、事务处理时间P和是否VIP的标志(1是VIP,0则不是),并且假设输入数据已经按到达时间先后排好了顺序;最后一行给出正整数K(≤10)—— 为开设的营业窗口数,以及VIP窗口的编号(从0到K−1)。这里假设每位顾客事务被处理的最长时间为60分钟。
输出格式:
在第一行中输出平均等待时间(输出到小数点后1位)、最长等待时间、最后完成时间,之间用1个空格分隔,行末不能有多余空格。
在第二行中按编号递增顺序输出每个窗口服务了多少名顾客,数字之间用1个空格分隔,行末不能有多余空格。
输入样例:
10
0 20 0
0 20 0
1 68 1
1 12 1
2 15 0
2 10 0
3 15 1
10 12 1
30 15 0
62 5 1
3 1
输出样例:
15.1 35 67
4 5 1
理解:
1.队列在随时间不断变化, 是以时间为天然的序
2.vip可以插队,vip在一个队列时如果有vip柜台空闲那么vip人员不管在队列中那个位置都直接去办理业务
思路:可以以时间为天然的序, 来进行处理,但要注意vip与vip柜台的对应
思考:对于这种大模拟,我们最好在开始时,进行模拟一次,理解模拟规则, 再按照某个方向进行模拟,而本题中直接就有时间这个序,可以直接for(int i = 0; i ; i ++)循环
在我的代码中对该方法进行了优化,使其不会循环不会使用的时间点, 但也导致使用了太多的STL容器, 其结果并不见得优化了多少
#include<bits/stdc++.h>
using namespace std;
const int N = 1101;
int n, k, vip_adress = 0, num[N], max_wait, last_complete, vis[N];//标记是否已经被服务
double all_wait;
struct node
{
int arrive_time;
int solve_time;
bool vis;
}customers[N];
struct counter
{
int id;
int start;
int end_time;
bool operator <(const counter &x)const
{
if(end_time != x.end_time) return end_time > x.end_time;
return id > x.id;
}
}counters[11];
priority_queue<counter> q;//优先队利存储柜台
void get_ans(int x, int y) // 将第y个人放入第x个柜台
{
counters[x].start = max(customers[y].arrive_time, counters[x].end_time);
counters[x].end_time = counters[x].start + customers[y].solve_time;
int x1 = counters[x].start - customers[y].arrive_time;
num[x] ++;
max_wait = max(x1, max_wait);
last_complete = max(last_complete, counters[x].end_time);
all_wait += x1;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> customers[i].arrive_time >> customers[i].solve_time >> customers[i].vis;
customers[i].solve_time = customers[i].solve_time > 60?60:customers[i].solve_time;
}
cin >> k >> vip_adress;
vip_adress ++;//(0, k - 1)
for(int i = 1; i <= k; i++)
{
counters[i].id = i;
q.push(counters[i]);
}
int rear = 1, pre = 1; //创建队列
while(rear <= n)
{
int x = q.top().id;
vector<counter> qual; // 找当前可能用到的柜台
bool flag1 = false;//判断相同时间内有无空闲的vip房间
while(vis[rear] == 1) rear ++;//在队列中还没有完成的人的
while(pre <= n && counters[x].end_time >= customers[pre].arrive_time) pre ++;// 更新有多少人放入队列
if(rear >= pre) pre = rear + 1; // 到达终点rear -> n
if(rear > n) break;
while(q.size() > 0 && (q.top().end_time == counters[x].end_time && counters[x].end_time >= customers[rear].arrive_time) || (counters[x].end_time <= customers[rear].arrive_time && q.top().end_time <= customers[rear].arrive_time)) //将可能用到的柜台放入数组
{//找可能使用的柜台时间相等时,还要注意vip房间
//1如果当前的第一个可能使用的人的到达时间小于最早结束时间, 后序相等的柜台都进
//2如果当前的第一个人到达的时间大于柜台最早结束的时间, 那么在他到达之前的时间内空闲的柜台都进
qual.push_back(q.top());
if(q.top().id == vip_adress)
{
flag1 = true;//标记是否有vip柜台
q.pop();
break;
}
q.pop();
}
if(counters[x].end_time < customers[rear].arrive_time) //当前的第一个人到达的时间大于柜台最早结束的时间
{
while(pre <= n && customers[pre].arrive_time == customers[rear].arrive_time) // 更新所有相等的人
pre ++;
}
bool flag = false;
int id_vip = 0;
for(int i = rear; i < pre; i++)//判断有无vip人
if(customers[i].vis && vis[i] == 0)
{
id_vip = i, flag = true;
break;
}
if(flag1 && flag)//如果是vip且该房间是vip房间
{
get_ans(vip_adress, id_vip);
for(int i = 0; i < qual.size() - 1; i++) q.push(qual[i]);
q.push(counters[vip_adress]);
vis[id_vip] = 1;
}
else
{
if(rear <= n)
{
get_ans(x, rear);
for(int i = 1; i < qual.size(); i++) q.push(qual[i]);
q.push(counters[x]);
vis[rear] = 1;
rear ++;
}
else
break;
}
}
printf("%.1f %d %d\n", all_wait/n, max_wait, last_complete);
for(int i = 1; i <= k; i++) printf("%d%c", num[i], i == k ? '\n':' ');
return 0;
}