PAT-A1026 Table Tennis 题目内容及题解

A table tennis club has N tables available to the public. The tables are numbered from 1 to N. For any pair of players, if there are some tables open when they arrive, they will be assigned to the available table with the smallest number. If all the tables are occupied, they will have to wait in a queue. It is assumed that every pair of players can play for at most 2 hours.

Your job is to count for everyone in queue their waiting time, and for each table the number of players it has served for the day.

One thing that makes this procedure a bit complicated is that the club reserves some tables for their VIP members. When a VIP table is open, the first VIP pair in the queue will have the priviledge to take it. However, if there is no VIP in the queue, the next pair of players can take it. On the other hand, if when it is the turn of a VIP pair, yet no VIP table is available, they can be assigned as any ordinary players.

Input Specification:

Each input file contains one test case. For each case, the first line contains an integer N (≤10000) - the total number of pairs of players. Then N lines follow, each contains 2 times and a VIP tag: HH:MM:SS - the arriving time, P - the playing time in minutes of a pair of players, and tag - which is 1 if they hold a VIP card, or 0 if not. It is guaranteed that the arriving time is between 08:00:00 and 21:00:00 while the club is open. It is assumed that no two customers arrives at the same time. Following the players' info, there are 2 positive integers: K (≤100) - the number of tables, and M (< K) - the number of VIP tables. The last line contains M table numbers.

Output Specification:

For each test case, first print the arriving time, serving time and the waiting time for each pair of players in the format shown by the sample. Then print in a line the number of players served by each table. Notice that the output must be listed in chronological order of the serving time. The waiting time must be rounded up to an integer minute(s). If one cannot get a table before the closing time, their information must NOT be printed.

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

题目大意

一个复杂的排队模拟程序。题目给定俱乐部的桌子数(VIP桌和普通桌)和顾客到达的时间、游戏分钟数以及顾客属性(是否VIP),题目要求输出每名顾客的到达时间、获得服务的时间和等待时间,并输出每张桌子服务的客户人数。

规则如下:

  • 每对玩家到来后分配到序号最小的可用桌子,并且最多占用2小时;
  • 如桌子全满则玩家需在队列中等待;
  • 如有VIP桌子空出,则等待中的VIP用户有权直接占用此桌;
  • 如果此时有VIP桌空出,但队列中无VIP用户准备,则下一对玩家可以使用此桌;
  • 如果轮到排队的VIP玩家,但是空出的桌子不是VIP,也可以直接占用;
  • 等待时间需要四舍五入;
  • 如用户在关门时间前不能获得桌子则不计入总时间。

解题思路

本道快乐模拟模拟过程相当“快乐”,也是截止到目前(2019.2.1)为止我认为的甲级最复杂的题目之一。但是如果适当分解任务可以极大地简化思路,从而得到正确的结果。

  1. 建立结构体分别存放玩家与球桌的信息;
  2. 预处理读入信息,将玩家按照到来时间进行排队,并标记其中的VIP用户,同时初始化球桌信息,将VIP球桌与普通球桌分别归队;
  3. 将入座过程模块化,内容包括更新玩家信息以及球桌信息等;
  4. 开始模拟选座过程:每叫到一名顾客,如果已被服务过(VIP用户有可能插队),叫下一个;如为vip用户,则先查找空闲的vip桌,如果没有,就查找空闲的普桌,如依然没有,查找最小等待时间的桌子并加入;如为普客,查找最小的空闲桌,加入,如当前没有空闲桌则查找等待时间最短的桌,如为普桌,直接加入,如为vip桌,查看是否有vip排队,如有则请vip使用,并从1开始重复当前过程。
  5. 输出结果并按格式输出,返回0值。

代码

#include<cstdio>
#include<algorithm>
#define maxn 10010
#define INF 100000000
#define Begin_time 28800 //大于等于 
#define End_time 75600//小于 
using namespace std;

struct Player{
    int arrive_time;//s到达时间 
    int seriving_time;//s,接收服务时间
    int wait_time;//s,等待时间 
    int service_time;//s,服务时间 
    int Player_type;//1vip,0普通 
    int served;//0未服务,1服务过 
}player[maxn];

struct Table{
    int serve_num;//服务数量
    int end_time;//当前服务结束时间 
    int table_type;//1vip,0普通 
}table[110];

int TQueuevip[110];//记录vip桌号 
int TQueueord[110];//记录普桌号
int vtnum=0,otnum=0;

int Vplayer[maxn];//vip编号 
int vipnum=0;//vip数量
int pfront=0,vfront=0;//当前队列进行情况,首客,首个vip 

int current_time;//记录当前时间 

int N,K,M;//玩家数,桌子数,vip桌数 

bool cmp1(Player a,Player b){
    return a.arrive_time<b.arrive_time;
}

bool cmp2(Player a,Player b){
    if(a.served!=b.served){
        return a.served>b.served;
    }else{
        return a.seriving_time<b.seriving_time;
    }
}

