原题: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之前不能开始训练,则不再训练,且不输出。
题目分析
根据题目要求,我们建立两个结构体来表示球员和球桌的信息,即person
和tablenode
,其中person
需要存储到达时间arrive
、开始时间start
、训练时间time
、和是否是会员VIP
,tablenode
需要存储其结束的时刻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球员,则直接分配球桌。
- 若该桌子是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球员的服务判断,后服务后的更新尤为重要。