题目链接:
https://pintia.cn/problem-sets/994805342720868352/problems/994805472333250560
题目分析:
此题类似于A1017的排队问题,但是里面存在一个vip球员,以及vip球桌的问题,需要单独处理。
需要设置球桌以及球员两个结构体。球桌,空闲时间以及服务人数,同时标记该球桌是否为vip球桌;球员,达到时间,服务开始时间,以及服务时长,同样标记该球员是否为vip球员。
为了方便统计时长以及比较时间的前后,将标准时间转换为以秒为单位的整型。
我们可以将球桌,球员分为普通以及vip两种,所以一共有四种情况:
1,有vip球桌空闲时,且此时队列中存在vip球员,则将vip球桌分配给队列中第一个vip球员。
2,有vip球桌空闲,且此时队列中一直不存在vip球员,则将vip球桌看成普通球桌。
3,无vip球桌空闲,且此时队列中存在vip球员(即队首球员为vip球员达到球馆后无vip球桌,且在存在vip球桌空闲前,先有普通球桌空闲),则将该vip球员最早空闲出来的普通球桌。
4,无vip球桌空闲,同时也无vip球员,则按顺序给队首球员分配球桌。
参考代码:
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N = 110;
const int inf = 1e9;
struct player{
int arrivetime, startime, servetime;
bool isvip;
}newplayer;
struct table{
int endtime, servenum;
bool isvip;
}table[N];
vector<player> ply;
bool cmp1(player a, player b){ //按到达时间排序
return a.arrivetime < b.arrivetime;
}
bool cmp2(player a, player b){ //按开始时间排序
return a.startime < b.startime;
}
int convert(int h, int m, int s){
return h*3600 + m*60 + s;
}
int nextVipplayer(int idx){ //从当前VIP球员移至下一个VIP球员
idx++;
while(idx < ply.size() && ply[idx].isvip == 0) idx++;
return idx;
}
void allotable(int pid, int tid){ //将编号为pid的球员分配到编号为tid的球桌
if(ply[pid].arrivetime <= table[tid].endtime){ //更新ply[pid]的开始时间。
ply[pid].startime = table[tid].endtime;
}else{
ply[pid].startime = ply[pid].arrivetime;
}
//更新球桌的结束时间为该球员的使用结束时间。
table[tid].endtime = ply[pid].startime + ply[pid].servetime;
table[tid].servenum++;
}
int n, m, k, viptable; //n表示用户数,
int main()
{
scanf("%d", &n); //球员对数
int sttime = convert(8, 0, 0); //开门时间
int edtime = convert(21, 0, 0); //闭馆时间
for(int i = 0; i < n; i++){
int h, m, s, p, tag;
scanf("%d:%d:%d %d %d",&h, &m, &s, &p, &tag);
newplayer.arrivetime = convert(h, m, s);
newplayer.startime = edtime; //开始时间初始化21点。
if(newplayer.arrivetime >= edtime) continue;
newplayer.servetime = p <= 120 ? p*60: 7200;
newplayer.isvip = tag;
ply.push_back(newplayer);
}
scanf("%d %d", &k, &m); //球桌数,VIP球桌数。
for(int i = 1; i <= k; i++){ //对球桌进行初始化。
table[i].endtime = sttime;
table[i].servenum = table[i].isvip = 0;
}
for(int i = 0; i < m; i++){
scanf("%d", &viptable);
table[viptable].isvip = 1;
}
sort(ply.begin(), ply.end(), cmp1); //球员到达时间排序
int i = 0, vip = -1;
vip = nextVipplayer(vip); //队列中第一个VIP球员
while(i < ply.size()){ //当前队列最前面的球员为i.
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(ply[i].isvip == 1 && i < vip){ //如果i号是VIP球员,但是VIP > i,说明i号球员已经在训练
i++; continue;
}
if(table[idx].isvip == 1){
if(ply[i].isvip == 1){ //球桌idx,球员i均为vip
allotable(i, idx); //将球桌idx分配给球员i。
if(vip == i) vip = nextVipplayer(vip);
i++;
}else{
if(vip < ply.size() && ply[vip].arrivetime <= table[idx].endtime){//当VIP球桌空闲时,队列中存在VIP用户,则把idx分配给他
allotable(vip, idx);
vip = nextVipplayer(vip);
}else{ //当VIP球桌空闲时,队列中无VIP用户
allotable(i, idx); i++;
}
}
}else{
if(ply[i].isvip == 0){ //若最早空闲的不是VIP球桌,且队员不是VIP队员,则把球桌idx分配给i。
allotable(i, idx);
i++;
}else{
//球桌不是VIP,球员是VIP
int vipidx = -1, minvipendtime = inf; //vipidx表示最早空闲的vip球桌编号
for(int j = 1; j <= k; j++){
if(table[j].isvip == 1 && table[j].endtime < minvipendtime){
minvipendtime = table[j].endtime;
vipidx = j;
}
}
if(vipidx != -1 && table[vipidx].endtime <= ply[i].arrivetime){ //i用户于vipidx球桌空闲后到达。
allotable(i, vipidx);
if(vip == i) vip = nextVipplayer(vip);
i++;
}else{
//如果球员来时vip球桌还未空闲,则将idx分配给i
allotable(i, idx);
if(vip == i) vip = nextVipplayer(vip);
i++;
}
}
}
}
sort(ply.begin(), ply.end(), cmp2);
for(i = 0; i < ply.size() && ply[i].startime <edtime; i++){
int t1 = ply[i].arrivetime;
int t2 = ply[i].startime;
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(i = 1; i <= k; i++){
printf("%d", table[i].servenum);
if(i != k) printf(" ");
}
return 0;
}