1
题目大意:

有K张乒乓球桌(编号为1-K)于8:00 21:00 开放,每一组球员都是选择当前空闲的最小编号的球桌进行训练,且训练时长最多允许2h,而如果到达时没有球桌空闲,则排成队列等待。这K张球桌中有M张是VIP球桌,如果存在VIP球桌空闲,且等待队列中存在VIP球员,那么等待队列中第一个VIP球员将前往编号最小的VIP球桌训练:如果存在VIP球桌空闲,等待队列中没有VIP球员,那么VIP球桌将被分配给等待队列中第一个普通球员:而如果当前没有VIP球桌空闲,那么VIP球员将被看作普通球员处理。现在给出每个球员的到达时间、需要训练时长、是否是VIP球员以及给出球桌数和所有VIP球桌编号,求所有在关门前得到训练的球员的到达时间、训练开始时间、等待时长以及所有球桌当天的服务人数。

解题思路:

该题目应该是PAT_1014和PAT_1017这类模拟问题中最为复杂的了。对于等待队列中有非VIP球员和VIP球员,球桌中有VIP球桌和非VIP球桌,那么该问题本质上就是将球桌和球员一一对应分配的问题,考虑所有的情况,简单点说就是,球座和球员的组合分为4种不同的情况:

  1. 第一:最早空闲的球桌index是VIP球桌,队首球员是VIP球员,那么将index号桌子分配给他。
  2. 第二:最早空闲的球桌index是VIP球桌,但是队首球员不是VIP球员,那么首先找到等待队列中第一个VIP球员,如果该VIP球员的到达时间在VIP球桌空闲之前,那么将该球桌分配给队列中的第一个VIP球员。如果不是,就将该VIP球桌分配给队首队员。
  3. 第三:最早空闲的球桌index不是VIP球桌,但是队首球员是VIP球员,那么首先去查询最早空闲的VIP球桌VIPindex,如果VIP球员的到达时间在VIP球桌空闲时间之后,那么将该VIP球桌分配给该球员。否则,就将非VIP球桌index分配给该VIP球员。
  4. 第四:最早空闲的球桌index不是VIP球桌,队首球员不是VIP球员,那么将该球桌分配给该球员即可。

想清楚上面的逻辑后接下来就是确定数据结构存储数据了,首先对于单个球员和球座都可以采用结构体来存储数据,球员的等待队列使用vector<Player> wait_queue来存储,由于球桌固定了编号与之对应,那么就可以使用结构体数组来存储所有的球桌,这里为了方便实现VIP球员和非VIP球员的分配使用了2个等待队列分别存储VIP球员等待队列和非VIP球员的等待队列,同时使用vip_indexnon_vip_index指向当前待处理的VIP球员和非VIP球员的位置,最后将每一个使用过球桌训练的队员使用vector<Player> result来存储(因为有的队员由于时间原因无法获得训练机会)。

算法步骤:

算法步骤:

  1. 第一步:数据的输入,按照题目要求的格式输入即可,不过得注意,对于到达时间在结束服务时间end_serve_time后的直接忽略,同时这里统一将时间以秒作为单位,
  2. 需要将训练时长乘以60,并且在训练时长超过limit_time(2h)的时候需要将训练时间压缩为2h。
  3. 第二步:将等待队列中的球员按照到达时间进行排序。
  4. 第三步:遍历等待队列,将VIP用户与非VIP用户分离开来。
  5. 第四步:处理算法的球员与球桌匹配逻辑,循环次数为等待队列的长度.
    循环开始: 首先获得队首球员,队首队员的获取是通过比较VIP等待队列和非VIP等待队列中待处理队员谁先到达来判断的,需要注意的是有可能VIP等待队列或者 非VIP等待队列中的队员处理完毕,所以需要进行判断(这里是该程序出现段错误和答案错误的点)。 然后就是4种情况的讨论,需要注意的是队员的等待时间有可能为0,在队员到达的时候球桌就是空闲的那么为0,否则为球桌空闲时间减去球员的到达时间, 球桌的下场服务时间可以统一为球桌开始服务时间+队员的需要训练时间。在程序中球员训练结束的标志就是等待时间计算完毕,然后就要更新桌子的下次 空闲时间,并且将队员添加到result中,将vip_indexnon_vip_index自增。第五步:对于result按照服务开始的时间进行排序,然后按照格式输出result中的球员的相关信息和球座的服务人数。
注意点:
  1. 等待时长是需要四舍五入到整数,也就是需要加上30秒再除以60或者使用round函数。
  2. table的下标是从1开始的,对于从0开始的写法,最后会输出3 2 3的情况(是的题目没有说(╥╯^╰╥))
  3. 在找到最早空闲的桌子下标Index的时候得判断当前球桌的空闲时间是否在服务结束时间之后,如果是那就直接退出循环,无需再进行遍历。
提交结果:

第一次提交:

测试点1,4,5答案错误
测试点3,6,7段错误

