7-48 银行排队问题之单窗口“夹塞”版 (30分)-map+优先队列

排队“夹塞”是引起大家强烈不满的行为,但是这种现象时常存在。在银行的单窗口排队问题中,假设银行只有1个窗口提供服务,所有顾客按到达时间排成一条长龙。当窗口空闲时,下一位顾客即去该窗口处理事务。此时如果已知第i位顾客与排在后面的第j位顾客是好朋友,并且愿意替朋友办理事务的话,那么第i位顾客的事务处理时间就是自己的事务加朋友的事务所耗时间的总和。在这种情况下,顾客的等待时间就可能被影响。假设所有人到达银行时,若没有空窗口,都会请求排在最前面的朋友帮忙(包括正在窗口接受服务的朋友);当有不止一位朋友请求某位顾客帮忙时,该顾客会根据自己朋友请求的顺序来依次处理事务。试编写程序模拟这种现象,并计算顾客的平均等待时间。

输入格式:

输入的第一行是两个整数:1≤N≤10000,为顾客总数;0≤M≤100,为彼此不相交的朋友圈子个数。若M非0,则此后M行,每行先给出正整数2≤L≤100,代表该圈子里朋友的总数,随后给出该朋友圈里的L位朋友的名字。名字由3个大写英文字母组成,名字间用1个空格分隔。最后N行给出N位顾客的姓名、到达时间T和事务处理时间P(以分钟为单位),之间用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. 朋友圈划分。通过map容器实现,first为名字,second为对应朋友圈编号,若朋友圈仅有一个人,将不会存储到map容器中。
  2. 排队队列实现。构建优先队列数组,每一个数组元素为一个优先队列,表示队列首位顾客需要办理的业务(包括自身和替朋友办理的业务)。开始往队列数组添加顾客,首先从第一个数组元素开始,索引依次递增,直到遇到两个情况,跳出循环:
    1. 当前数组元素优先队列的顾客数为零(size()=0),说明当前队列没有其他顾客,该顾客为这个队列的首位顾客,该顾客的离开时间设置为leaveTime[i]=max(leaveTime[i-1],arrTime)+costTime,即上一个队列元素首位顾客离开时间和该顾客到达时间的最大值加上该顾客办理业务所耗时间。
    2. 该顾客所属朋友圈编号和当前元素队列首位顾客朋友圈编号一致,并且该顾客到达时间小于队列首位顾客的离开时间,将该顾客加入当前元素队列,并更新首位各科离开时间。
  3. 等待时间统计。设置两个变量,总等待时间totalTime,当前时间currentTime,初值均为零。遍历优先队列数组,进入一个元素队列前,currentTime=max(current,arrTime),即将其值取为curreTime和当前队列首位顾客到达时间的最大值,之后有totalTime+=currentTime-arrTime。在一个队列内,由于各顾客的业务办理时间是连续的,所以处理完一个顾客业务之后,currentTime+=costTime。

代码如下:

#include <iostream>
#include <queue>
#include <map>
using namespace std;
#define NMax 10000
#define MMax 100
struct customer{
	char Name[4];
	int arrTime,costTime;
	bool operator < (const customer &a) const{
	return arrTime>a.arrTime;
	}
};
int main(){
	int M,N,K,i,j;
	char name[4];
	int leaveTime[MMax];
	map<string,int> SN;
	priority_queue<struct customer> q[NMax];
	cin>>N>>M;	
	for(i=0;i<M;i++){
		cin>>K;
		while(K--){
			scanf("%s",name);
			SN[name]=i;
		//	cout<<name<<endl;
		}		
	}
	struct customer c;
	int cnt=0;
	for(i=0;i<N;i++){
		scanf("%s%d%d",c.Name,&c.arrTime,&c.costTime);
		c.costTime=c.costTime<=60?c.costTime:60;
		//cout<<c.Name<<" "<<c.arrTime<<" "<<c.costTime<<endl;
		for(j=0;j<NMax;j++){
			if(!q[j].size()){
				q[j].push(c);
				if(j==0)
					leaveTime[j]=c.costTime;
				else
					leaveTime[j]=(c.arrTime>leaveTime[j-1]?c.arrTime:leaveTime[j-1])+c.costTime;
				cnt++;
				break;
			}
			else if(c.arrTime<=leaveTime[j]&&SN.find(c.Name)!=SN.end()&&SN[c.Name]==SN[q[j].top().Name]){
				q[j].push(c);
				leaveTime[j]+=c.costTime;
				break;
			}
		}
	}
	int totalTime=0,currentTime=0;
	for(i=0;i<cnt;i++){
		currentTime=currentTime>q[i].top().arrTime?currentTime:q[i].top().arrTime;
		while(!q[i].empty()){
			totalTime+=currentTime-q[i].top().arrTime;
			printf("%s\n",q[i].top().Name);
			//printf("%s %d %d %d\n",q[i].top().Name,i,currentTime,totalTime);
			currentTime+=q[i].top().costTime;
			q[i].pop();	
		}	
	}
	printf("%.1f\n",totalTime*1.0/N);	
	return 0;
}

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值