[PAT-A 1026]Table Tennis

在这里插入图片描述
在这里插入图片描述
题目大意:
有K张乒乓球桌,编号为1-K,从8:00:00-21:00:00开放,每对球员到达的时间总选择当前编号最小的球桌进行训练,且训练时长超过两小时会被压缩成两小时,如果到达的时候没有球桌空闲,则拍成队列等待。
K张球桌中有M张是VIP球桌。
1.如果存在VIP球桌空闲,且等待队列中存在VIP球员,那么等待队列中第一个VIP球员将前往编号最小的VIP球桌进行训练。
2.如果存在VIP球桌空闲,且等待队列中没有VIP球员,那么VIP球桌将被分配给等待队列中的第一个普通球员。
3.如果当前没有VIP球桌空闲,那么VIP球员将会被当做普通球员处理。
现在给出每个球员的到达时间,训练时长,是否是VIP球员以及给出球桌数以及VIP球桌编号。
求所有在关门之前得到训练的球员的到达时间,训练开始时间,等待时间,以及所有球桌当天服务人数。
所有在21:00:00之后(含)还没有得到训练的球员将不再训练,且不需要输出。

思路:
1.对球员与球桌分别建立结构体:
球员中包括:到达时间,开始时间,训练时间(服务时长),是否是VIP球员。
球桌中包括:当前占用该球桌的结束时间,服务人数,是否是VIP球桌。

2.球员到达的顺序是无序的,因此不能直接定义队列,需要使用vector存放读入的球员信息,然后按照到达的时间进行排序。定义Table数组,用来存放所有球桌的信息。

3.按照队列中的球员顺序来分配球桌,因此需要对每个待分配的队首球员,求当前最早结束训练的球桌编号idx,可以根据最早空闲的球桌是idx是否是VIP进行分类:
1)如果最早空闲的球桌idx是VIP球桌,且队首球员是VIP球员,那么把球桌idx分配给他
2)如果最早空闲的球桌idx是VIP球桌,且队首球员不是VIP球员,那么查看第一个VIP球员是否可以在球桌idx空闲之前到达,如果是,把球桌idx分配给VIP球员(让其插队),否则,还是把球桌分配给队首球员。
3)如果最早空闲的球桌idx不是VIP球桌,且队首球员不是VIP球员,那么把球桌idx分配给他。
4)如果最早空闲的球桌idx不是VIP球桌,且队首球员是VIP球员,那么查询最早空闲的VIP球桌VIPidx,看其是否在该VIP球员到达之前空闲,如果是,就把球桌VIPidx分配给该球员。

4.为了实现上面的过程,可以设置下标i与VIPi,分别指向当前队首球员,和当前队首VIP球员。之后每次把球桌分配给VIP球员时,就需要将VIPi指向下一个VIP球员。(由于存在VIP球员插队的情况,导致i<VIPi出现,当下次i扫描到VIPi时,就需要跳过这个VIP球员,如果当前队首i是VIP球员,且满足i<VIPi,直接令i++,跳过当前分配过程。)

5.如果存在空闲的VIP球桌,则VIP球员总是选择编号最小的空闲的VIP球桌而不是最小的空闲普通球桌,如果不存在空闲的VIP球桌,则VIP球员等同于普通球员。

6.每个球员必须在2h内结束训练,超过2h的压缩为2h。

7.等待时长应该严格四舍五入取整数,即30s应该进位至1min。round函数,直接%.0f会报错。

8.可能有不在21:00:00前到达的球员,一律不予考虑。21:00:00没有开始的训练不再训练,而不是21:00:01。

9.分配球桌时要根据球桌空闲时间和球员到达的时间先后来选择该球桌的下一次服务结束时间。

10.在最早空闲的球桌idx时VIP球桌,且队首球员不是VIP球员的情况下,如果第一个VIP球员VIPi比球桌空闲时间来得早,那么将把球桌分配分配给VIPi,这种情况中队首球员是不变的,并没有i++来改变队首球员位置。

11.VIPi不能从0开始,必须在一开始就指向第一个VIP球员。

12.时间单位换算成s为单位,以便进行时间的比较与计算,输出时按照球员开始训练的时间从前往后输出,而不是到达时间。

//input
5 
08:00:00 60 0
08:10:00 30 0
08:20:00 10 0
08:30:00 10 1
08:40:00 10 1
2 1 
1
//output
08:00:00 08:00:00 0
08:10:00 08:10:00 0
08:20:00 08:40:00 20
08:30:00 08:50:00 20
08:40:00 09:00:00 20
2 3

//input
2
20:00:00 60 0
20:30:00 10 1
1 1
1
//output
20:00:00 20:00:00 0
1

//input
2
21:00:00 10 1
21:01:00 10 1
3 3 
1 2 3
//output
0 0 0

//input
2
08:00:00 130 0
09:00:00 10 0
1 0
//output
08:00:00 08:00:00 0
09:00:00 10:00:00 60
2

AC代码:

