PAT 甲级 1026 Table Tennis(逻辑复杂+坑多+代码量大容易出错)

解题总结

1.逻辑提前理清楚,条件都用文档写出来
2.代码量大的时候善用代码块隐藏和功能注释
3.若限制条件交叉,则选择以某一个为基础,展开不同情况往下讨论

写题路程

卡住的点:
1.不能用我自己原来的写法(优先队列模拟最先空余的桌子),因为VIP桌子编号不定,不好处理,则直接遍历所有桌子,分类讨论
2.现在队首的顾客来时,可能已经有很多个空闲的桌子,而要取满足要求的编号最小的桌子而非最早空闲的桌子

条件:
1.若此时有VIP桌子空闲
(1)此时已经有VIP顾客到达,则放第一个到的VIP
(2)此时没有已经到的VIP顾客,则放第一个到的顾客
2.此时VIP顾客在队首
(1)有空闲的VIP桌
(2)没有空闲的VIP桌
(i)有空闲的桌
(ii)没有空闲的桌,选最早空闲的桌子

题解思路

结合两个限制因素分类讨论,第一层大循环是顾客(此事时间),然后第二层循环内得到最早空闲的桌子,分情况讨论
1.第一个空闲的桌子是VIP桌子
(1)此时有到来的vip会员
(2)此时没有到来的vip会员
直接选此时队首的顾客
2.第一个空闲的桌子是普通桌子
(1)队首是vip会员
(i)此时有空闲的vip桌子,选编号最小的
(ii)此时没有空闲的vip桌子
(a)此时有空闲的普通桌,选编号最小的
(b)此时没有空闲的桌,选最早空闲的
(2)队首是普通顾客
(i)选编号最小的空闲桌子(此时是取普通桌)
你一定能保证这是普通桌子吗???

		#include <iostream>
		#include<cstdio>
		#include<algorithm>
		#include<vector>
		#include<queue>
		
		using namespace std;
		
		struct player{
		    int arrive, serve, progress, wait, isVip;
		    int used;
		};
		
		struct table{
		    int isVip, cnt, vacant;
		};
		
		vector<table> tables;
		vector<player> players;
		
		bool cmpPlayer(player a, player b){
		    return a.arrive < b.arrive;
		}
		
		bool cmpStartServe(player a, player b){
		    return a.serve < b.serve;
		}
		
		int main(){
		    int N, K, M;
		    scanf("%d", &N);
		    for(int i = 0; i < N; i++){
		        player temp;
		        int h, s, m, vip, playingTime;
		        scanf("%d:%d:%d %d%d", &h, &m, &s, &playingTime, &vip);
		        temp.arrive = 3600*h + 60*m + s;
		        temp.progress = (playingTime < 120 ? 60*playingTime: 2*60*60);
		        temp.isVip = vip;
		        temp.used = 0;
		        if(temp.arrive <  21*3600) players.push_back(temp);
		        //来晚的人不管
		    }
		    sort(players.begin(), players.end(), cmpPlayer);
		
		    //输入顾客且排序没问题;
		
		    scanf("%d%d", &K, &M);
		    for(int i = 0; i < K; i++){
		        table temp;
		        temp.cnt = 0, temp.vacant = 8*3600;
		        temp.isVip = 0;//忘记初始化vip
		        tables.push_back(temp);
		    }
		    //tables下标数组从0开始,而桌子编号从1开始!
		    for(int i = 0; i < M; i++){
		        int x;
		        scanf("%d", &x);
		        tables[x-1].isVip = 1;
		    }
		
		    for(int i = 0; i < players.size(); ){
		        while(players[i].used) i++;//找到没有招待过的顾客为止
		        if(i >= players.size()) break;//全都招待过了,退出
		        int min_vacant = 21*60*60 + 1, min_index = -1;
		        //找出编号最小的最早空闲的桌子
		
		        //但这样无法保证有vip桌的时候让VIP先进去啊。
		        for(int j = 0; j < tables.size(); j++){
		            if(min_vacant > tables[j].vacant){
		                min_vacant = tables[j].vacant;
		                min_index = j;
		            }
		        }
		
		        if(min_vacant >= 21*60*60) break;//已经过了时间,退
		        //若是vip桌子,若有已经到达的vip顾客就放进去
		        if(tables[min_index].isVip){
		            int j;
		            for(j = 0; j < players.size(); j++){
		                //找到队列中一个没服务过的vip则退出
		                if(players[j].isVip && players[j].used == 0 && players[j].arrive <= tables[min_index].vacant)
		                    break;
		            }
		            //找到了符合要求的vip,更新顾客和桌子信息
		            if(j < players.size()){
		                players[j].used = 1;
		                players[j].serve = max(players[j].arrive,min_vacant);
		                players[j].wait = (players[j].arrive<min_vacant?min_vacant-players[j].arrive:0);
		                tables[min_index].cnt++;
		                tables[min_index].vacant = players[j].serve + players[j].progress;
		            }
		            //否则直接取一个未遍历过的第一个顾客即可
		            else{
		                players[i].used = 1;
		                players[i].serve = max(players[i].arrive,min_vacant);
		                players[i].wait = (players[i].arrive<min_vacant?min_vacant-players[i].arrive:0);
		                tables[min_index].cnt++;
		                tables[min_index].vacant = players[i].serve + players[i].progress;
		                i++;
		            }
		        }else{//找到的是普通桌子
		            //若第一个顾客是vip,则看是否有在他到来之前已经空闲的VIP桌子
		            if(players[i].isVip){
		                int qwq;
		                for(qwq = 0; qwq < tables.size(); qwq++)
		                    if(tables[qwq].isVip && tables[qwq].vacant <= players[i].arrive){
		                        min_index = qwq;
		                        break;
		                    }
		                //若找到空闲vip桌子,则将Min_index赋值为其下标换
		
		                //没找到空闲vip桌子,直接去空闲的编号最小的桌子.
		                if(qwq == tables.size()){
		                    //寻找一来就空闲的编号最小的桌子,有则换其下标为Min_index
		                    int ri;
		                    for(ri = 0; ri < tables.size(); ri++)
		                        if(tables[ri].vacant <= players[i].arrive){
		                            min_index = ri;
		                            break;
		                        }
		                }
		            }
		            //第一个顾客是普通顾客
		            else{
		                //找有没有闲置的桌,有则取编号最小的为min_index,没有则还是默认的min——index
		                int ri;
		                for(ri = 0; ri < tables.size(); ri++)
		                    if(tables[ri].vacant <= players[i].arrive){
		                        min_index = ri;
		                        break;
		                    }
		            }
		            players[i].used = 1;
		            players[i].serve = max(players[i].arrive,tables[min_index].vacant);
		            players[i].wait = (players[i].arrive<tables[min_index].vacant?tables[min_index].vacant-players[i].arrive:0);
		            tables[min_index].cnt++;
		            tables[min_index].vacant = players[i].serve + players[i].progress;
		            i++;
		        }
		    }
		
		    sort(players.begin(), players.end(), cmpStartServe);
		    for(int i = 0; i < players.size(); i++){
		        if(players[i].used == 0) continue;
		        int h1 = players[i].arrive/3600, m1 = (players[i].arrive - 3600*h1)/60 , s1 = players[i].arrive%60;
		        int h2 = players[i].serve/3600, m2 = (players[i].serve - 3600*h2)/60 , s2 = players[i].serve%60;
		        int wait_min = (players[i].wait + 30)/60;//等待时间四舍五入!~
		        printf("%02d:%02d:%02d %02d:%02d:%02d %d\n", h1, m1, s1, h2, m2, s2, wait_min);
		    }
		    for(int i = 0; i < tables.size(); i++){
		        if(i) printf(" ");
		        printf("%d", tables[i].cnt);
		    }
		    return 0;
		}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值