模拟服务台前的排队问题
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include<malloc.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
/**********************结构体************************************************/
//事件表节点
typedef struct Event
{
int OccurTime; //事件发生时刻
int NType; //事件类型,0表示到达事件,1至n表示四个窗口的离开事件
struct Event *next;
}Event, *EventList;
//队列节点
typedef struct QElemType
{
int ArriveTime;//到达时间
int Duration;//办理业务所需时间
struct QElemType *next;
}QElemType;
//队列指针
typedef struct
{
QElemType *front;//头指针
QElemType *rear;//尾指针
}LinkQueue;
/********************函数申明*************************************************/
//事件表基本操作函数
Event NewEvent(int occurT,int nType); //根据OccurTime和NType值,创建新事件
int InitList();//初始化事件链表
int OrderInsert(EventList L, Event e);//将事件e按发生时间顺序插入有序链表L中
int ListEmpty(EventList L);//判断链表L是否为空,为空返回TRUE,否则返回FALSE
int DelFirst(EventList L,Event *memorry); //链表L不为空,删除其首结点,用memorry返回,并返回OK;否则返回ERROR
int ListTraverse(EventList L);//遍历链表
//队列基本操作函数
int InitQueue(LinkQueue *Q); //初始化队列Q
int EmptyQueue(LinkQueue *Q); //若队列Q为空,返回TRUE,否则返回FALSE
int DelQueue(LinkQueue *Q, QElemType *memrroy); //若队列Q不为空,首结点出队,用memrroy返回,并返回OK;否则返回ERROR
int EnQueue(LinkQueue *Q, QElemType e);//结点e入队Q
int QueueLength(LinkQueue *Q);//返回队列Q的长度,即元素个数
int GetHead(LinkQueue *Q, QElemType *memorry);//若队列Q不为空,用memrroy返回其首结点,并返回OK,否则返回ERROR
int QueueTraverse(LinkQueue *Q);//遍历队列Q
//排队主操作函数
int ShortestQueue();//获取最短队列的编号
void OpenForDay(); //初始化操作
void CustomerArrived();//顾客达到事件
void CustomerDepature();//顾客离开事件
void Bank_Simulation(); //银行排队模拟
//显示函数
void PrintEventList();//显示当前事件表
void PrintQueue();//显示当前窗口队列
/****************************全局变量*****************************************/
#define MAXSIZE 20 //宏定义
EventList ev;//事件指针变量
Event en; //事件
LinkQueue q[MAXSIZE];//队列指针结构体数组
QElemType customer;//队列节点
int windows_num;//窗口个数
int TotalTime,CustomerNum;//顾客总时间,顾客总人数
int CloseTime=50;//关闭时间,即营业时间长度
/***********************主函数***********************************************/
int main()
{
srand((unsigned)time(NULL));//设置随机种子
Bank_Simulation();//银行模拟排队
return 0;
}
/*******************银行排队主操作函数****************************************/
//银行开门初始化
void OpenForDay()
{
int i;
//全局变量赋初值
TotalTime = 0;
CustomerNum = 0;
//建立空事件表
InitList();
//对一天中的第一个事件发生事件和类型赋初值
en.OccurTime=0;
en.NType=0;
//第一个事件发生预订,插入事件表
OrderInsert(ev,en);
//得到用户输入窗口数
printf("请输入排队窗口个数:");
scanf("%d",&windows_num );
while(windows_num<1 || windows_num>MAXSIZE)
{ printf("请输入1到20之间的数:");
scanf("%d",&windows_num );
}
//得到用户输入银行关闭时间
printf("请输入银行关闭时间:");
scanf("%d",&CloseTime);
//建立若干个空队列
for(i = 0; i < windows_num; i++)
InitQueue(&q[i]);//初始化windows_no个窗口队列
}
//客户到达事件处理
void CustomerArrived()
{
int durtime,intertime,i,t;
QElemType e;
++CustomerNum;
intertime = rand()%5 + 1; //间隔时间在5分钟内
durtime = rand()%30 + 1; //办理业务时间在30分钟内
t = en.OccurTime + intertime;//下一客户到达的时间
//银行尚未关门
if(t < CloseTime)
{ //下一个顾客来的预订,插入事件表
printf("\n上一个顾客到达的时间:%d,现在也入列\n",en.OccurTime);
OrderInsert(ev, NewEvent(t,0));
//顾客来到后入人数最少的队列
e.ArriveTime = en.OccurTime;//入队时间
e.Duration = durtime;//办理业务时间
i = ShortestQueue();//得到最短队列编号
EnQueue(&q[i], e);//入队操作
//如果入列后排在队头,则发生离开事件的预订
if(QueueLength(&q[i]) == 1)
OrderInsert(ev, NewEvent(en.OccurTime + durtime, i+1));//窗口编号为队列序号加1
}
//银行关门(不再执行下一个来到事件的预订)
else
{
printf("\n银行下班,不再接待新客户!");
printf("\n上一个顾客到达的时间:%d,现在也入列\n",en.OccurTime);
//顾客来到后入人数最少的队列
e.ArriveTime = en.OccurTime;//入队时间
e.Duration = durtime;//办理业务时间
i = ShortestQueue();//得到最短队列编号
EnQueue(&q[i], e);//入队操作
//如果入列后排在队头,则发生离开事件的预订
if(QueueLength(&q[i]) == 1)
OrderInsert(ev, NewEvent(en.OccurTime + durtime, i+1));//窗口编号为队列序号加1
}
return ;
}
//顾客离开事件
void CustomerDepature()
{
int i = en.NType - 1;//队列编号为窗口编号减1
//删除队列首节点
DelQueue(&q[i], &customer);
printf("\n客户离开时间:%d,事件也删除\n",en.OccurTime);//输出顾客离开时间
TotalTime += en.OccurTime - customer.ArriveTime;
//如果队列不为空,则根据新的队列首节点,来确定离开事件预订
if(!EmptyQueue(&q[i]))
{
GetHead(&q[i], &customer);
OrderInsert(ev, NewEvent(en.OccurTime + customer.Duration, i+1));//窗口编号为队列序号加1
}
return;
}
//银行排队模拟
void Bank_Simulation()
{
OpenForDay();
while(!ListEmpty(ev))//直到事件表为空
{
//删除事件表首结点,并得到首节点的数据作为判断依据
DelFirst(ev, &en);
//类型为0则表示到达事件,类型为1、2、3、4...为离开事件
if(en.NType == 0)
CustomerArrived();
else
CustomerDepature();
PrintEventList();
PrintQueue();
system("PAUSE");//用户暂停操作,用于调试
}
printf("\n客户办理总时间为: %d 分钟,客户办理平均时间: %f 分钟\n",TotalTime,(float)TotalTime/CustomerNum);
}
//获取最短队列的编号
int ShortestQueue()
{
int i,min;
int a[MAXSIZE];
min = 0;
for(i = 0; i < windows_num; i++)
{
a[i] = QueueLength(&q[i]);
}
for(i = 1; i < windows_num; i++)
{
if(a[i] < a[min])
{
min = i;
}
}
return min;
}
/*******************************显示函数*************************************/
//显示当前队列状态
void PrintQueue()
{
int i;
printf("\n队列状态:\n");
for(i = 0; i < windows_num; i++)
{
printf("队列 %d :", i+1);
QueueTraverse(&q[i]);
}
printf("\n");
}
//显示事件表状态
void PrintEventList()
{
printf("\n事件表状态:\n");
ListTraverse(ev);
}
/************事件表基本操作函数***********************************************/
//根据OccurTime和NType值,创建新事件
Event NewEvent(int occurT,int nType)
{
Event e;
e.OccurTime = occurT;
e.NType = nType;
return e;
}
//空事件表的建立
int InitList()
{
EventList L;
L = (EventList)malloc(sizeof(Event));
if(L == NULL)
{
printf("内存分配失败!\n");
exit(-1);
}
else
{
L->next=NULL;
ev = L;//得到空表的地址
}
return OK;
}
//向事件表中按时间升序插入元素
int OrderInsert(EventList L, Event e)
{
EventList p,q;
q = L;
p = L->next;
while(p && e.OccurTime > p->OccurTime)
{
q = p;
p = p->next;
}
//q指向节点的后面挂新节点
q->next = (EventList)malloc(sizeof(Event));
//对新节点赋值
q->next->OccurTime = e.OccurTime;
q->next->NType = e.NType;
q->next->next = p;
return OK;
}
//判断链表L是否为空,为空返回TRUE,否则返回FALSE
int ListEmpty(EventList L)
{
if((L->next == NULL))
return TRUE;
else
return FALSE;
}
//链表L不为空,删除其首结点,用e返回,并返回OK;否则返回ERROR
int DelFirst(EventList L, Event *memorry)
{
EventList p ;
if(ListEmpty(L) == TRUE )
{
printf("链表为空!\n");
return ERROR;
}
else
{
p = L->next;
L->next = p->next;
//保存数值
*memorry = *p;
free(p);
return OK;
}
}
//遍历链表
int ListTraverse(EventList L)
{
EventList p ;
p = L;
while(p->next)
{
p = p->next;
printf("时间:%d,类型:%d\n",p->OccurTime,p->NType);
}
printf("\n");
return OK;
}
/**********************队列相关函数******************************************/
//初始化队列Q
int InitQueue(LinkQueue *Q)
{
Q->front = Q->rear=(QElemType *)malloc(sizeof(QElemType));
if(!Q->front)
{
printf("内存分配失败!\n");
exit(-1);
}
Q->front->next=NULL;
return OK;
}
//若队列Q为空,返回TRUE,否则返回FALSE
int EmptyQueue(LinkQueue *Q)
{
if(Q->front == Q->rear)
return TRUE;
else
return FALSE;
}
//若队列Q不为空,首结点出队,用memorry返回,并返回OK;否则返回ERROR
int DelQueue(LinkQueue *Q, QElemType *memorry)
{
QElemType *p ;//节点指针
if( EmptyQueue(Q))
{
printf("队列为空,不能再进行出列操作!\n");
return ERROR;
}
else
{
p = Q->front->next;
*memorry = *p;
Q->front->next = p->next;
if(Q->rear == p)
{
Q->rear = Q->front;
}
free(p);
return OK;
}
}
//结点e入队Q
int EnQueue(LinkQueue *Q, QElemType e)
{
QElemType* p;//节点指针
p = (QElemType *)malloc(sizeof(QElemType));
if(NULL == p)
{
printf("内存分配失败!\n");
exit(-1);
}
else
{
*p = e;
p->next = NULL;
Q->rear->next = p;
Q->rear = p;
return OK;
}
}
//返回队列Q的长度,即元素个数
int QueueLength(LinkQueue *Q)
{
QElemType * p;//队列节点指针
int count ;
count = 0;
p = Q->front->next;//得到队列第一个节点的地址
while(p)
{
count++;
p = p->next;
}
return count;
}
//若队列Q不为空,用memorry返回其首结点,并返回OK,否则返回ERROR
int GetHead(LinkQueue *Q, QElemType *memorry)
{
if(EmptyQueue(Q))
return ERROR;
*memorry = *(Q->front->next);
return OK;
}
//遍历队列Q
int QueueTraverse(LinkQueue *Q)
{
QElemType *p=Q->front->next;
if(!p)
{
printf("队列为空.\n");
return ERROR;
}
while(p)
{
printf("(%d,%d)", p->ArriveTime,p->Duration);
p=p->next;
}
printf("\n");
return OK;
}