1. 程式人生 > >1026 Table Tennis (30 分)模擬排列問題

1026 Table Tennis (30 分)模擬排列問題

題目

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這種題目的大神,差距很大,繼續努力。