1026 Table Tennis (30 分)模拟排列问题

版权声明:emmm……我也想成为大佬 https://blog.csdn.net/CV_Jason/article/details/84671066

题目

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

解题思路

  题目大意: 一个乒乓球俱乐部有N个球员和K个乒乓球桌台,俱乐部8点到21点开门,N个球员不同时间到达俱乐部,根据他们的到达时间和打球时间按照乒乓球台号码从小到大分配球台。原本是一个极其简单的模拟队列问题,但是题目加了一句话,使得这道题的逻辑瞬间复杂了起来——

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.

  这句话增加了一些约束条件:
  有VIP球桌和VIP会员:1. 如果VIP球桌空闲,且到场的有VIP队员,那么VIP在排队等候的时候优先于一般会员。2. 如果没有VIP会员,VIP球桌空闲的话,一般用户也可以使用,先到先得;3. 如果没有空闲的VIP球桌,VIP会员和一般会员一样排队。

在这里插入图片描述
  我们可以把这个场景抽象建模,按照到达先后顺序,判断每个会员按照规则所能分配的桌台已经对应时间,分为4类情况——
  1 最先空闲下来的是VIP桌子,那么需要检测排队的人群(可能不止一个)中,是否有VIP,如果有,那么先给VIP分配(而不是最早到的那个);
  2 最先空下来的是VIP桌子,但是排队的人群中没有VIP,那么可以给非VIP会员分配VIP桌子;
  3 最先空下来的是非VIP桌子,如果排队的第一个人是VIP,那么需要考虑的是,我们现在比较的是最早空下来的桌子和第一个排队的人,但,如果同时存在其他空闲的VIP桌子,需要给VIP安排VIP桌子,所以此时,不能仅仅找到最先空下来的桌子,应该坚持VIP作为第一个排队者,是否存在VIP空闲桌;
  4 最后一种情况最简单,空闲的是非VIP桌子,第一个排队的是非VIP,直接分配即可。
  这道题没有使用太高级的数据结构的算法,但是逻辑比较复杂,能够在短时间内从题面传达的信息中抽象出来逻辑模型,也颇有难度。

  一些技巧
  1 为了方便时间比较,可统一化为秒来计算,这是PAT模拟排列题型的常用套路;
  2排序还是用sort比较方便,数据结构可以用数组也可以用stl的任意一种序列容器,这里使用的是vector;
  3 查找比较过程中,仅记录桌子以及会员的索引或者指针即可,更新数据可以统一放到最后分配方案确定了之后,可以节省代码长度;
  4 如果一下看不出来题目逻辑,不妨考虑手动把Demo走一遍,然后摸索其中规则,我就是这么做的……

/*
** @Brief:No.1026 of PAT advanced level.
** @Author:Jason.Lee
** @Date:2018-11-27 
** @status: Accepted!
*/

#include<iostream>
#include<algorithm>
#include<vector> 

using namespace std;

struct member{
	int arrivingTime;
	int playingTime;
	int vip;
	int waitingTime;
	int startTime; 
	int haveTable;
	int num;
};

struct table{
	int endTime;
	int totalPeople;
	int vip; 
};

bool cmp(member a,member b){
	return a.arrivingTime<b.arrivingTime;
}

bool cmp2(member a,member b){
	return a.startTime<b.startTime;
}

vector<member> waitQueue;
table tables[101];

