7-2 银行排队问题之单窗口“夹塞”版 (30 分)

PTA
排队“夹塞”是引起大家强烈不满的行为,但是这种现象时常存在。在银行的单窗口排队问题中,假设银行只有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

题解:思路在注释里,就不在这里写了。

#include <bits/stdc++.h>    //双向链表实现,本题还有一个更好的优化方法,可以通过记录朋友属性的次数来忽略不必要的搜索
using namespace std;
typedef struct consumer
{
	char name[5];
	double arrive;
	double start;
	double process;
	double end;
	double wait;
	struct consumer *next; //朋友找人帮忙看的是end,输出看的是start. 
	struct consumer *prior;
}List;
int main()
{
	int n,m;
	char name[5];
	int record[60][60][60]={0};  //这个数组每一个下标代表着名字的每一个字母,数组元素存放着共同属性---是不是朋友,一种更为简单的方法是使用map函数
	List *head=NULL,*tail,*p;    //本机调试不减65很容易段错误,可能是三维数组的特性?
	cin>>n>>m;
	for (int i=1;i<=m;i++)   //输入朋友圈 
	{
		int k;
		cin>>k;
		for (int j=0;j<k;j++)
		{
			getchar();
			scanf("%s",name);
			record[name[0]-65][name[1]-65][name[2]-65]=i;
		}
	}
	double arrive,process,lastendtime=0;
	for (int i=0;i<n;i++)   //每个人的信息并在输入的过程中查看前面有没有朋友 
	{                       //有没有朋友指自己的到达时间比朋友离开的时间要早,即下面的判断条件 
		int flag=0;         //record[temp->name[0]-65][temp->name[1]-65][temp->name[2]-65]==record[p->name[0]-65][p->name[1]-65][p->name[2]-65]&&p->arrive<=temp->end
		scanf("%s%lf%lf",name,&arrive,&process);
		if (process>60) process=60;
		p=(List *)malloc(sizeof(List));   //为朋友节点赋值 
		p->next=NULL;
		p->prior=NULL;
		strcpy(p->name,name);
		p->arrive=arrive;
		p->process=process;
		if (i==0)                        //第一个人一定是一来就被处理 
		{
			p->start=p->arrive;
		}
		else                            //lastendtime是窗口的availiable的时间 
		{
			if (p->arrive<=lastendtime)    //到的时间比availiable时间早,那么需要等一会,一有空就去处理 
			p->start=lastendtime;
			else                          //这是窗口有一段空的时间,来了就处理,同时要注意下边56行对lastendtime的刷新 
			p->start=p->arrive;
		}
		p->end=p->start+p->process;       //结束时间 
		lastendtime=p->end;              //刷新 
		p->wait=p->start-p->arrive;        //计算此人的等待时间 
		if (p->wait<0)   p->wait=0;        //如果等待时间为负,那么说明来的比lastendtime晚,没等待(注:实际上,53行已考虑了) 
		if (head==NULL)       //插入 
		{
			head=p;
			tail=p;            //别忘了,不然后面的插入会发生段错误 
		}
		else
		{
				for (List *temp=tail;temp!=NULL;temp=temp->prior)   //找朋友 
				{
					if (record[temp->name[0]-65][temp->name[1]-65][temp->name[2]-65]==record[p->name[0]-65][p->name[1]-65][p->name[2]-65]&&p->arrive<=temp->end)   //temp处是要插入的前一个位置 
					{   //如果二人属性相同并且后来者是赶在朋友走之前来的 
						flag=1;  //需要在前面插入 
						for (List *rr=temp->next;rr!=NULL;rr=rr->next)   //temp是他的朋友,在这个人加塞后,后面的每个人都要 
						{											//加时长 
							rr->start+=p->process;
							rr->end+=p->process;
							rr->wait=rr->start-rr->arrive;
							if (rr->wait<=0) rr->wait=0;
						}
						if (temp->next==NULL)    //样例中BOB和ANN的情况,如果朋友也在最后,那么tail要移动到这个人 
						{
							tail=p;
						}
						p->start=temp->end;           //朋友的结束就是此人的开始 
						p->end=p->start+p->process;   //刷新此人的所有信息(82-84) 
						p->wait=p->start-p->arrive;
						if (p->wait<0) p->wait=0;
						p->next=temp->next;
						if (temp->next!=NULL)    //连接,画图理解 
						temp->next->prior=p;
						p->prior=temp;
						temp->next=p;
						break;
					}
				}
				if (flag==0)   //此人没有找到朋友,可怜,只能尾插 
				{
					p->prior=tail;
					tail->next=p;
					tail=p;
				}
		}
	}
	for (List *tt=head;tt!=NULL;tt=tt->next)   //输出名字 
	{
		printf("%s\n",tt->name);
	}
	double sum=0;
	for (List *temp=head;temp!=NULL;temp=temp->next)  //计算总等待时间 
	{ 
		sum+=temp->wait;
	}
	printf("%.1lf",sum/n);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值