(* PAT Advanced)1026.Table Tennis (队列模拟,结构体排序) C++

原题:https://pintia.cn/problem-sets/994805342720868352/problems/994805472333250560
在这里插入图片描述

Sample Input:
9
20:52:00 10 0
08:00:00 20 0
08:02:00 30 0
20:51:00 10 0
08:10:00 5 0
08:12:00 10 1
20:50:00 10 0
08:01:30 15 1
20:53:00 10 1
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:50:00 20:50:00 0
20:51:00 20:51:00 0
20:52:00 20:52:00 0
3 3 2

题目大大意

给定K张球桌,其中M张是VIP球桌,然后给出N个前来训练的球员,球员到达后总是会选择编号最小的空桌子。如果球员训练时间大于2h,则会被压缩成2小时。

特别的,对于M张VIP桌,若VIP桌子空闲,队列内有VIP球员,且时间允许,则第一个VIP球员到编号最小的VIP球桌训练。如果VIP桌空闲,但是没有VIP球员到来,则可以分配给普通人。若VIP来了,但是没有空的VIP球桌,则被看待成普通人。

其中N个球员会给出到达的时间、训练时间、和是否是VIP,同时给出VIP球桌的编号。输出关门前得到训练的球员的到达时间、训练开始时间、和等待时长(四舍五入取分钟数)。营业时间为8:00~21.00,若21.00之前不能开始训练,则不再训练,且不输出。

题目分析

根据题目要求,我们建立两个结构体来表示球员和球桌的信息,即persontablenode,其中person需要存储到达时间arrive、开始时间start、训练时间time、和是否是会员VIPtablenode需要存储其结束的时刻end(即当前在该桌训练的球员的离开时间)、服务的球员数目num和是否是会员球桌VIP

由于输入的球员是乱序的,所以我们需要将球员按照到达时间进行排序,为了方便,将所有时间统一成秒。然后,将所有球员看成一个排队的队列,每次按照空闲球桌来安排球员进行训练,由于VIP球员的特殊性,所以我们单独设置一个索引vipindex来表示当前需要服务的VIP球员编号。大致的调度算法如下:

  • 初始化球桌的结束时间为8:00,初始化球员的开始时间为21:00 。
  • 找到当前空余的最小编号的球桌index
    • 若该桌子是VIP球桌,则判断队列中是否有VIP球员到达时间早于其结束时间(或者该VIP球员就是队首就不用提前到),即球员vipindex,若满足,则服务vipindex,同时vipindex变为下一VIP球员;若没有VIP球员满足条件,则服务队列中的第一个球员。
    • 若该桌子不是VIP球桌,则判断队列中第一个球员是不是VIP球员:
      • 若队列中第一个球员是VIP球员,则还需要判断是否有VIP球桌会在该球员到达之前空余出来,若有,则该球员应该去另外的VIP球桌;若队列中第一个不是VIP球员,则直接分配球桌。

而且由于不会有两个球员同时到来,所以可以按照球员一个一个服务来思考,即每次判断一个时间最早的球桌的调度情况。

/* 1026 Table Tennis (30 分) */
#include<iostream>
#include<vector>
#include<math.h>
#include<algorithm>

using namespace std;

const int BEGIN = 8 * 3600;
const int END = 21 * 3600;
// 球员
typedef struct {
	int arrive, start, time;
	int vip;
}person;
// 球桌
typedef struct {
	int endtime = BEGIN, num;
	int vip;
}tablenode;

vector<person> player; // 球员队列
vector<tablenode> table; // 球桌序列
int n, k, m;

int cmp1(person p1, person p2) {
	return p1.arrive < p2.arrive; // 按照到达的先后顺序
}
int cmp2(person p1, person p2) {
	return p1.start < p2.start;
}
int findNextVip(int vipid) {
	// 找到下一个等待服务的VIP球员编号
	vipid++;
	while (vipid < player.size() && player[vipid].vip == 0) vipid++;
	return vipid;
}
void printTime(int time) {
	printf("%02d:%02d:%02d ", time / 3600, time % 3600 / 60, time % 60);
}
void allocTable(int personid, int tableid) {
	// 分配tableid桌子给球员personid
	if (player[personid].arrive <= table[tableid].endtime)
		player[personid].start = table[tableid].endtime; // 早到
	else
		player[personid].start = player[personid].arrive;
	table[tableid].endtime = player[personid].start + player[personid].time;
	table[tableid].num++; // 服务人数加1
}

