【数据结构算法暑期实习】PTA Table Tennis(两个队列的排队问题)

本文介绍了一个模拟乒乓球俱乐部服务流程的问题解决思路与实现方法。玩家在不同时间到达,系统需合理分配桌子并计算等待时间及每桌服务人数。

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

一、题目

A table tennis club has N tables available to the public. The tables are numbered from 1 to N. For any pair of players, if there are some tables open when they arrive, they will be assigned to the available table with the smallest number. If all the tables are occupied, they will have to wait in a queue. It is assumed that every pair of players can play for at most 2 hours.

Your job is to count for everyone in queue their waiting time, and for each table the number of players it has served for the day.

One thing that makes this procedure a bit complicated is that the club reserves some tables for their VIP members. When a VIP table is open, the first VIP pair in the queue will have the priviledge to take it. However, if there is no VIP in the queue, the next pair of players can take it. On the other hand, if when it is the turn of a VIP pair, yet no VIP table is available, they can be assigned as any ordinary players.

Input Specification:

Each input file contains one test case. For each case, the first line contains an integer N (≤10000) - the total number of pairs of players. Then N lines follow, each contains 2 times and a VIP tag: HH:MM:SS - the arriving time, P - the playing time in minutes of a pair of players, and tag - which is 1 if they hold a VIP card, or 0 if not. It is guaranteed that the arriving time is between 08:00:00 and 21:00:00 while the club is open. It is assumed that no two customers arrives at the same time. Following the players’ info, there are 2 positive integers: K (≤100) - the number of tables, and M (< K) - the number of VIP tables. The last line contains M table numbers.

Output Specification:
For each test case, first print the arriving time, serving time and the waiting time for each pair of players in the format shown by the sample. Then print in a line the number of players served by each table. Notice that the output must be listed in chronological order of the serving time. The waiting time must be rounded up to an integer minute(s). If one cannot get a table before the closing time, their information must NOT be printed.

Sample Input:

10
20:52:00 10 0
08:00:00 20 0
08:02:00 30 0
20:51:00 10 0
08:10:00 30 0
08:12:00 10 1
20:40:00 13 0
08:01:30 15 1
20:53:00 10 1
20:54:00 10 0
3 1
2

Sample Output:

08:00:00 08:00:00 0
08:01:30 08:01:30 0
08:02:00 08:02:00 0
08:12:00 08:16:30 5
08:10:00 08:20:00 10
20:40:00 20:40:00 0
20:51:00 20:51:00 0
20:52:00 20:52:00 0
20:53:00 20:53:00 0
4 3 2

二、思路

1.题目大意

整体上就是一个乒乓球俱乐部给玩家提供服务的流程。
玩家在不同的时间点到来,俱乐部负责给他们安排桌子。如果玩家到达时有一些牌桌空着,玩家将被分配到编号最小的可用牌桌。如果所有的桌子都被占用,他们将不得不排队等候。俱乐部为VIP会员保留了一些桌子。当 VIP牌桌空闲时,队列中的第一个VIP玩家组将有权使用它。但若队列中没有VIP玩家,普通玩家也可以使用该VIP牌桌。同理,如果轮到 VIP 玩家组时没有VIP牌桌但有普通牌桌可用,则他们也可使用普通牌桌。
题目需要求解的就是计算排队的每组玩家的等待时间,以及每张桌子当天服务的玩家人数。

2.题目分析

可以比较明确,这道题就是一个排队的问题,需要用到队列。比较复杂之处在于需要排两个队:所有人都排普通队,VIP玩家再排一个VIP队。两个队同时更新.
(1)输入信息并不是按照玩家先来后到的时间顺序给的,而是打乱顺序输入。因此排队前应该先按照到达时间对玩家进行排序。排好序再入队。
(2)两个主体,玩家和牌桌,每个主体都有多个属性。因此很明显最好使用结构体。
(3)不能使用stl,那么排序函数、队列的结构定义和各种函数需要自己定义和编写。

3.关键思路整理

1.结构体定义
玩家结构体包括属性:到来时间、玩的时间、得到服务的时间、离开时间、是否为vip玩家;牌桌结构体包括属性:牌桌服务次数、当前服务状态、是否为vip牌桌。
实例化结构体数组p[i]、t[i]存储相应信息。

2.输入并记录玩家和牌桌信息。
时间信息在存储时统一按秒制,方面后续计算。

3.对输入信息进行排序。
按照玩家先来后到的顺序,使用自定义的堆排序。

