队列的应用–银行客户的平均排队问题

  • Post author:
  • Post category:其他


某银行有一个客户办理业务站,在一天内随机地有客户到达,设每位客户的业务办理时间是某个范围内的值。设只有一个窗口,一位业务人员,要求程序模拟统计在

一天时间内,所有客户的平均等待时间。模拟数据按客户到达的先后顺序依次由键盘输入,对应每位客户有两个数据,到达时刻和需要办理业务的时间。

输入格式

第一行:一天内的客户总人数n

第二行:第一个客户的到达时刻和需要办理业务的时间

第三行:第二个客户的到达时刻和需要办理业务的时间

……

第n行:第n – 1个客户的到达时刻和需要办理业务的时间

第n + 1行:第n 个客户的到达时刻和需要办理业务的时间

输出格式

第一行:所有客户的平均等待时间(精确到小数点后2位)

输入样例

3

1 3

2 1

3 5

输出样例

1.33

基本思路:

1、定义一个结构体,表示的是一个人。其中这个结构体数组中的成员属性主要有两个,两个double类型的变量from,wait,表示这个人来到银行的时间以及办理业务所需要的时间。

typedef struct NODE{
   double from;
   double wait;
}Node;

2、定义一个循环队列,从而在初始化队列的时候,不至于分配较大的内存空间,从而造成内存空间的浪费。

3、输入数字n表示银行一共会有多少人来

4、输入from,wait两个数字,from表示新人T来到银行的时间,wait表示这个人需要办理业务所需要的时间。

5、

如果队列为空,表示这个新输入的人前面没有人,所以不需要排队,这时候,我们直接将这个新人T压入到队列中;如果队列不为空,那么从队列中跳出一个人P,然后将这个P最后办理完业务的时间end和T刚刚来到银行的时间newFrom进行比较,如果end小于等于newFrom,那么新人T不需要等待,直接就可以进行办理业务,否则,需要等待,等待的时间为end - newFrom



6、重复4、5的操作,直到输入完了n个人的数据,这时候,我们就可以将所需要等待的平均时间输出了。

对应的代码:

#include<stdio.h>
#include<stdlib.h>
#define ERROR 0
#define OK 1
#define QUEUE_SIZE 100
typedef struct NODE{
    double from;//办事开始的时间
    double end;//办事结束的时间
}Node;
typedef struct QUEUE{
    Node *arr;//定义一个Node类型的指针变量,表示要排队的人
    int front;
    int rear;
    int size;//表示队列的大小
}Queue;
int initQueue(Queue &queue){
   //将队列初始化
   queue.arr = (Node *)malloc(sizeof(Node) * QUEUE_SIZE);
   if(queue.arr == NULL)
      return ERROR;
   queue.front = 0;
   queue.rear = 0;
   queue.size = QUEUE_SIZE;
   return OK;
}
//压队操作
int push(Queue &queue,double from,double end){
   //如果栈为满,那么就回到最开始的时候
   if((queue.rear + 1) % queue.size == queue.front){
      printf("队列为满!!!\n");
      return ERROR;//栈为满,那么返回ERROR,表示压队错误
   }
   queue.arr[queue.rear].from = from;
   queue.arr[queue.rear].end = end;
   queue.rear = (queue.rear + 1) % queue.size;
   return OK;
}
//出队操作
int pop(Queue &queue,double &from,double &end){
   //判断队列是否为空,如果为空,那么返回ERROR
   if(queue.rear == queue.front)
      return ERROR;
      /*
   printf("删除的人起始时间:%.0f,结束时间:%.0f,办事时间:%.0f\n",queue.arr[queue.front].from,
          queue.arr[queue.front].from + queue.arr[queue.front].end,queue.arr[queue.front].end);
          */
   from = queue.arr[queue.front].from;
   end = queue.arr[queue.front].end;
   queue.front = (queue.front + 1) % queue.size;
   return OK;
}
//获取队列头节点的起始时间,结束时间
int getTop(Queue &queue,double &from,double &end){
   if(queue.rear == queue.front)
      return ERROR;
      /*
   printf("队列头起始时间:%.0f,结束时间:%.0f,办事时间:%.0f\n",queue.arr[queue.front].from,
          queue.arr[queue.front].from + queue.arr[queue.front].end,queue.arr[queue.front].end);
          */
   from = queue.arr[queue.front].from;
   end = queue.arr[queue.front].end;
   return OK;
}
int isEmpty(Queue &queue){
  return queue.front == queue.rear;//判断队列是否为空
}
int main(){
  Queue queue;
  int n,i;
  double from,end,wait = 0,newFrom,newWait;
  double time = 0;//表示要等待的时间
  if(!initQueue(queue)){
    printf("创建队列失败!!!\n");
    exit(0);
  }
  scanf("%d",&n);//输入有n个人需要办事
  for(i = 1; i <= n; i++){
    scanf("%lf%lf",&newFrom,&newWait);//输入新人排队的时间以及办事的时间
/*
判断队列是否为空,如果队列不为空,那么不断从队列中跳出一个人P出来,将这个从队列中跳出来的人结束办理业务的时间end和新来银行办理业务的人T的时间newFrom进行比较,如果end大于newFrom,说明新来银行的人需要等待end - newFrom时间,否则不需要等待。重复这一步,直到队列为空,才将新来银行的人压入到队列中
*/
    //这里也可以将while(!isEmpty(queue))来替换if判断,结果是一样的
    if(!isEmpty(queue)){
        pop(queue,from,wait);//获取新来银行的人T前面正在办事的人P
        end = wait + from;//P结束办理业务的时间
       // printf("上一个人来的时间:%.0f,办理业务结束的时间:%.0f\n",from,end);
        if(end > newFrom){
        //如果P结束办理业务的时间大于T刚来银行的时间,那么就说明T需要等待end - newFrom时间
            time += end - newFrom;
         //   printf("第%d个人需要等待的时间为:%.0f\n",i,end - newFrom);
         /*
         (这一步非常重要,因为新来的人T需要等待时间,所以P结束办理业务的时
         间就是T开始办理业务的时间)
         */
            newFrom = end;//更改后面排队的人开始办事的时间
        }
    }

 //   printf("第%d个人开始办理事务的时间%.0f,办事的时间%.0f,最终结束的时间是:%.0f\n",i,newFrom,newWait,newFrom + newWait);//可以用于检验是否正确,或者说哪一块出现了问题
    push(queue,newFrom,newWait);//队列为空的时候,将新来银行的人P压入到队列中
  }
  printf("%.2f",time / n);
  return 0;
}