//PAT_A 1026
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int k = 111;
const int inf = 0x3fffffff;
struct Player{
	int arriveTime, startTime, trainTime;//到达时间,训练开始时间,训练时长
	bool isVip;//是否Vip
}newPlayer;
struct Table {
	int endTime, numSever;//结束时间 服务人数
	bool isVip;//是否时Vip球员
}table[k];
vector<Player> player;
int convertTime(int h, int m, int s) {
	return h * 3600 + m * 60 + s;
}
bool cmpArriveTime(Player a, Player b) {//按照先来后到排序
	return a.arriveTime < b.arriveTime;
}
bool cmpStartTime(Player a, Player b) {//按到达时间排序
	return a.startTime < b.startTime;
}
int nextVipPlayer(int VIPi) {
	VIPi++;
	while (VIPi < player.size() && player[VIPi].isVip == 0)VIPi++;
	return VIPi;
}
void allotTable(int pId, int tId) {//将编号为tId的桌子分配给pId的选手
	if (player[pId].arriveTime <= table[tId].endTime) {
		player[pId].startTime = table[tId].endTime;
	}
	else player[pId].startTime = player[pId].arriveTime;
	table[tId].endTime = player[pId].startTime + player[pId].trainTime;
	table[tId].numSever++;
}
int main() {
	int n, k, m, VIPtable;
	(void)scanf("%d", &n);
	int stTime = convertTime(8, 0, 0);
	int edTime = convertTime(21, 0, 0);
	for (int i = 0; i < n; i++) {
		int hh, mm, ss, trainTime, isVip;
		(void)scanf("%d:%d:%d %d %d", &hh, &mm, &ss, &trainTime, &isVip);
		newPlayer.arriveTime = convertTime(hh, mm, ss);
		newPlayer.startTime = edTime;//初始化
		if (newPlayer.arriveTime >= edTime)continue;
		if (trainTime >= 120)newPlayer.trainTime = 7200;
		else newPlayer.trainTime = trainTime * 60;
		newPlayer.isVip = isVip;
		player.push_back(newPlayer);
	}
	(void)scanf("%d %d", &k, &m);
	for (int i = 1; i <= k; i++) {
		table[i].endTime = stTime;
		table[i].numSever = table[i].isVip = 0;//初始化
	}
	for (int i = 0; i < m; i++) {
		(void)scanf("%d", &VIPtable);
		table[VIPtable].isVip = 1;
	}
	sort(player.begin(), player.end(), cmpArriveTime);
	int i = 0, vipi = -1;
	vipi = nextVipPlayer(vipi);
	while (i < player.size()) {
		int idx = -1, minEndTime = inf;
		for (int j = 1; j <= k; j++) {
			if (table[j].endTime < minEndTime) {
				minEndTime = table[j].endTime;
				idx = j;
			}
		}
		if (table[idx].endTime >= edTime)break;//关门
		if (player[i].isVip == 1 && i < vipi) {//说明当前i号球员时vip球员且已经在训练
			i++;
			continue;
		}
		if (table[idx].isVip == 1) {
			if (player[i].isVip == 1) {
				//3-1 如果最早空闲的球桌idx是VIP球桌,且队首球员是VIP球员,那么把球桌idx分配给他
				allotTable(i, idx);
				if (vipi == i)vipi = nextVipPlayer(vipi);
				i++;
			}
			else {
				//3-2 如果最早空闲的球桌idx是VIP球桌,且队首球员不是VIP球员,那么查看第一个VIP球员是否可以在球桌idx空闲之前到达,如果是,把球桌idx分配给VIP球员(让其插队),否则,还是把球桌分配给队首球员。
				if (vipi < player.size() && player[vipi].arriveTime <= table[idx].endTime) {//注意这里没有i++
					allotTable(vipi, idx);
					vipi = nextVipPlayer(vipi);
				}
				else {
					allotTable(i, idx);
					i++;
				}
			}
		}
		else {
			if (player[i].isVip == 0) {
				//3-3 如果最早空闲的球桌idx不是VIP球桌,且队首球员不是VIP球员,那么把球桌idx分配给他。
				allotTable(i, idx);
				i++;
			}
			else {
				//3-4 如果最早空闲的球桌idx不是VIP球桌,且队首球员是VIP球员,那么查询最早空闲的VIP球桌VIPidx,看其是否在该VIP球员到达之前空闲,如果是,就把球桌VIPidx分配给该球员。
				int VipIdx = -1, minVipEndTime = inf;
				for (int j = 1; j <= k; j++) {
					if (table[j].isVip == 1 && table[j].endTime < minVipEndTime) {
						minEndTime = table[j].endTime;
						VipIdx = j;
					}
				}
				if (VipIdx != -1 && player[i].arriveTime >= table[VipIdx].endTime) {
					//vip球桌存在,空闲时间比队员来得早 相当于这个桌子被预约了
					allotTable(i, VipIdx);
					if (vipi == i)vipi = nextVipPlayer(vipi);
					i++;
				}
				else {
					//VIP球员到,VIP球桌还没有空闲,或VIP球桌不存在
					allotTable(i, idx);
					if (vipi == i)vipi = nextVipPlayer(vipi);
					i++;

				}
			}
		}
	}
	sort(player.begin(), player.end(), cmpStartTime);//按开始训练时间排序输出
	for (i = 0; i < player.size() && player[i].startTime < edTime; i++) {
		int t1 = player[i].arriveTime;
		int t2 = player[i].startTime;
		printf("%02d:%02d:%02d ", t1 / 3600, t1 % 3600 / 60, t1 % 60);
		printf("%02d:%02d:%02d ", t2 / 3600, t2 % 3600 / 60, t2 % 60);
		printf("%.0f\n", round((t2 - t1) / 60.0));
	}
	for (int i = 1; i <= k; i++) {
		printf("%d", table[i].numSever);
		if (i < k)printf(" ");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值