4.建立两个队。
普通队q1、VIP队q2.

5.在营业时间内,做如下分析:
(1)首先清场送客。
遍历所有桌子,对于正在服务的桌子,看所服务玩家的离开时间是否等于当前时间。如果等于,则送客,当前桌子的服务状态修改为-1(空闲)。
(2)新玩家排队。
所有玩家按照到来时间的顺序入队。所有玩家都入q1队,vip玩家再入q2队。
(3)vip队伍服务。
请添加图片描述

(4)普通队伍服务。

请添加图片描述

(5)输出
按玩家得到服务的时间顺序输出玩家信息(按顺序保存在动态数组v中),注意输出时要将秒制再转化为时钟制;输出每张桌子的服务次数。

三、代码

堆排序头文件:HeapSort.h

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
using namespace std;

/* 玩家对结构体定义*/
struct playerPair {
	int comeTime;//到来时间
	int playTime;//打牌持续的时间
	int serveTime = 0;//得到服务的时间,初始化为0
	int leaveTime;//离开时间
	int isVIP = 0;//记录是否为vip,初始化为0

};


/* 交换函数 */
void Swap(playerPair* x, playerPair* y)
{
	playerPair tmp = *x;
	*x =*y;
	*y = tmp;
}

/* 堆排序中的一次调整 */
void OneAdjust(playerPair* arr, int len, int root)
{
	int parent = root;
	int left = 2 * root + 1;
	int right = 2 * root + 2;
	if (left<len && arr[left].comeTime > arr[parent].comeTime) {
		parent = left;
	}
	if (right<len && arr[right].comeTime > arr[parent].comeTime) {
		parent = right;
	}
	if (parent!=root) {
		Swap(&arr[parent], &arr[root]);
		OneAdjust(arr,len,parent);
	}
	
}


/* 堆排序 */
void HeapSort(playerPair* arr, int len)
{
	for (int i = len / 2 - 1; i >= 0; i--) {
		OneAdjust(arr,len,i);
	}

	for (int i = len - 1; i >= 0;i--) {
		Swap(&arr[0],&arr[i]);
		OneAdjust(arr, i, 0);
	}
}

队列头文件:Queue.h

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define maxsize 10001


/* 队列结构定义(顺序存储)*/
typedef int ElementType;
typedef int position;
typedef struct QNode* PtrToQNode;
struct QNode {
	ElementType *data;
	position rear;
	position front;
	int Maxsize;
};
typedef PtrToQNode Queue;


/* 创建空队列 */
Queue CreateQueue(int Maxsize)
{
	Queue Q;
	/*申请获得队列空间*/
	Q = (Queue)malloc(sizeof(struct QNode));
	Q->data = (ElementType*)malloc(Maxsize* sizeof(ElementType));
	Q->front = Q->rear = 0; 
	Q->Maxsize = Maxsize;
	return Q;
}

/* 判断队列是否为空 */
bool IsEmptyQ(Queue Q)
{
	return Q->front == Q->rear;
}


/* 判断队列是否为满 */
bool IsFullQ(Queue Q)
{
	return (Q->rear + 1) % Q->Maxsize == Q->front;
}

/* 队列长度 */
int QueueSize(Queue Q)
{
	return (Q->rear - Q->front + Q->Maxsize) % Q->Maxsize;
}


/* 入队 */
bool pushQ(Queue Q, ElementType X)
{
	if (IsFullQ(Q)) { 
		printf("队列满");    
		return false; 
	}
	else {
		Q->rear = (Q->rear + 1) % Q->Maxsize;
		Q->data[Q->rear] = X;
		return true;
	}
}


/* 出队 */
bool popQ(Queue Q)
{
	if (IsEmptyQ(Q)) { 
		printf("队列空");    
		return false; 
	}
	else {
		Q->front = (Q->front + 1) % Q->Maxsize;
		return true;
	}
}

/*返回队首元素*/
int FrontQ(Queue Q) {
	return Q->data[Q->front];
}

主程序源文件:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <vector>
#include <iostream>
#include "Quene.h"
#include "HeapSort.h"
#define maxsize1 10001
#define maxsize2 101
using namespace std;


/* 牌桌结构体定义 */
struct Table {
	int serve_cnts=0;//记录牌桌的服务次数
	int serve = -1;//记录牌桌的服务状态:若当前未服务(空闲),则记为-1;若当前正在服务,则其值等于所服务的玩家的序号。
	int isVIP = 0;//先默认牌桌均为普通牌桌
};



