【PAT甲级 模拟 测试点0、3、4、5、7、8分析】1026 Table Tennis (30 分)

以下代码已过时,无法通过测试点0,请参考我最新的文章:以游戏编程的角度看待模拟时间的算法题——以PAT甲级1026 Table Tennis为例

这篇文章帮我解决了测试点5、7
测试点分析都在代码注释里了

#include<bits/stdc++.h>
using namespace std;

int N, K, M; // 球友对的数量、球桌数、VIP球桌数
const int OpenTime = 28800;  // 开门时间 8点
const int CloseTime = 75600; // 关门时间 21点
struct Player {
    int arrvTime; // 球友到达时间
    int playTime; // 球友占用球桌的时间
    int waitTime; // 球友等待时间
    bool isVipPly;// 球友是不是vip
    int num;      // 球友的输出的编号
    Player(): arrvTime(OpenTime), playTime(0), waitTime(0),isVipPly(false), num(0xffffff) {}
};

struct Table {
    int EndTime;  // 上一对球友打完球离开该球桌的时间
    bool isVipTbl;// 是不是vip桌
    int serveNum; // 桌子招待过多少球友
    Table():EndTime(OpenTime), isVipTbl(false), serveNum(0) {}
};

int main() {
    Player player[10000];
    Table table[101];
    bool VipIsIn[10000] = {false}; // 记录当前VIP是否已被处理过,防止当前处理到的是之前已经服务过的的VIP

    cin >> N;
    int realN = 0; // 记录符合要求的输入数据(到达时间在21点之前的球友)
    for(int i = 0;i < N;++i) {
        // 输入:每队球友的到达时间、占用球桌的时间(最多不能超过两小时)、是否是VIP
        int h, m, s, playT, isVip;
        scanf("%d:%d:%d %d %d\n", &h, &m, &s, &playT, &isVip);
        int arvT = (h*60 + m)*60 + s; // 按照秒数记录player到达时间
        if(arvT < CloseTime){ // 针对测试点3,到达时间刚好为21点,要舍去这个输入
            player[realN].arrvTime = arvT;
            player[realN].playTime = playT > 120 ? 120*60 : playT*60; // 针对测试点4,打球超过2小时的都要强制变成120分钟
            player[realN].isVipPly = isVip;
            realN++;
        }
    }
    N = realN; // 将N变成真正有效的player个数
    
	// 输入有关球桌的数据
    cin >> K >> M;
    for(int i = 0;i < M;++i) {
        int tableID;
        cin >> tableID;
        table[tableID].isVipTbl = true; // 标记vip桌
    }


    sort(player, player+N, [](Player &a, Player &b){  // 按照到达时间排序
        return a.arrvTime < b.arrvTime;
    });
    queue<int> VIPS; // 将VIP按照到达时间放入一个单独的队列
    for(int i = 0;i < N;++i)
        if(player[i].isVipPly)
            VIPS.push(i);

	/*** 算法开始(太难了呀!!) ***/
    int numb = 0; // 记录球友开始打球的顺序
    for(int i = 0;i < N;++i) {
        // 如果当前选手i是已经被提前处理的VIP,那就不能处理要跳过
        if(VipIsIn[i]) continue;
        
        //找到最小结束时间的桌子
        int minTableID = -1;
        int minEndTime = 0xffffff;
        for(int j = 1;j <= K;++j) {
            if(table[j].EndTime < minEndTime){
                minTableID = j;
                minEndTime = table[j].EndTime;
            }
        }
        Player &Pl = player[i]; // 当前要处理的player的引用
        Player &vipPl = player[VIPS.front()]; // 当前队列中第一个vip的引用

        // 如果最早结束的桌子的结束时间超过了21点,那当前和后面到达的选手都不能处理输出
        if(table[minTableID].EndTime >= CloseTime) { // 测试点0、8(测试点8是等于号)
            Pl.num = 0xffffff; // 标记为无效,不能输出
            break; // 因为每个选手的num一开始就都初始化为0xffffff,所以可以直接break;否则要continue
        }
        
        // 测试点5、7:vip用户要选择第一张空闲的vip桌
        if(Pl.isVipPly && !table[minTableID].isVipTbl){ // 当前选手是VIP选手,要选择空闲的普通桌子和VIP桌子中选择VIP桌子
            for(int j = 1; j <= K; j++){ 
                if(Pl.arrvTime >= table[j].EndTime && table[j].isVipTbl){ //测试点7在这个等于号
                    minTableID = j; 
                    break;
                }
            }
        }
		
		/******* 终于找到了正确的桌子 *******/
        Table &Tb = table[minTableID]; 

        // 如果vip和非vip都需要等待,而且空出来的球桌是VIP球桌,那就让VIP优先
        if(vipPl.arrvTime < Tb.EndTime && Pl.arrvTime < Tb.EndTime &&
           Tb.isVipTbl && !VIPS.empty()) { // !VipIsIn[i]是防止当前处理到的是之前已经服务过的的VIP
            // 处理的是队伍中当前选手i的后面的VIP选手
            vipPl.waitTime = (Tb.EndTime - vipPl.arrvTime);
            Tb.EndTime += vipPl.playTime;
            VipIsIn[VIPS.front()] = true; // 记录该VIP已被服务过,防止后面遍历到他又处理一遍
            vipPl.num = numb; // 记录输出顺序
            i--; // 因为处理的是当前选手i后面的VIP选手,这一次的循环没有处理选手i,所以要i--抵消i++来停下处理当前选手i
            VIPS.pop(); // 处理完当前VIP了
            
        } else { // 当前选手不是的VIP选手 或者 是VIP选手但当成普通选手的VIP选手(空闲球桌不是VIP球桌)
            if(Pl.arrvTime < Tb.EndTime) { // 需要等待
                Pl.waitTime = (Tb.EndTime - Pl.arrvTime);
                Tb.EndTime += Pl.playTime;
            } else { // 不需要等待
                Tb.EndTime = Pl.arrvTime + Pl.playTime;
            }
            Pl.num = numb; // 记录输出顺序
            
            /* VIP选手没碰上VIP发挥作用的情况(也就是当前处理的选手是VIP但是把他当普通选手处理了
            所以要在VIP队列里删除他,并且标记该VIP为已经处理过) */
            if(Pl.isVipPly){ // 这个超关键 
                VipIsIn[i] = true;
                VIPS.pop();
            }
        }
        numb++; // 因为有i--,所以用numb来标记输出顺序
        Tb.serveNum++; // 统计桌子招待选手数量
    }

    
    sort(player, player+N, [](Player &a, Player &b){ // 按照记录的输出标记顺序来输出
        return a.num < b.num;
    });
    for(int i = 0;i < N;++i) {
        Player &Pl = player[i];
        if(Pl.num == 0xffffff) continue; // 超过21点才能打球的球友就不能输出了
        int ah = Pl.arrvTime/3600, am = Pl.arrvTime%3600/60, as = Pl.arrvTime%60;
        int bh = (Pl.arrvTime+Pl.waitTime)/3600, bm = (Pl.arrvTime+Pl.waitTime)%3600/60, bs = (Pl.arrvTime+Pl.waitTime)%60;
        // 等待的分钟数要四舍五入
        if(player[i].waitTime%60 >= 30) {
            player[i].waitTime = player[i].waitTime/60 + 1;
        } else {
            player[i].waitTime = player[i].waitTime/60;
        }
        printf("%02d:%02d:%02d %02d:%02d:%02d %d\n",ah,am,as,bh,bm,bs,player[i].waitTime);
    }
    for(int i = 1;i <= K;++i) {
        cout << table[i].serveNum << (i == K?"\n":" ");
    }
    
    return 0;
}