第二次提交:答案全对,错误的原因在于需要在最开始获取队首队员的时候需要判断此时还有没有待处理的VIP队员或者非VIP队员。
PAT甲级_1026提交结果

具体代码实现如下:
#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

int N;//球员数量
int K,M;//K张桌子,M张VIP桌子
int begin_serve_time = 8*3600;//开始服务时间
int end_serve_time = 21*3600;//结束服务时间
int limit_time = 2*3600;//最长服务时间

struct Player{
    int arriving_time;// 到达时间
    int playing_time;// 需要服务时间
    int waiting_time;// 等待时间
    int serve_time; // 开始服务的时刻
    bool isVIP;//是否是VIP
};

vector<Player> wait_queue;//等待队列
int vip_index = 0;//等待队列工作指针,指向第一个待处理的VIP用户
int non_vip_index = 0;//等待队列工作指针,指向第一个待处理的非VIP用户
vector<Player> vip_wait_queue;//VIP等待队列
vector<Player> non_vip_wait_queue;//非VIP用户等待队列
vector<Player> result;//按照处理结束的顺序保存每一个已经处理的队员


struct Table{
    int begin_time = begin_serve_time;//开始服务时间
    int number;//服务的球员数目
    bool isVIP;//是否是VIP桌子
}table[110];

int getTime(int hh,int mm,int ss){
    return hh*3600+mm*60+ss;
}

bool cmpByArriveTime(Player a,Player b){
    return a.arriving_time<b.arriving_time;
}

bool cmpByServeTime(Player a,Player b){
    return a.serve_time<b.serve_time;
}