/* 时间按照时钟制输出 */
void printTime(int time) {
	int h = time / 3600;
	int m = time % 3600 / 60;
	int s = time % 60;
	printf("%02d:%02d:%02d",h,m,s);
}


playerPair p[maxsize1];
Table t[maxsize2];
vector<int> v;


int main() {

	int N, K, M;//玩家人数、牌桌总数、VIP桌数


	/* 输入并记录玩家信息 */
	scanf_s("%d", &N);
	for (int i = 0; i < N; i++) {
		int h, m, s;
		char c;
		scanf_s("%d%c%d%c%d", &h, &c,1, &m, &c,1, &s);
		scanf_s("%d", &p[i].playTime);
		scanf_s("%d", &p[i].isVIP);
		//时间信息在存储时统一按秒制,方面后续计算
		p[i].comeTime = h * 3600 + m * 60 + s;
		p[i].playTime *= 60;
		if (p[i].playTime > 7200) {
			p[i].playTime = 7200;
		}
	}



	/* 输入并记录牌桌信息 */
	scanf_s("%d %d", &K, &M);
	while (M--) {
		int j;
		scanf_s("%d", &j);
		t[j].isVIP = 1;
	}


	/* 按照玩家先来后到的顺序,对输入信息进行排序 */
	HeapSort(p, N);


	/* 新来的玩家先排队。排两个队,所有人都排普通队,vip玩家单独排一个vip队 */
	Queue q1 = CreateQueue(maxsize1);
	Queue q2 = CreateQueue(maxsize1);
	int cursor = 0;


	/* 在营业时间内做如下分析 */
	for (int Time = 28800; Time < 75600; Time++) {

		/* 1.首先清场送客*/
		for (int i = 1; i <= K; i++) {
			if (t[i].serve >= 0) {
				int j = t[i].serve;
				if (p[j].leaveTime == Time) t[i].serve = -1;
			}
		}


		/* 2.开始入队 */
		while (p[cursor].comeTime == Time && cursor < N) {
			pushQ(q1,cursor);
			if (p[cursor].isVIP == 1) pushQ(q2,cursor);
			cursor++;
		}

		/* 3.vip队伍服务中 */
		while (QueueSize(q2) && p[FrontQ(q2)].serveTime != 0) {
			popQ(q2);
		}
		for (int i = 1; i <= K; i++) {//判断是不是vip,如果是,服务为该顾客的号码,并且更新顾客的离开时间和桌子的服务次数
			if (t[i].isVIP != 1) {
				continue;
			}
			if (t[i].serve == -1) {//桌子没在服务状态且q2队列有人
				if (QueueSize(q2)) {
					int j = FrontQ(q2);
					v.emplace_back(j);//记录服务了的号码
					t[i].serve = j;
					p[j].serveTime = Time;//排到队伍的时间就是开始服务的时间
					p[j].leaveTime = p[j].serveTime + p[j].playTime;//开始服务的时间加玩的时间就是离开时间
					t[i].serve_cnts++;//桌子服务次数+1
					while (QueueSize(q2) && p[FrontQ(q2)].serveTime != 0) {
						popQ(q2);
					}
				}
			}
		}


		/* 4.普通队伍服务中 */
		while (QueueSize(q1) && p[FrontQ(q1)].serveTime != 0) {
			popQ(q1);
		}
		for (int i = 1; i <= K; i++) {//判断桌子是不是服务状态 如果不是,服务状态记录为该顾客的号码,并更新顾客的离开时间和桌子的服务次数
			if (t[i].serve == -1 && QueueSize(q1)) {
				int j = FrontQ(q1);
				t[i].serve = j;
				v.emplace_back(j);//记录服务了的号码
				p[j].serveTime = Time;//排到队伍的时间就是开始服务的时间
				p[j].leaveTime = p[j].serveTime + p[j].playTime;//开始服务的时间加玩的时间就是离开时间
				t[i].serve_cnts++;//桌子服务次数+1
				while (QueueSize(q1) && p[FrontQ(q1)].serveTime != 0) {
					popQ(q1);
				}
			}

		}
	}


	/* 输出 */
	for (int each : v) {
		printTime(p[each].comeTime);
		printf(" ");
		printTime(p[each].serveTime);
		printf(" ");
		printf("%d", (p[each].serveTime - p[each].comeTime + 30) / 60);
		printf("\n");

	}

	for (int c = 1; c <= K; c++) {
		if (c != 1) {
			printf(" ");
		}
		printf("%d", t[c].serve_cnts);
	}

	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值