/* 测试点5、7测试样例,输出的桌子要为 0 0 1 而不是 1 0 0
1
08:00:00 20 1
3 1
3
*/

K个球桌(编号1~K),但球桌都满的时候,有一条等待队伍
有VIP的存在,所以在输入的最后给了VIP球桌的编号,当有VIP球桌开放的时候,
队伍里面的第一个VIP将会无视他在队伍的顺序,直接进入该VIP球桌;
如果没有VIP的话或VIP球桌那就按正常形式。

营业时间为:8点~21点

统计队伍中每个人的等待时间 和 每个球桌在这一天共招待了几对球友

输出每对球友的到达时间 开始打球的时间

无事做将代码缩短成了100行

#include<bits/stdc++.h>
using namespace std;

int N, K, M;
const int OpenTime = 28800;
const int CloseTime = 75600;
bool VipIsIn[10000] = {false};
struct Player {
    int arrvTime, playTime, waitTime, isVipPly, num;
    Player(): arrvTime(OpenTime), playTime(0), waitTime(0),isVipPly(false), num(0xffffff) {}
    bool operator<(Player b)const{ return this->arrvTime < b.arrvTime; }
}player[10000];
struct Table {
    int EndTime, isVipTbl, serveNum; 
    Table():EndTime(OpenTime), isVipTbl(false), serveNum(0) {}
}table[101];

