[PAT]Table Tennis (30)Java實現

JacobGo發表於2017-10-25

1026. Table Tennis (30)

時間限制
400 ms
記憶體限制
65536 kB
程式碼長度限制
16000 B
判題程式
Standard
作者
CHEN, Yue

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


題目要求對乒乓球廳的事件進行模擬。

根據使用者到達時間排隊,有空桌子則按先到先服務的原則處理,如果佇列中有VIP使用者並且有VIP桌子空閒,則VIP可以“自成一隊”,按照到達順序直接分配到VIP桌,如果沒有VIP桌空閒,則VIP和普通使用者同樣對待。如果隊中沒有VIP並且編號最小的恰是VIP桌,普通使用者也可以使用VIP桌。

這類題目需要處理桌子的空閒與使用者的到達、服務時間之間的關係,需要有一個較好的思路,否則很容易混亂。

一個比較好的思路是為每個桌子設定空閒時間,首先全部初始化為上午8:00,當處理一個人時,首先從桌子列表最前面取到桌子,然後根據自己的到達時間和桌子的空閒時間即可計算出桌子的新空閒時間、使用者的等待時間和服務時間(有可能到關門時達不到預期服務時間)。

這道題麻煩在VIP上,如果有VIP使用者,他們可以“插隊”,要處理這些使用者,就會讓問題變得複雜,不能簡單的取出第一個未服務使用者和第一個桌子,而是要考慮有VIP使用者和VIP桌子的情況,這裡有兩種優秀的解法:

①類似歸併排序的思想,維護普通使用者和VIP使用者兩個佇列。

②僅使用一個佇列,先考慮VIP情況,沒有VIP被處理則按照正常情況處理,演算法來自sunbaigui

我完整的參考了sunbaigui的解法,他的解法非常巧妙,下面羅列一下關鍵點:

①對使用者排序後從前到後處理,初始化服務開始時間為INF,這樣當處理完一個人時,他的服務時間不再是INF,由此判斷是否處理完畢,不必另設標誌。

②不對桌子排序,而是找到所有桌子中空閒時間最早的,從桌子列表從前到後篩選,這樣就保證了按照編號的順序,十分巧妙。

③根據②中找到的最早空閒時間,找出所有符合的桌子和使用者,分別儲存一個新的容器,後面就針對這兩個新的容器處理。

④分情況討論,單人單桌、多人單桌、多人多桌,在這三種情況下分別判斷是否有VIP被服務。

⑤如果④中沒有VIP被服務,則取出新容器中第一個使用者和第一個桌子,正常處理。



package go.jacob.day1024;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;

