队列
银行业务队列简单模拟 (25分)
设某银行有A、B两个业务窗口,且处理业务的速度不一样,其中A窗口处理速度是B窗口的2倍 —— 即当A窗口每处理完2个顾客时,B窗口处理完1个顾客。给定到达银行的顾客序列,请按业务完成的顺序输出顾客序列。假定不考虑顾客先后到达的时间间隔,并且当不同窗口同时处理完2个顾客时,A窗口顾客优先输出。
输入格式:
输入为一行正整数,其中第1个数字N(≤\le≤1000)为顾客总数,后面跟着N位顾客的编号。编号为奇数的顾客需要到A窗口办理业务,为偶数的顾客则去B窗口。数字间以空格分隔。
输出格式:
按业务处理完成的顺序输出顾客的编号。数字间以空格分隔,但最后一个编号后不能有多余的空格。
输入样例:
8 2 1 3 9 4 11 13 15
输出样例:
1 3 2 9 11 4 13 15
/*
解题思路:
1)将入队元素按题意,分别进入A队列和B队列;
2)再将控制出队顺序和数量即可(A优先出队两个,B出队一个)
提交代码:
编译器:g++
*/
#include <iostream>
using namespace std;
const int MAXN = 1002;
int Aqueue[MAXN], Bqueue[MAXN], Ans[MAXN];//A队列、B队列
int main()
{
int n, x;
int indexA = 0, indexB = 0, index = 0;
cin>>n;
for(int i = 0; i < n; ++i)
{
cin>>x;
if(x%2) Aqueue[indexA++] = x;
else Bqueue[indexB++] = x;
}
for(int i = 0, j = 0; i < indexA || j < indexB; ++i, ++j)
{
if(i < indexA)
Ans[index++] = Aqueue[i];
if(i < indexA-1) Ans[index++] = Aqueue[i + 1], i++;//如果A队列可以出队两个元素,则输出两个
if(j < indexB)
Ans[index++] = Bqueue[j];
}
for(int i = 0; i < index; ++i)
{
if(i) cout<<' ';
cout<<Ans[i];
}
return 0;
}
银行排队问题之单队列多窗口服务 (25分)
假设银行有KKK个窗口提供服务,窗口前设一条黄线,所有顾客按到达时间在黄线后排成一条长龙。当有窗口空闲时,下一位顾客即去该窗口处理事务。当有多个窗口可选择时,假设顾客总是选择编号最小的窗口。
本题要求输出前来等待服务的NNN位顾客的平均等待时间、最长等待时间、最后完成时间,并且统计每个窗口服务了多少名顾客。
输入格式:
输入第1行给出正整数NNN(≤1000\le 1000≤1000),为顾客总人数;随后NNN行,每行给出一位顾客的到达时间T
和事务处理时间P
,并且假设输入数据已经按到达时间先后排好了顺序;最后一行给出正整数KKK(≤10\le 10≤10),为开设的营业窗口数。这里假设每位顾客事务被处理的最长时间为60分钟。
输出格式:
在第一行中输出平均等待时间(输出到小数点后1位)、最长等待时间、最后完成时间,之间用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 61
5 3 1
解题思路:注意到达时间和窗口结束时间的关系,以及窗口结束时间的动态改变。
1)到达时间会选择窗口结束时间最短的那个窗口(包括提前结束的窗口)
2)选择窗口之后,然后计算等待时间,并动态的更新该窗口的结束时间
3)将等待时间计入总等待时间
提交代码:
编译器:g++
#include <iostream>
#include <stdio.h>
using namespace std;
const int MAXN = 1002;
const int MAXW = 12;
const int INF = (1<<30);
struct infi{
int atime, rtime;
}costmer[MAXN];//一个顾客包含到达时间、处理时间
struct window{
int peo;
int endtime;
}w[MAXW]; //窗口,其中应该有处理的人数以及结束的时间
int main()
{
int n, k;
int WaitTime, MaxWaitTime = 0,TotWaitTime;
scanf("%d", &n);
for(int i = 0; i < n; ++i)
{
scanf("%d%d", &costmer[i].atime, &costmer[i].rtime);
if(costmer[i].rtime > 60) costmer[i].rtime = 60;
}
scanf("%d", &k);
for(int i = 0; i < n; ++i)
{
int index = 0;
int MinEndTime = INF;
for(int win = 0; win < k; ++win)//模拟排队情况
{
if(w[win].endtime < costmer[i].atime)//如果第i个人到达时,会选择没有人的窗口,也就是结束时间早于到达时间的窗口
{
index = win;
break;
}
if(w[win].endtime < MinEndTime)
{
MinEndTime = w[win].endtime;//得到最早的结束的窗口
index = win;
}
}
w[index].peo++;
if(w[index].endtime > costmer[i].atime)//计算等待时间和index窗口终止时间
{
WaitTime = w[index].endtime - costmer[i].atime;
w[index].endtime = w[index].endtime + costmer[i].rtime;
}
else
{
WaitTime = 0;
w[index].endtime = costmer[i].atime + costmer[i].rtime;
}
TotWaitTime += WaitTime;//将等待时间计入总等待时间
if(WaitTime > MaxWaitTime) MaxWaitTime = WaitTime;//并得到最长的等待时间
}
int EndTime = w[0].endtime;
for(int i = 1; i < k; ++i)//计算整个队列的最长结束时间
if(EndTime < w[i].endtime)
EndTime = w[i].endtime;
printf("%.1lf %d %d\n", 1.0 * TotWaitTime / n, MaxWaitTime, EndTime);
for(int i = 0; i < k; ++i)
{
if(i) printf(" ");
printf("%d", w[i].peo);
}
return 0;
}
银行排队问题之单窗口“夹塞”版 (30分)
排队“夹塞”是引起大家强烈不满的行为,但是这种现象时常存在。在银行的单窗口排队问题中,假设银行只有1个窗口提供服务,所有顾客按到达时间排成一条长龙。当窗口空闲时,下一位顾客即去该窗口处理事务。此时如果已知第iii位顾客与排在后面的第jjj位顾客是好朋友,并且愿意替朋友办理事务的话,那么第iii位顾客的事务处理时间就是自己的事务加朋友的事务所耗时间的总和。在这种情况下,顾客的等待时间就可能被影响。假设所有人到达银行时,若没有空窗口,都会请求排在最前面的朋友帮忙(包括正在窗口接受服务的朋友);当有不止一位朋友请求某位顾客帮忙时,该顾客会根据自己朋友请求的顺序来依次处理事务。试编写程序模拟这种现象,并计算顾客的平均等待时间。
输入格式:
输入的第一行是两个整数:1≤N≤100001\le N \le 100001≤N≤10000,为顾客总数;0≤M≤1000 \le M \le 1000≤M≤100,为彼此不相交的朋友圈子个数。若MMM非0,则此后MMM行,每行先给出正整数2≤L≤1002\le L \le 1002≤L≤100,代表该圈子里朋友的总数,随后给出该朋友圈里的LLL位朋友的名字。名字由3个大写英文字母组成,名字间用1个空格分隔。最后NNN行给出NNN位顾客的姓名、到达时间TTT和事务处理时间PPP(以分钟为单位),之间用1个空格分隔。简单起见,这里假设顾客信息是按照到达时间先后顺序给出的(有并列时间的按照给出顺序排队),并且假设每个事务最多占用窗口服务60分钟(如果超过则按60分钟计算)。
输出格式:
按顾客接受服务的顺序输出顾客名字,每个名字占1行。最后一行输出所有顾客的平均等待时间,保留到小数点后1位。
输入样例:
6 2
3 ANN BOB JOE
2 JIM ZOE
JIM 0 20
BOB 0 15
ANN 0 30
AMY 0 2
ZOE 1 61
JOE 3 10
输出样例:
JIM
ZOE
BOB
ANN
JOE
AMY
75.2
解题思路:等待时间上题中已说明,这题处理大体也是这个思路,但是还要考虑插队的情况,下面考虑如何处理插队的情况
1)首先要将每一个人对应到所在的朋友圈。题中给定的人名只有三个大写字母,则对应为数字;再利用数组映射到所在的朋友圈
2)模拟排队时,先对朋友队列排队,然后再将人员排到对应朋友队列的末尾,并更新结束时间。
3)如果朋友队列不在在大队列中(包括已经处理完走掉的朋友),则他作为所属朋友队列的对头。
4)按题意输出所需内容
我采用的如下数据结构存储信息:
提交代码:
编译器:g++
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int MAXN = 102;//朋友圈的大小
const int MAXP = 18000;//人名映射为数字后的最大容量
typedef struct Person Per;
typedef struct Friend Fri;
typedef struct Queue Q;
struct Person{
char name[5];
int sTime, rTime;
struct Person *next;
};//每个人
struct Friend{
int endTime;
struct Person *first, *last;
struct Friend *next, *pre;
};//小队列
struct Queue{
struct Friend *head, *tail;
}q;//大队列
struct Friend *isInQueue[MAXN];//用映射直接对应一个朋友圈
int name[MAXP];//用映射直接对应一个人
int getHash(char *str);
struct Queue buildQueue(void);
void Push(Per *tmp, Q &q, int index);
int main()
{
int n, m, k;
int s, e;
int totWaitTime = 0, endTime = 0;
char str[5];
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i)
{
scanf("%d", &k);
for(int j = 0; j < k; ++j)
{
scanf("%s", str);
int index = getHash(str);//将人名映射为数字
name[index] = i;//该人员所对在第i个朋友圈
}
}
q = buildQueue();//建立队列
for(int i = 0; i < n; ++i)
{
Per *tmpp = new Per;
tmpp->next = NULL;
scanf("%s %d %d", tmpp->name, &tmpp->sTime, &tmpp->rTime);
if(tmpp->rTime > 60) tmpp->rTime = 60;//如果处理时间超过60,按60计
int index = name[getHash(tmpp->name)];//得到该人员所在的朋友圈
if(isInQueue[index] == NULL)//如果他是该朋友圈第一到达的人员,那么不能插队
{
Push(tmpp, q, index);
}
else//否则看是否可以插队
{
if(isInQueue[index]->endTime >= tmpp->sTime)//如果该朋友圈最后结束的时间晚于他到达的时间
{
Fri *tmpf = isInQueue[index];
tmpf->last->next = tmpp;
tmpf->last = tmpp;//当前人员插队
tmpf->endTime += tmpp->rTime;//更新该朋友圈的最后结束时间
Fri *tmpfn = tmpf->next;//更新被插队的人员的结束时间
int nEndTime = tmpf->endTime;
while(tmpfn)
{
Per *tmppn = tmpfn->first;
while(tmppn)
{
if(nEndTime >= tmppn->sTime)
nEndTime += tmppn->rTime;
else
nEndTime = tmppn->sTime + tmppn->rTime;
tmppn = tmppn->next;
}
tmpfn->endTime = nEndTime;//更新之后的朋友圈
tmpfn = tmpfn->next;
}
}
else//如果该朋友圈的人都处理完了,只好排在队尾
{
Push(tmpp, q, index);
}
}
}
q.head = q.head->next;
while(q.head)//输出处理的人员顺序,并计算总等待时间
{
Per *tmp = q.head->first;
while(tmp)
{
if(endTime > tmp->sTime)
{
totWaitTime += endTime - tmp->sTime;
endTime += tmp->rTime;
}
else
endTime = tmp->sTime + tmp->rTime;
printf("%s\n", tmp->name);
tmp = tmp->next;
}
q.head = q.head->next;
}
printf("%.1lf\n", 1.0 * totWaitTime / n);
return 0;
}
int getHash(char *str)//原来是十进制,错的很莫名其妙;之后改用26进制,就对了
{
int index = 0;
index += (str[0] - 'A') * 26 * 26;
index += (str[1] - 'A') * 26;
index += (str[2] - 'A');
return index;
}
struct Queue buildQueue(void)
{
Q q;
Fri *tmp = new Fri;
tmp->endTime = 0;
tmp->first = tmp->last = NULL;
tmp->next = tmp->pre = NULL;
q.head = q.tail = tmp;
return q;
}
void Push(Per *tmp, Q &q, int index)
{
Fri *tmpf = new Fri;
isInQueue[index] = tmpf;//将新朋友圈定位在tmpf这里
tmpf->first = tmpf->last = tmp;
tmpf->next = NULL, tmpf->pre = q.tail;
q.tail->next = tmpf;
q.tail = tmpf;//更新大队列
if(tmpf->pre->endTime > tmp->sTime)
tmpf->endTime = tmpf->pre->endTime + tmp->rTime;
else
tmpf->endTime = tmp->sTime + tmp->rTime;
}