「数据结构」第四次作业(2023春 - 银行排队模拟)

该文描述了一个模拟银行排队服务的程序设计问题,采用生产者-消费者模式。系统中有五个窗口,包括三个对私、一个对公和一个外币窗口。当对私窗口平均等待人数超过7人时,银行会临时增加窗口,减少至7人以下时则减少窗口。程序需要处理客户到达、服务时间和窗口调整策略,确保服务效率与成本平衡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第四次作业(银行排队模拟)

5. 银行排队模拟(生产者-消费者模拟) - 分类别

这道题比较难,单独拿出来说。

先再看一遍题目:

题干描述:

【问题描述】

一个系统模仿另一个系统行为的技术称为模拟,如飞行模拟器。模拟可以用来进行方案论证、人员培训和改进服务。计算机技术常用于模拟系统中。

生产者-消费者(Server-Custom)是常见的应用模式,见于银行、食堂、打印机、医院、超等提供服务和使用服务的应用中。这类应用的主要问题是消费者如果等待(排队)时间过长,会引发用户抱怨,影响服务质量;如果提供服务者(服务窗口)过多,将提高运管商成本。(经济学中排队论)

假设某银行网点有五个服务窗口,分别为三个对私、一个对公和一个外币窗口。银行服务的原则是先来先服务。通常对私业务人很多,其它窗口人则较少,可临时改为对私服务。假设当对私窗口等待服务的客户(按实际服务窗口)平均排队人数超过(大于或等于)7人时,等待客户将可能有抱怨,影响服务质量,此时银行可临时将其它窗口中一个或两个改为对私服务,当平均排队客户少于7人时,将立即恢复原有业务。设计一个程序用来模拟银行服务。

说明:

  1. 增加服务窗口将会增加成本或影响其它业务,因此,以成本增加或影响最小为原则来增加服务窗口,即如果增加一个窗口就能使得按窗口平均等待服务人数小于7人,则只增加一个窗口。一旦按窗口平均等待服务人数小于7人,就减少一个所增加的窗口。

  2. 为了简化问题,假设新到客户是在每个服务周期开始时到达。

  3. 根据客户业务服务时间将客户分为3类:1(简单业务)、2(普通业务)、3(复杂业务),分别对应花费1-3个时间周期。

  4. 当等待服务人数发生变化时(新客户到达或有客户已接受服务),则及时计算按实际服务窗口平均等待服务人数,并按相应策略调整服务窗口数(增加或减少额外的服务窗口,但对私窗口不能减少)。

注意:只在获取新客户(不管到达新客户数是否为0)时,才按策略调整服务窗口数。进一步讲,增加服务窗口只在有客户到达的周期内进行(也就是说增加窗口是基于客户的感受,银行对增加窗口是不情愿的,因为要增加成本,一旦不再有新客户来,银行是不会再增加服务窗口的);一旦有客户去接受服务(即等待客户减少),银行将根据策略及时减少服务窗口,因此,在每个周期内,有客户去接受服务后要马上判断是否减少服务窗口(因为能减少成本,银行是积极的)。

本问题中假设对公和对外币服务窗口在改为对私服务时及服务期间没有相应因公或外币服务新客户到达(即正好空闲),同时要求以增加成本或影响最小为前提,来尽最大可能减少对私服务客户等待时间。

【输入形式】

首先输入一个整数表示时间周期数,然后下一行输入每个时间周期到达的客户人数;再依次分行输入每个时间周期到达的因私业务的客户类别。注:一个时间周期指的是银行处理一笔业务的平均处理时间,可以是一分钟、三分钟或其它。每行中的整数之间以一个空格分隔。

例如:

6

2 5 8 11 15 6

1 3

2 2 1 3 2

说明:共有6个时间周期,第1个周期来了2个客户(序号分别为1、2,业务分别为简单业务和复杂业务),第2个时钟周期来了5人(序号分别为3、4、5、6、7,业务分别为普通业务、普通业务、简单业务、复杂业务和普通业务),以此类推。

【输出形式】

每个客户等待服务的时间周期数。输出形式如下:

用户序号 : 等待周期数

说明:客户序号与等待周期数之间用英文冒号:分隔,冒号(:)两边各有一个空格,等待周期数后直接为回车。

【样例输入】

4

2 5 13 11

1 3

2 2 1 3 2

1 1 1 1 3 3 2 2 1 2 3 1 1

3 3 2 1 3 1 1 3 1 3 3

【样例输出】

1 : 0

2 : 0

3 : 0

4 : 0

5 : 2

6 : 2

7 : 2

8 : 1

9 : 2

10 : 3

11 : 3

12 : 4

13 : 4

14 : 4

15 : 6

16 : 7

17 : 7

18 : 8

19 : 8

20 : 9

21 : 8

22 : 9

23 : 10

24 : 11

25 : 12

26 : 12

27 : 12

28 : 13

29 : 13

30 : 14

31 : 15

【样例说明】