int main(){
	int N,M,K;
 	while(scanf("%d",&N)!=EOF){
		for(int i=0;i<N;i++){
			// 输入数据 
			member temp;
			int hh,mm,ss;
			scanf("%d:%d:%d%d%d",&hh,&mm,&ss,&temp.playingTime,&temp.vip);
			temp.arrivingTime = hh*3600+mm*60+ss;
			temp.playingTime = (temp.playingTime>120?120*60:temp.playingTime*60);
			temp.haveTable = -1;
			temp.startTime = 21*3600;
			temp.num = i+1;
			waitQueue.push_back(temp);
		}
		table initTable{8*3600,0,0};
		fill(tables,tables+101,initTable);
		scanf("%d%d",&K,&M);
		for(int i=0;i<M;i++){
			int temp;
			scanf("%d",&temp);
			tables[temp-1].vip = 1;
		}
		sort(waitQueue.begin(),waitQueue.end(),cmp);
		/* -------------分配策略---------------*/		
		for(auto it = waitQueue.begin();it!=waitQueue.end();){
			// 分配第it个会员
			auto it_current = it;// it是遍历整个排队队列的主指针,it_current是临时指针,指向每一次遍历需要安排的会员 
			int min_endTime = 21*3600;// min_endTime 负责记录最早空闲出来的桌子的时间 
			int postion = 0;// postion记录最终分派的桌子的位置 
			bool novip = true;
			// 从K个桌子中遍历最早空闲的桌子,如果存在多个桌子,选择最小的那个 
			for(int i=0;i<K;i++){
				if(min_endTime>tables[i].endTime){
					postion = i;
					min_endTime = tables[i].endTime;
				}
			}
			if(tables[postion].endTime>=21*3600)// 如果最早空出来的桌子都超过了21点,显然此时任何一个桌子都不能再分配 
				break;
			// 选出合适的位置了之后,开始判断,先看桌子是不是VIP桌子,如果是,再看目前排队的人有没有VIP 
			if(tables[postion].vip){
				//【第一大类】最先空出来的桌子是VIP桌子,
				// 那么下一步判断在该桌子空闲的过程中,it_current身后排队的人群中有没有VIP,如果有,VIP优先于当前
				for(;it_current!=waitQueue.end()&&it_current->arrivingTime<=min_endTime;it_current++){
					if(it_current->vip&&it_current->haveTable==-1){
						//【1】 VIP桌子,存在到场的VIP会员,分配该桌子给VIP 
						//cout<<"num = "<<it_current->num<<" positon = "<<postion<<endl;
						it_current->haveTable = postion;
						novip = false;
						break;// 此时it指针不能移动,因为可能vip在很后面的位置,如果移动,当前节点就会被跳过 ,标记该VIP已经被分配过即可 
					}
				} 
				if(novip){
					//【2】VIP桌子,但是不存在到场的VIP会员,那么分配给排队的第一个人 
					it_current = it;
					it_current->haveTable = postion;
					it++;
				}
			}else{//【第二大类】最先空出来的不是VIP桌 
				// 如果当前的会员不是VIP,非VIP球桌分配给非VIP会员,直接分配
				if(it_current->vip!=1){
					it_current->haveTable = postion;
					it++;
				}else{// 如果当前排队的是VIP,那么需要确认是否存在其他空闲的VIP桌子 
					int vipTable = -1;
					for(int i=0;i<K;i++){
						if(tables[i].vip&&tables[i].endTime<=it_current->arrivingTime){
							vipTable = i;
							break;
						}
					} 
					// 存在VIP空闲桌,那么给VIP分配VIP桌子 
					if(vipTable!=-1){
						//cout<<"vipTable = "<<vipTable<<" num = "<<it_current->num<<endl;
						it_current->haveTable = vipTable;
						postion = vipTable; 
					}else{// 不存在VIP桌,只能分配普通桌 
						if(it_current->haveTable==-1){
							it_current->haveTable = postion; 
							//cout<<"postion = "<<postion<<" num = "<<it_current->num<<endl;
						}else{
							it++;
							continue;
						}
					}
					it++;
				} 
			}
			// 更新会员数据,如果到的时候,是空闲的,那么以到的时间为开始时间,否则以桌子的空闲时间为开始时间 
			it_current->startTime = min_endTime>it_current->arrivingTime?min_endTime:it_current->arrivingTime;
			it_current->waitingTime = min_endTime<=it_current->arrivingTime? 0:min_endTime-it_current->arrivingTime;			
			// 更新桌子的数据,结束时间以当前使用者开始使用+使用时间为准 
			tables[postion].endTime = it_current->startTime+it_current->playingTime; 
			if(it_current->startTime<21*3600)
				tables[postion].totalPeople++;
		}
		/* -------------输出---------------*/
		sort(waitQueue.begin(),waitQueue.end(),cmp2);
		for(auto elem:waitQueue){
			if(elem.startTime>=21*3600) continue;
			int hh1 = elem.arrivingTime/3600;
			int mm1 = (elem.arrivingTime - hh1*3600)/60;
			int ss1 = elem.arrivingTime - mm1*60 - hh1*3600;
			int hh2 = elem.startTime/3600;
			int mm2 = (elem.startTime - hh2*3600)/60;
			int ss2 = elem.startTime - mm2*60 - hh2*3600;
			int waitingTime = elem.waitingTime/60 +((elem.waitingTime%60>=30)? 1:0);
			printf("%02d:%02d:%02d %02d:%02d:%02d %d\n",hh1,mm1,ss1,hh2,mm2,ss2,waitingTime);
		} 
		printf("%d",tables[0].totalPeople);
		for(int i=1;i<K;i++){
			printf(" %d",tables[i].totalPeople);
		}
	}
	return 0;
}

在这里插入图片描述

总结

  做这个题真的是累,最开始花了一整天的时间,才搞出来,结果第5和第7测试点始终通不过,然后又花了将近一天的时间去debug,最后发现是自己少考虑了一种情况。真心佩服那些在赛场上能够在短时间AC这种题目的大神,差距很大,继续努力。

猜你喜欢

转载自blog.csdn.net/CV_Jason/article/details/84671066