7-16银行排队问题之单队列多窗口服务(25分)
假设银行有ķ个窗口提供服务,窗口前设一条黄线,所有顾客按到达时间在黄线后排成一条长龙。当有窗口空闲时,下一位顾客即去该窗口处理事务。当有多个窗口可选择时,假设顾客总是选择编号最小的窗口。
本题要求输出前来等待服务的Ñ位顾客的平均等待时间,最长等待时间,最后完成时间,并且统计每个窗口服务了多少名顾客。
输入格式:
输入第1行给出正整数N(≤1000),为顾客总人数;随后Ñ行,行每一位给出的顾客到达时间T
状语从句:事务处理时间P
,并且假设输入数据已经按到达时间先后排好了顺序;最后一行给出正整数K(≤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
这个题目刚开始感觉无从下手,经过自习思考边写代码边改最终还是做出来了。
首先,把所有客户信息和柜台信息都输入之后开始正式解决这个问题。
我使用的循环嵌套,外层大循环是遍历客户,到一个就把一个人安排到一个窗口,而这个循环里需要判断当前窗口是否有空余,是否需要等待,等待时间,更新窗口信息。
具体解决方法是:先遍历一边窗口,记录先最小服务剩余时间和该窗口的序号。
然后判断客户到达时间与当前时间加上最小服务剩余时间之和大小(对应第50行),如果前者比后两者之和还大(Q[j].T>alltime+mintime),说明当客户到达之前那个最小服务剩余时间的窗口的服务时间已经过来,它已经空闲了(注意其他窗口可能也已经空闲了),所以客户就不用等待。这时候还要再遍历一遍窗口,把所有窗口的服务剩余时间减去客户到达时间减去当前总时间之差,然后在遍历一遍窗口找到空闲的序号最小的窗口,记录先来位置。然后让客户进入这个窗口(窗口服务剩余之间等于客户事务时间,窗口服务人数加1)最后需更新当前总用时,前面已经把窗口剩余时间减去客户到达时间减去当前总时间之差了,当前总用时更新为:上次总用时+客户到达时间减去当前总时间之差(alltime+=needtime;)。
否则(客户到达时间小于当前时间加上最小服务剩余时间之和)(对应67行else),说明客户需要等待,这是计算出需要等待的时间(waittime=alltime+mintime-Q[j].T),更新总等待时间,判断是否为最长等待时间。然后再一次遍历数组,所以窗口服务剩余时间减去mintime,然后让客户到第一次遍历窗口记录下的location窗口去(因为locating是最小时间窗口,减去mintime之后肯定为0且仅为0),更新窗口,然后更新当前时间(alltime+=mintime;)。
以上循环体里的内容。
出来循环之后,还要把当前窗口所有客户都完成事务之后才算正在的结束。
#include<stdio.h>
int main(){
typedef struct{
int T;
int P;
}Queue;
typedef struct{
int residue;
int sum;
}counter;
int N,K;
scanf("%d",&N);
Queue Q[1005]; //用数组代表队列
counter window[10]; //窗口数组
for(int i=0;i<N;i++) //输入N个人的数据
{
int T1,P1;
scanf("%d %d",&T1,&P1);
Q[i].T=T1;
if(P1>=60) //根据要求服务时间最多为60
Q[i].P=60;
else
Q[i].P=P1;
}
scanf("%d",&K);
for(int i=0;i<K;i++){ //将窗口的服务人数,服务时间置0
window[i].residue=0;
window[i].sum=0;
}
int alltime=0,sumtime=0,maxtime=0; //花费总时间,等待总时长,等待最大时长
for(int j=0;j<N;j++) //外层循环:对队列每个人遍历。
{
int mintime=9999; //记录窗口的服务剩余最短的时间
int location = 0; //记录窗口的位置
for(int i=0;i<K;i++){ //遍历窗口,这一次遍历是为了找到服务剩余最短的时间的窗口
if(window[i].residue==0){ //如果服务剩余为0,则不用向后找了,直接记录位置和服务剩余时间,跳出循环
mintime=0;
location=i;
break;
}
if(mintime>window[i].residue){ //记录先小于当前最短时间的窗口和服务最短时间
mintime=window[i].residue;
location=i;
}
}
if(Q[j].T>alltime+mintime){ //如果客户到达时间大于当前用的总时间(当前时间)+窗口服务最短时间,则不用等待
int needtime=Q[j].T-alltime; //记录先当前用时(当前时间)到客户到来中间有多少时间
for(int i=0;i<K;i++){ //窗口服务剩余时间减去这一段时间
if(window[i].residue<=needtime)
window[i].residue=0;
else
window[i].residue-=needtime;
}
for (int i=0; i<K; i++) { //找到窗口序号最小的为0的窗口(因为顾客不需要的所有来的时候一定有空闲窗口)
if(window[i].residue==0){
location=i;
break;
}
}
window[location].residue+=Q[j].P; //该窗口接待该客户,服务时间等于客户事务处理时间P
window[location].sum++; //该窗口服务人数加1
alltime+=needtime; //用时总时间应该加上needtime(因为从上一次记录时间到现在就是needtime这段时间)
}else{ //如果客户到达时间小于当前时间+窗口服务最短时间,则需要等待
int waittime=alltime+mintime-Q[j].T; //等待时间等于当前时间+剩余服务时间-客户到达时间
sumtime+=waittime; //等待总时间更新
if(waittime>maxtime) //判断是否为最大等待时间
maxtime=waittime;
for(int i=0;i<K;i++){ //遍历链表,服务窗口减去最小剩余时间
window[i].residue-=mintime;
}
window[location].residue+=Q[j].P; //该窗口接待该孤苦
window[location].sum++; //服务总人数加1
alltime+=mintime; //总用时更新
}
}
int max=0;
for(int i=0;i<K;i++){ //最后一个客户开始服务后,需要当前窗口所有客户都事务完毕后才算真正的结束
if(window[i].residue>max) //找到当前窗口最大剩余时间
max=window[i].residue;
}
alltime+=max;//总用时更新
printf("%.1f %d %d\n",1.0*sumtime/N,maxtime,alltime);
for(int i=0;i<K;i++){
printf("%d",window[i].sum);
if(i<K-1)
printf(" ");
}
}