样例输入表明有四个时间周期,第一个周期来了2人(序号1-2);第二个周期来了5人(序号3-7);第三个周期来了13人(序号8-20);第四个周期来了11人(序号21-31)。由于第一个时间周期内只来了2人,银行(有三个服务窗口)能及时提供服务,因此客户等待时间为0;第二个时间周期内来了5人,银行一个周期内一次只能服务3人,而且第一个周期内有一位办理复杂业务的客户,所以这时只有两个空闲窗口可以提供服务,前2人等待时间为0,另外3人需要等待;其它类推。

【评分标准】

通过所有测试点得满分。

参考解答:

笑死,助教也被这道题恶心到了,交了两遍总算过了。这题今年(2023春)有修改过,然后原本就模糊的描述更加不明所以了。

这题最难的地方在于理解题目,或者直白点说:什么时候增加窗口什么时候减少窗口

从我的代码里可以看到:

  • 队列人数增加,也就是有新的客户入队时,判断是否应该增加窗口。
  • 队列人数减少,也就是有些人进入窗口后,判断是否应该减少窗口。

从上面两点我们可以知道,当后续不再有客户进入银行后,队列只增不减。

还有一个误区:

题目并未要求每次服务过程中平均等待人数均少于7人。

而且,引用另一个助教的话:如果当前需要减少的这个窗口,正在服务复杂业务,可能还需要1~2个周期才能服务完,那这个窗口在计算时候是要减少(即不能给它分配新的客户),但原先的客户是要继续服务的

如果你还是不能理解,下面我换一种说法:

  1. 这题的队列不是你们传统认知上,比如你一进食堂就决定排哪条队伍,然后就呆在一条队伍直到轮到你的过程。这道题的背景是银行,其实想象成银行的叫号更加准确,即客户一进银行就领取一个号码,然后等待窗口”叫到你“再去接受服务。

  2. 然后就是我在上面提到的队列人数变化。当这一轮有新的客户进入(哪怕是0人),处于”等待区“的人数过多时,就多开窗口,尽量满足平均等待人数小于7人;当一部分等待区中的人到窗口去服务时,等待区人数减少,就判断是否需要减少窗口,这个减少窗口实际上指的是减少“预分配的窗口”,已经在窗口接受服务的用户是不受影响的。

  3. 按照这种理解,如果客户服务尚未完成(是普通或者复杂服务),他仍需在窗口接受服务,但是等待区不包含该用户,在窗口减少的过程中,也不需要影响或者变化该用户的服务情况。

具体的实现看代码吧,思路还是比较清晰的。

#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
	int num;
	int val;
	int wait;
	struct node *next;
} node, *nodeptr;