int main(){
    scanf("%d",&N);
    for(int i=0;i<N;++i){
        int hh,mm,ss,time,vip;
        scanf("%d:%d:%d %d %d",&hh,&mm,&ss,&time,&vip);
        int arrive_time = getTime(hh,mm,ss);
        if(arrive_time<end_serve_time){
            Player player;
            // 在结束服务之前到达
            player.arriving_time = arrive_time;
            // 最长服务时间是2小时
            player.playing_time = time*60>limit_time?limit_time:time*60;
            player.isVIP = vip == 1;
            wait_queue.push_back(player);
        }
    }
    scanf("%d %d",&K,&M);
    // 标记VIP桌子
    for(int i=0;i<M;++i){
        int index;
        scanf("%d",&index);
        table[index].isVIP = true;
    }
    int n = wait_queue.size();
    // 按照到达时间进行排序
    sort(wait_queue.begin(),wait_queue.end(),cmpByArriveTime);
    // 将VIP用户与非VIP用户分离开来
    for(int i=0;i<n;++i){
        if(wait_queue[i].isVIP){
            vip_wait_queue.push_back(wait_queue[i]);
        }else{
            non_vip_wait_queue.push_back(wait_queue[i]);
        }
    }
    int vip_number = vip_wait_queue.size();
    int non_vip_number = non_vip_wait_queue.size();
    // 处理n个用户
    for(int i=0;i<n;++i){
        // 第二次提交修改的代码部分:
        //队首球员
        bool flag;//判断队首球员是否是VIP球员
        if(vip_index<vip_number&&non_vip_index<non_vip_number){
            // VIP队员和非VIP队员都存在,选择先到达的作为队首队员
            flag = vip_wait_queue[vip_index].arriving_time < non_vip_wait_queue[non_vip_index].arriving_time;
        }else {
            // 存在VIP队员就是true,没有就是false
            flag = vip_index < vip_number;
        }
        // 队首队员
        Player player = flag ? vip_wait_queue[vip_index] : non_vip_wait_queue[non_vip_index];
        // 首先找到服务时间最早结束并且编号最小的桌子
        int minTime = 0x3fffffff;
        int index;
        for(int j=1;j<=K;++j){
            if(minTime>table[j].begin_time){
                minTime = table[j].begin_time;
                index = j;
            }
        }
        if(minTime>=end_serve_time){
            //最早服务结束的桌子在21点以后就不进行服务了
            break;
        }
        if(table[index].isVIP){
            // 如果是VIP桌子
            if(player.isVIP){
                // 队首球员也是VIP,将该VIP球座分配给VIP球员
                if(table[index].begin_time>=player.arriving_time){
                    // 球员到的时候球桌还没有空闲
                    player.waiting_time = table[index].begin_time - player.arriving_time;
                    player.serve_time = table[index].begin_time; // 更新球员的开始服务时间
                }else{
                    // 球员到的时候球桌已经空闲,没有等待时间,开始服务时间就是到达时间
                    player.waiting_time = 0;
                    player.serve_time = player.arriving_time;// 球员开始服务时间就是球员到达时间
                }
                table[index].begin_time = player.serve_time + player.playing_time;//更新该桌子的开始服务时间
                ++table[index].number;//累计该桌子为用户提供服务的人数
                result.push_back(player);
                //队首球员是 VIP
                ++vip_index;
            }else{
                // 队首球员不是VIP,判断第一个VIP球员是否在球座空闲之前到达。
                if(vip_index<vip_number&&vip_wait_queue[vip_index].arriving_time<=table[index].begin_time){
                    // 在空闲前到达,分配给该VIP球员.
                    vip_wait_queue[vip_index].waiting_time = table[index].begin_time - vip_wait_queue[vip_index].arriving_time;
                    vip_wait_queue[vip_index].serve_time = table[index].begin_time;
                    table[index].begin_time += vip_wait_queue[vip_index].playing_time;//更新该桌子的开始服务时间
                    ++table[index].number;//累计该桌子为用户提供服务的人数
                    result.push_back(vip_wait_queue[vip_index]);
                    ++vip_index;
                }else{
                    // 没有VIP球员或者不在桌子空闲前到达,分配给队首球员
                    if(table[index].begin_time>=player.arriving_time){
                        // 球员到的时候球桌还没有空闲
                        player.waiting_time = table[index].begin_time - player.arriving_time;
                        player.serve_time = table[index].begin_time; // 更新球员的开始服务时间
                    }else{
                        // 球员到的时候球桌已经空闲,没有等待时间,开始服务时间就是到达时间
                        player.waiting_time = 0;
                        player.serve_time = player.arriving_time;// 球员开始服务时间就是球员到达时间
                    }
                    table[index].begin_time = player.serve_time + player.playing_time;//更新该桌子的开始服务时间
                    ++table[index].number;//累计该桌子为用户提供服务的人数
                    result.push_back(player);
                    //队首球员不是VIP
                    ++non_vip_index;
                }
            }
        }else {
            // 桌子不是VIP
            if(player.isVIP){
                //队首球员是VIP,查找最早空闲的VIP球座
                minTime = 0x3fffffff;
                int vipIndex = -1;
                for(int j=1;j<=K;++j){
                    if(minTime>table[j].begin_time&&table[j].isVIP){
                        minTime = table[j].begin_time;
                        vipIndex = j;
                    }
                }
                if(vipIndex!=-1&&table[vipIndex].begin_time<=player.arriving_time){
                    // 找到一张VIP桌子并且在队首球员到达之后就是空闲的,将该VIP球桌分配给队首球员
                    player.waiting_time = 0;//没有等待时间
                    player.serve_time = player.arriving_time;//开始服务时间就是到达时间
                    table[vipIndex].begin_time = player.serve_time + player.playing_time;//更新该桌子的下次服务时间
                    ++table[vipIndex].number;
                    result.push_back(player);
                    //队首球员是 VIP
                    ++vip_index;
                }else {
                    // 没有VIP桌子或者队首球员到达时没有空闲,将index非VIP桌子分配给队首球员
                    if(table[index].begin_time>=player.arriving_time){
                        // 球员到的时候球桌还没有空闲
                        player.waiting_time = table[index].begin_time - player.arriving_time;
                        player.serve_time = table[index].begin_time; // 更新球员的开始服务时间
                    }else{
                        // 球员到的时候球桌已经空闲,没有等待时间,开始服务时间就是到达时间
                        player.waiting_time = 0;
                        player.serve_time = player.arriving_time;// 球员开始服务时间就是球员到达时间
                    }
                    table[index].begin_time = player.serve_time + player.playing_time;//更新该桌子的开始服务时间
                    ++table[index].number;//累计该桌子为用户提供服务的人数
                    result.push_back(player);
                    //队首球员是 VIP
                    ++vip_index;
                }
            }else {
                // 球员不是VIP,直接将该桌子分配给该球员
                if(table[index].begin_time>=player.arriving_time){
                    // 球员到的时候球桌还没有空闲
                    player.waiting_time = table[index].begin_time - player.arriving_time;
                    player.serve_time = table[index].begin_time; // 更新球员的开始服务时间
                }else{
                    // 球员到的时候球桌已经空闲,没有等待时间,开始服务时间就是到达时间
                    player.waiting_time = 0;
                    player.serve_time = player.arriving_time;// 球员开始服务时间就是球员到达时间
                }
                table[index].begin_time = player.serve_time + player.playing_time;//更新该桌子的开始服务时间
                ++table[index].number;//累计该桌子为用户提供服务的人数
                result.push_back(player);
                //队首球员不是VIP
                ++non_vip_index;
            }
        }
    }
    // 根据开始服务的时间进行排序
    sort(result.begin(),result.end(),cmpByServeTime);
    //输出每一个用户的到达时间,开始服务时间和等待时间
    int r = result.size();
    for(int i=0;i<r;++i){
        printf("%02d:%02d:%02d %02d:%02d:%02d %d\n",result[i].arriving_time/3600,result[i].arriving_time%3600/60,result[i].arriving_time%60,
               result[i].serve_time/3600,result[i].serve_time%3600/60,result[i].serve_time%60,
               (result[i].waiting_time+30)/60);
    }
    //输出每一张桌子服务的人数
    for(int i=1;i<=K;++i){
        printf("%d",table[i].number);
        if(i<K) printf(" ");
    }
    return 0;
}

乔梓鑫
569 声望17 粉丝

主要分享个人学习经验和心得