int main() {
    cin >> N;
    int realN = 0;
    for(int i = 0;i < N;++i) {
        int h, m, s, playT, isVip;
        scanf("%d:%d:%d %d %d\n", &h, &m, &s, &playT, &isVip);
        int arvT = (h*60 + m)*60 + s;
        if(arvT < CloseTime){
            player[realN].arrvTime = arvT;
            player[realN].playTime = playT > 120 ? 120*60 : playT*60;
            player[realN].isVipPly = isVip;
            realN++;
        }
    }
    N = realN;
    cin >> K >> M;
    for(int i = 0;i < M;++i) {
        int tableID;
        cin >> tableID;
        table[tableID].isVipTbl = true;
    }
    sort(player, player+N);
    queue<int> VIPS;
    for(int i = 0;i < N;++i)
        if(player[i].isVipPly)
            VIPS.push(i);

    int numb = 0;
    for(int i = 0;i < N;++i) {
        Player &Pl = player[i];
        Player &vipPl = player[VIPS.front()];
        int minTableID = -1, minEndTime = 0xffffff;
        for(int j = 1;j <= K;++j) {
            if(table[j].EndTime < minEndTime){
                minTableID = j;
                minEndTime = table[j].EndTime;
            }
        }
        if(table[minTableID].EndTime >= CloseTime) {
            Pl.num = 0xffffff;
            break;
        }
        if(Pl.isVipPly && !table[minTableID].isVipTbl){
            for(int j = 1; j <= K; j++){ 
                if(Pl.arrvTime >= table[j].EndTime && table[j].isVipTbl){
                    minTableID = j; 
                    break;
                }
            }
        }
        Table &Tb = table[minTableID]; 
        if(VipIsIn[i]) continue;
        if(vipPl.arrvTime < Tb.EndTime && Pl.arrvTime < Tb.EndTime && Tb.isVipTbl && !VIPS.empty()) {
            vipPl.waitTime = (Tb.EndTime - vipPl.arrvTime);
            Tb.EndTime += vipPl.playTime;
            VipIsIn[VIPS.front()] = true;
            vipPl.num = numb; 
            i--;
            VIPS.pop();
        } else {
            if(Pl.arrvTime < Tb.EndTime) {
                Pl.waitTime = (Tb.EndTime - Pl.arrvTime);
                Tb.EndTime += Pl.playTime;
            } else {
                Tb.EndTime = Pl.arrvTime + Pl.playTime;
            }
            if(Pl.isVipPly){ 
                VipIsIn[i] = true;
                VIPS.pop();
            }
            Pl.num = numb;
        }
        numb++;
        Tb.serveNum++;
    }
    sort(player, player+N, [](Player a, Player b){return a.num < b.num;});
    for(Player &Pl : player) {
        if(Pl.num == 0xffffff) continue;
        int ah = Pl.arrvTime/3600, am = Pl.arrvTime%3600/60, as = Pl.arrvTime%60, bh = (Pl.arrvTime+Pl.waitTime)/3600, bm = (Pl.arrvTime+Pl.waitTime)%3600/60, bs = (Pl.arrvTime+Pl.waitTime)%60;
        Pl.waitTime = Pl.waitTime/60 + (Pl.waitTime%60 >= 30);
        printf("%02d:%02d:%02d %02d:%02d:%02d %d\n",ah,am,as,bh,bm,bs,Pl.waitTime);
    }
    for(int i = 1;i <= K;++i) cout << table[i].serveNum << (i == K?"\n":" ");
    return 0;
}
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值