int main()
{
	int t;
	int nums[20];
	
	int res[200];

	scanf("%d", &t);
	for (int i = 0; i < t; i++)
	{
		scanf("%d", nums + i);
	}

	nodeptr head = (nodeptr)malloc(sizeof(node));
	head->next = NULL;
	nodeptr tail = head;

	int num_cnt = 1;
	int wait_cnt = 0;
	int window_cnt = 3;

	for (int i = 0; i < t; i++)
	{
		// 先把所有新用户加入等待队列
		for (int j = 0; j < nums[i]; j++)
		{
			tail->next = (nodeptr)malloc(sizeof(node));
			tail = tail->next;
			tail->num = num_cnt++;
			scanf("%d", &(tail->val));
			tail->wait = 0;
			tail->next = NULL;
		}

		// 队列人数增加->判断是否应该增加窗口
		wait_cnt += nums[i];
		if (wait_cnt / window_cnt >= 7 && window_cnt < 5)
		{
			window_cnt = (wait_cnt / 7 + 1) <= 5 ? (wait_cnt / 7 + 1) : 5;
		}

		// 窗口处理用户服务
		nodeptr q = head;
		nodeptr p = head->next;
		for (int j = 0; j < window_cnt && p != NULL; j++)
		{
			(p->val)--;
			if (!(p->val))
			{
				res[p->num] = p->wait;
				q->next = p->next;
				free(p);
				p = q->next;
				if (p == NULL)
				{
					tail = q;
				}
			}
			else
			{
				q = p;
				p = p->next;
			}
		}

		// 增加还在等待中的所有客户的等待时间
		wait_cnt = 0;
		while (p != NULL)
		{
			(p->wait)++;
			p = p->next;
			wait_cnt++;
		}

		// 队列人数减少->判断是否应该减少窗口
		if (wait_cnt / window_cnt < 7 && window_cnt > 3)
		{
			window_cnt = (wait_cnt / 7) >= 3 ? wait_cnt / 7 : 3;
		}
	}

	while (head != tail)
	{
		// 窗口处理用户服务
		nodeptr q = head;
		nodeptr p = head->next;
		for (int j = 0; j < window_cnt && p != NULL; j++)
		{
			(p->val)--;
			if (!(p->val))
			{
				res[p->num] = p->wait;
				q->next = p->next;
				free(p);
				p = q->next;
				if (p == NULL)
				{
					tail = q;
				}
			}
			else
			{
				q = p;
				p = p->next;
			}
		}

		// 增加还在等待中的所有客户的等待时间
		wait_cnt = 0;
		while (p != NULL)
		{
			(p->wait)++;
			p = p->next;
			wait_cnt++;
		}

		// 队列人数减少->判断是否应该减少窗口
		if (wait_cnt / window_cnt < 7 && window_cnt > 3)
		{
			window_cnt = (wait_cnt / 7) >= 3 ? (wait_cnt / 7) : 3;
		}
	}

	// 输出结果
	for (int i = 1; i <= num_cnt - 1; i++)
	{
		printf("%d : %d\n", i, res[i]);
	}

	return 0;
}
假设某银行有n个窗口对外接待客户,从早晨银行9点开门起到5点关门,不断有客户进入银行,由于每个窗口在某个时刻只能接待一个客户。因此在客户人数众多时需要在每个窗口前顺次排队,对于刚进银行的客户。如果某个窗口的业务员正空闲,则可上前输业务。反之,若个窗口均有客户所占,他便会排在为数最少的队伍后面。编制一个程序模拟银行的这种业务活动并计算一天中客户在银行的平均逗留时间。 首先从题目析:N个窗口排队,首先就要建立N个队列来存储排队的用户信息 ,然后算出那个队列最短就用户就到那个队伍排队,同时通过随机生成他办理业务的时间和到来的时间,通过计算用户的到来时间和离开时间就可以计算出某个用户在银行的逗留时间 ;话不多说直接上代码。 下面是主函数,由用户输入银行上下班时间,计算营业多长时间Total_time,如何当前时间小于关门的时间,就一直进入customer_into();函数,用户不断的进来 #define FALSE 0 #define TRUE 1 #define QUEUE_SUM 4 //窗口的数量 int rand_business_time=0, rand_wait_time=0;//定义办理时间,等待时间变量 int Total_time=0,now_tim=0;//总时间,当前时间 int go_time[4] = {0,0,0,0};//定义数组存储每个窗口最后一位办理完业务的时间 int sum_nan[4] = {0,0,0,0};//定义数组存储每个窗口排队的人数 int Sign=TRUE; //是否关门标志位 float Sum_Wait_Time=0.0; //等待的总时间 float Sun_Nan=0.0; //总人数 int open_time;//开门时间 int off_time; //关门时间 int main() { Prompted(); printf("输入银行的24小时制营业时间:如营业时间为9:00--17:00,则应输入:9,17\n"); scanf("%d,%d", &open;_time,&off;_time); Total_time = (off_time - open_time) * 60;//计算银行总营业多少钟 for (int i = 0; i now_time) { customer_into(); //客户进入函数 } printf("银行关门时间到不再接收客人\n\n"); for (int i = 0; i < QUEUE_SUM; i++) { DisposeQueue(&queue;[i],i);//输入在银行关门前还没有办理完业务的客户信息 } printf("平均时间为%.2f钟",Sum_Wait_Time/Sun_Nan); /*通过各个客户的总等待时间总和/总人数算出逗留平均时间*/ _getch(); return 0; }
一、 课程设计目的 在多道程序环境下,进程同步问题十重要,通过解决“生产者-消费者”问题,可以帮助我们更好的理解进程同步的概念及实现方法。掌握线程创建和终止的方法,加深对线程和进程概念的理解,会用同步与互斥方法实现线程之间的进行操作。 在学习操作系统课程的基础上,通过实践加深对进程同步的认识,同时,可以提高运用操作系统知识解决实际问题的能力;锻炼实际的编程能力、创新能力及团队组织、协作开发软件的能力;还能提高调查研究、查阅技术文献、资料以及编写软件设计文档的能力。 二、 课程设计内容 模拟仿真“生产者-消费者”问题的解决过程及方法。 三、 系统析与设计 1、 系统析 在OS中引入进程后,虽然提高了资源的利用率和系统的吞吐量,但由于进程的异步性,也会给系统造成混乱,尤其是在他们争用临界资源时。为了对多个相关进程在执行次序上进行协调,以使并发执行的诸程序之间能有效地共享资源和相互合作,使程序的执行具有可再现性,所以引入了进程同步的概念。信号量机制是一种卓有成效的进程同步工具。 在生产者---消费者问题中应注意(信号量名称以多个生产者和多个消费者中的为例):首先,在每个程序中用于互斥的wait(mutex)和signal(mutex)必须成对出现;其次,对资源信号量empty和full的wait和signal操作,同样需要成对地出现,但它们别处于不同的程序中。生产者消费者进程共享一个大小固定的缓冲区。其中,一个或多个生产者生产数据,并将生产的数据存入缓冲区,并有一个或多个消费者从缓冲区中取数据。 2、 系统设计: 系统的设计必须要体现进程之间的同步关系,所以本系统采用2个生产者、2个消费者 和20个缓冲区的框架体系设计。为了更能体现该系统进程之间的同步关系,系统的生产者消费者的速度应该可控,以更好更明显的表现出结果。 为了使本系统以更加简单、直观的形式把“消费者-生产者”问题表现出来,我选择了使 用可视化界面编程。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值