void Init(){
    int i;
    int h,m,s,st,vipno;
    scanf("%d",&N);
    for(i=0;i<N;i++){
        scanf("%d:%d:%d %d %d",&h,&m,&s,&st,&player[i].Player_type);
        player[i].arrive_time=h*3600+m*60+s;
        player[i].served=0;
        player[i].service_time=(st>120?120:st)*60;
    }
    sort(player,player+N,cmp1);
    for(i=0;i<N;i++){
        if(player[i].Player_type==1){
            Vplayer[vipnum++]=i;
        }//记录vip位置 
    }
    scanf("%d%d",&K,&M);
    for(i=0;i<M;i++){
        scanf("%d",&vipno);
        table[vipno].table_type=1;
    }
    for(i=1;i<=K;i++){
        table[i].end_time=Begin_time;
        table[i].serve_num=0;
        if(table[i].table_type==1){
            TQueuevip[vtnum++]=i;//插入vip桌 
        }else{
            TQueueord[otnum++]=i;//插入普桌 
        }
    }
}


void Sit_down(int cus,int no){//第cus号球员入座no号桌子 
    if(player[cus].arrive_time>=table[no].end_time){
        player[cus].seriving_time=player[cus].arrive_time;//来时立即服务
    }else{
        player[cus].seriving_time=table[no].end_time;//等待后加入 
    }
    current_time=player[cus].seriving_time;//更新当前时间 
    if(current_time>=End_time){
        return;//如到达服务时到达关店时间,不执行后续活动 
    }
    //更新球员情况 
    player[cus].wait_time=player[cus].seriving_time-player[cus].arrive_time;
    //等待时间
    player[cus].served=1;//服务已接受过
    //更新桌子情况
    table[no].end_time=player[cus].seriving_time+player[cus].service_time;
    table[no].serve_num++;
    return;
} 
void Find_TtoV(int cus){//给cus号vip找桌子 
    int no=-1,min=INF;
    int i;
    pfront++;
    vfront++;//vip队列位置移动
    //找空vip 
    for(i=0;i<vtnum;i++){
        no=TQueuevip[i];
        if(table[no].end_time<=player[cus].arrive_time){
            //来得巧,有空vip桌
            Sit_down(cus,no);//该顾客坐在no号位置 
            return;
        } 
    }
    //找空普桌
    for(i=0;i<otnum;i++){
        no=TQueueord[i];
        if(table[no].end_time<=player[cus].arrive_time){
            //来得巧,有空普桌
            Sit_down(cus,no);//该顾客坐在no号位置 
            return;
        }
    }
    //找最小号桌 
    for(i=1;i<=K;i++){
        if(table[i].end_time<min){
            no=i;
            min=table[no].end_time;
        }
    }//找最小桌
    Sit_down(cus,no);//该顾客坐在no号位置
    return; 
}

void Find_TtoO(int cus){//给cus号普客找桌子 
    int i;
    int no,min,vipID;
    //找空位,它排第一位,如找到空位立即入座,队中定无等待中vip 
    pfront++;
    for(i=1;i<=K;i++){
        if(table[i].end_time<=player[cus].arrive_time){
            Sit_down(cus,i);//该顾客坐在i号位置
            return;
        }
    }
    while(1){
        no=-1,min=INF;
        for(i=1;i<=K;i++){
            if(table[i].end_time<min){
                no=i;
                min=table[no].end_time;
            }
        }//找最小桌
        if(table[no].table_type==1){//找到vip桌 
            if(vfront<vipnum){//仍有vip 
                vipID=Vplayer[vfront];//排在最前的vip 
                if(table[no].end_time>player[vipID].arrive_time){//有vip排队 
                    Sit_down(vipID,no);//队首VIP入座
                    vfront++;
                    continue;//继续循环 
                } 
            }    
        }
        Sit_down(cus,no);//该顾客坐在no号位置
        break; 
    }
    return;
}

void Print(){
    int i;
    int arr,ser,wai;
    int h1,m1,s1,h2,m2,s2;
    sort(player,player+N,cmp2);
    for(i=0;i<N;i++){
        if(player[i].served==0){
            break;
        }//未服务过
        arr=player[i].arrive_time;
        ser=player[i].seriving_time;
        wai=player[i].wait_time;
        h1=arr/3600;
        h2=ser/3600;
        m1=(arr%3600)/60;
        m2=(ser%3600)/60;
        s1=arr%60;
        s2=ser%60;
        printf("%02d:%02d:%02d %02d:%02d:%02d %d\n",h1,m1,s1,h2,m2,s2,(wai+30)/60);
    }
    for(i=1;i<=K;i++){
        printf("%d",table[i].serve_num);
        if(i==K){
            printf("\n");
        }else{
            printf(" ");
        }
    }
    return;
}

int main(){
    Init();
    while(pfront<N){
        if(player[pfront].served==1){
            pfront++;
            continue;//读到已入座的vip 
        }
        if(player[pfront].Player_type==1){//排头pfront 
            Find_TtoV(pfront);//读入排头 
        }else{
            Find_TtoO(pfront);//读入排头 
        }
        if(current_time>=End_time){
            break;
        }
    }
    Print();
}

运行结果

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值