pat甲级A1026 Table Tennis (30分)

题目链接:

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;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值