public class Demo1 {
	static List<Node> user;
	static List<Table> table;

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		user = new ArrayList<Node>();
		table = new ArrayList<Table>();
		// 讀入運動員資訊
		for (int i = 0; i < n; i++) {
			String[] times = sc.next().split(":");
			Node node = new Node(calTime(times), sc.nextInt()*60, sc.nextInt());
			user.add(node);
		}
		// 讀取桌子資訊
		int k = sc.nextInt(), m = sc.nextInt();// 桌子數和vip桌子數
		for (int i = 0; i < k; i++) {
			Table t = new Table(0, 8 * 3600, 0);
			table.add(t);
		}
		// 設定vip桌子
		for (int i = 0; i < m; i++) {
			int c = sc.nextInt() - 1;// 桌子的序號為N-1
			table.get(c).tag = 1;
		}
		// 按到時間升序排序
		Collections.sort(user, new Comparator<Node>() {
			@Override
			public int compare(Node n1, Node n2) {
				return n1.arrive - n2.arrive;
			}
		});
//		System.out.println("第一次排序:"+user);
//		System.out.println("第一次排序:"+table);
		// 開始處理服務
		for (int i = 0; i < n; i++) {
			if (user.get(i).serve != Integer.MAX_VALUE)
				continue;
			// 找出最早空閒的時間,採用從頭到尾遍歷的方式。
			int minFreeTime = Integer.MAX_VALUE;
			for (int j = 0; j < k; j++) {
				minFreeTime = Math.min(minFreeTime, table.get(j).freeTime);
			}
			// 確定最早開始服務的時間
			int timePoint = Math.max(user.get(i).arrive, minFreeTime);
			if (timePoint >= 21 * 3600)
				break;// 超時,退出迴圈
			List<Integer> userList = new ArrayList<Integer>();
			List<Integer> tableList = new ArrayList<Integer>();
			// 根據time Point找到所有可能被服務的人,是為了處理有VIP優先去VIP桌的情況
			for (int j = i; j < n; j++)
				if (user.get(j).serve == Integer.MAX_VALUE && user.get(j).arrive <= timePoint)
					userList.add(j);
			// 找出timePoint之前空閒的桌子,因為可能使用者到達比較晚,會有多個桌子空閒
			for (int j = 0; j < k; j++)
				if (table.get(j).freeTime <= timePoint)
					tableList.add(j);

			boolean flag = false;// 判斷是否處理了一個服務
			// 首先特殊處理VIP,如果沒有VIP被處理則處理一個普通使用者,每次只處理一個
			if (userList.size() == 1 && tableList.size() > 1) {
				if (user.get(userList.get(0)).tag == 1) {
					for (int j = 0; j < tableList.size(); j++) {
						if (table.get(tableList.get(j)).tag == 1) {
							flag = true;
							updateInfo(userList.get(0), tableList.get(j));
							break;
						}
					}
				}
			} else if (tableList.size() == 1 && userList.size() > 1) {// 一桌多人,要考慮VIP使用者
				if (table.get(tableList.get(0)).tag == 1) {
					for (int j = 0; j < userList.size(); j++) {
						if (user.get(userList.get(j)).tag == 1) {
							flag = true;
							updateInfo(userList.get(j), tableList.get(0));
							break;
						}
					}
				}
			} else if (tableList.size() > 1 && userList.size() > 1) {// 多人多桌
				for (int j = 0; j < tableList.size(); j++) {
					if (table.get(tableList.get(j)).tag == 1) {
						for (int u = 0; u < userList.size(); u++) {
							if (user.get(userList.get(u)).tag == 1) {
								flag = true;
								updateInfo(userList.get(u), tableList.get(j));
								break;
							}
						}
					}
				}
			}
			if (!flag)// 如果還沒有被處理,則第一個與第一個配對
				updateInfo(userList.get(0), tableList.get(0));
			i--;// 如果處理的是第i個人,寫一次迴圈會直接continue
		}
		// 輸出處理
		// 根據服務時間和到達時間排序
		Collections.sort(user, new Comparator<Node>() {
			@Override
			public int compare(Node o1, Node o2) {
				if (o1.serve == o2.serve)
					return o2.arrive - o1.arrive;
				return o1.serve - o2.serve;
			}
		});
		for (int i = 0; i < n; i++) {
			if (user.get(i).serve >= 21 * 3600)
				break;
			int h1, m1, s1, h2, m2, s2;
			int t = user.get(i).arrive;
			h1 = t / 3600;
			t %= 3600;
			m1 = t / 60;
			t %= 60;
			s1 = t;
			t = user.get(i).serve;
			h2 = t / 3600;
			t %= 3600;
			m2 = t / 60;
			t %= 60;
			s2 = t;
			// 等待時間四捨五入
			System.out.printf("%02d:%02d:%02d %02d:%02d:%02d %d\n", h1, m1, s1, h2, m2, s2,
					(user.get(i).wait + 30) / 60);
		}
		for (int i = 0; i < k; i++) {
			if (i != k - 1)
				System.out.printf("%d ", table.get(i).num);
			else
				System.out.printf("%d", table.get(i).num);
		}
	}

	// 更新資訊
	private static void updateInfo(Integer userID, Integer tableID) {
		
		user.get(userID).serve = Math.max(user.get(userID).arrive, table.get(tableID).freeTime);

		user.get(userID).wait = user.get(userID).serve - user.get(userID).arrive;
		table.get(tableID).num++;
		table.get(tableID).freeTime = user.get(userID).serve + Math.min(user.get(userID).process, 2 * 3600);
//		System.out.println("hhhhhhhhh:"+table.get(tableID));
	}

	// 計算時間,以秒為單位
	private static int calTime(String[] times) {
		int h = Integer.parseInt(times[0]);
		int m = Integer.parseInt(times[1]);
		int s = Integer.parseInt(times[2]);

		return h * 3600 + m * 60 + s;
	}
}

/*
 * 運動員類
 */
class Node {
	int arrive, process, tag;// 到達時間、打球時間和VIP標誌
	int serve = Integer.MAX_VALUE;
	int wait = Integer.MAX_VALUE;// 服務開始時間和等待時間

	public Node(int arrive, int process, int tag) {
		super();
		this.arrive = arrive;
		this.process = process;
		this.tag = tag;
	}

	@Override
	public String toString() {
		return "Node [arrive=" + arrive + ", process=" + process + ", tag=" + tag + ", serve=" + serve + ", wait="
				+ wait + "]";
	}
}

class Table {
	int tag;// vip標誌
	int freeTime, num;// 空閒時間和服務數

	public Table(int tag, int freeTime, int num) {
		super();
		this.tag = tag;
		this.freeTime = freeTime;
		this.num = num;
	}

	@Override
	public String toString() {
		return "Table [tag=" + tag + ", freeTime=" + freeTime + ", num=" + num + "]";
	}
	
}






相關文章