测试用例:

在这里插入图片描述

用例二:

在这里插入图片描述

好的,看完了对应的代码,接下来我们来解析一下

步骤5的代码

  for(i = 1; i <= n; i++){
    scanf("%lf%lf",&newFrom,&newWait);//输入新人排队的时间以及办事的时间
/*
判断队列是否为空,如果队列不为空,那么不断从队列中跳出一个人P出来,将这个从队列中跳出来的人结束办理业务的时间end和新来银行办理业务的人T的时间newFrom进行比较,如果end大于newFrom,说明新来银行的人需要等待end - newFrom时间,否则不需要等待。重复这一步,直到队列为空,才将新来银行的人压入到队列中
*/
    if(!isEmpty(queue)){
        pop(queue,from,wait);//获取新来银行的人T前面正在办事的人P
        end = wait + from;//P结束办理业务的时间
       // printf("上一个人来的时间:%.0f,办理业务结束的时间:%.0f\n",from,end);
        if(end > newFrom){
        //如果P结束办理业务的时间大于T刚来银行的时间,那么就说明T需要等待end - newFrom时间
            time += end - newFrom;
         //   printf("第%d个人需要等待的时间为:%.0f\n",i,end - newFrom);
         /*
         (这一步非常重要,因为新来的人T需要等待时间,所以P结束办理业务的时
         间就是T开始办理业务的时间)
         */
            newFrom = end;//更改后面排队的人开始办事的时间
        }
    }

 //   printf("第%d个人开始办理事务的时间%.0f,办事的时间%.0f,最终结束的时间是:%.0f\n",i,newFrom,newWait,newFrom + newWait);//可以用于检验是否正确,或者说哪一块出现了问题
    push(queue,newFrom,newWait);//队列为空的时候,将新来银行的人P压入到队列中
  }

好的,看完这个代码,有人就会问了,为什么在获取等待的时间那里,if判断还可以通过while循环来进行判断呢?

我们通过画图来进行解析:

在这里插入图片描述

通过分析,我们可以知道,队列中只有一个人,表示新来到银行的人的前一个在办理业务的人。所以,就不用通过通过while,只要通过if来判断新来到银行的人是否需要等待即可,虽然可以通过while也有同样的效果,但是这里并没有必要哈。

错误的代码(之前做的时候,处理步骤五的代码):

  for(i = 1; i <= n; i++){
    scanf("%lf%lf",&newFrom,&newWait);//输入新人排队的时间以及办事的时间
    if(!isEmpty(queue)){
        getTop(queue,from,wait);//获取新来银行的人T前面正在办事的人P
        end = wait + from;//P结束办理业务的时间
       // printf("上一个人来的时间:%.0f,办理业务结束的时间:%.0f\n",from,end);
        if(end > newFrom){
        //如果P结束办理业务的时间大于T刚来银行的时间,那么就说明T需要等待end - newFrom时间
            time += end - newFrom;
            pop(queue,from,wait);
         //   printf("第%d个人需要等待的时间为:%.0f\n",i,end - newFrom);
         /*
         (这一步非常重要,因为新来的人T需要等待时间,所以P结束办理业务的时
         间就是T开始办理业务的时间)
         */
            newFrom = end;//更改后面排队的人开始办事的时间
        }
    }

 //   printf("第%d个人开始办理事务的时间%.0f,办事的时间%.0f,最终结束的时间是:%.0f\n",i,newFrom,newWait,newFrom + newWait);//可以用于检验是否正确,或者说哪一块出现了问题
    push(queue,newFrom,newWait);//队列为空的时候,将新来银行的人P压入到队列中
  }

通过仔细观察,我们可以知道,错误的代码和正确代码的区别主要在if判断中,正确代码是不管队列的人结束办理业务的时间是否大于新来银行的人来到银行的时间,都要将从队列中跳出一个人,但是错误的代码确实首先获取队列中的一个人,然后那这个人的结束办理业务的时间和新到银行的人到银行的时间进行比较,然后如果队列中跳出的人的办理业务的时间大于新银行的人道银行的时间,那么就从队列中跳出这个人。

嗯,看似效果是一样的,但是我们将行注释的内容显示一下,在进行比较,我们就可以知道为什么不可以使用错误的代码了。过程就不再分析了,大家可以尝试着将对应的样例写出,看看每一次新人到银行是队列中是什么样的,就知道错误代码为什么不可以。

在这里插入图片描述



版权声明:本文为weixin_46544385原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。