int main() {
	scanf("%d", &n);
	int i, j;
	int hh, mm, ss, t, tag;
	person tmpperson;
	for (i = 0; i < n; i++) {
		scanf("%d:%d:%d %d %d", &hh, &mm, &ss, &t, &tag);
		tmpperson.arrive = hh * 3600 + mm * 60 + ss;
		if (tmpperson.arrive >= END) continue;
		tmpperson.time = t <= 120 ? t * 60 : 120 * 60;
		tmpperson.start = END;
		tmpperson.vip = tag;
		player.push_back(tmpperson);
	}
	scanf("%d %d", &k, &m);
	table.resize(k+1);
	for (i = 0; i < m; i++) {
		scanf("%d", &j);
		table[j].vip = 1;
	}
	sort(player.begin(), player.end(), cmp1);
	i = 0; // 当前服务的球员
	int vipindex = -1; // 等待服务的VIP球员
	vipindex = findNextVip(vipindex);
	while (i < player.size()) {
		// 先找最早空闲的桌子
		if (player[i].vip == 1 && i < vipindex) {
			i++;
			continue; // 已经服务过的VIP球员
		}
		int index = -1, minendtime = 999999999;
		for (j = 1; j <= k; j++) {
			if (table[j].endtime < minendtime) {
				minendtime = table[j].endtime;
				index = j;
			}
		}
		if (table[index].endtime >= END) break; // 打烊了
		if (table[index].vip == 1) { // 是会员球桌
									 // 注意,这里是VIP球员比球桌先到,才能抢
									 // 或者会员在队首,时间就不用提前
			if (vipindex < player.size() && (player[vipindex].arrive <= table[index].endtime
				|| i == vipindex)) { 
				// 先服务VIP球员
				allocTable(vipindex, index);
				if (i == vipindex) i++; // 队首恰好是服务的VIP球员
				vipindex = findNextVip(vipindex);
			}
			else 
				// 服务普通球员
				allocTable(i++, index);
		}
		else { // 不是会员球桌
			if (player[i].vip == 1) {
				// 队首是VIP,则看是否会有空余的VIP球桌
				int viptableindex = -1, minvipendtime = 999999999;
				for (j = 1; j <= k; j++) {
					if (table[j].vip && table[j].endtime < minvipendtime) {
						viptableindex = j;
						minvipendtime = table[j].endtime;
					}
				}
				// 注意,这里是VIP球员比球桌后到
				// 因为此时队首已是VIP,另一个VIP桌虽然早结束,但是还没有球员抢占,所以VIP球员不用提前到
				// 同时若球桌晚于VIP球员结束,则球员不会去VIP桌子
				// 所以,需要VIP球桌早点结束,但是又没有Index早才会出现这种情况。
				if (viptableindex != -1 && minvipendtime <= player[i].arrive) { 
					// 则更换球桌
					allocTable(i, viptableindex);
					if (i == vipindex) vipindex = findNextVip(vipindex);
					i++;
				}
				else {
					allocTable(i, index);
					if (i == vipindex) vipindex = findNextVip(vipindex);
					i++;
				}
			}
			else allocTable(i++, index); // 队首不是VIP
		}
	}
	// 按照球员的开始时间进行排序
	sort(player.begin(), player.end(), cmp2);
	for (i = 0; i < player.size() && player[i].start < END; i++) {
		printTime(player[i].arrive);
		printTime(player[i].start);
		printf("%.0f\n", round((player[i].start - player[i].arrive) / 60.0)); // 往上取证,注意要是浮点数保留小数
	}
	for (i = 1; i <= k; i++) {
		printf("%d", table[i].num);
		i != k ? printf(" ") : printf("\n");
	}
	system("pause");
	return 0;
}
/*
* round()函数表示四舍五入取整,0.5向上
* ceil()表示向上取整
* floor()表示向下取整
*/

此题对于会员的判断逻辑是这几道模拟题中比较复杂的了,需要多分类,多思考,多讨论。
所以对于VIP球员的服务判断,后服务后的更新尤为重要。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值