[PAT]Table Tennis (30)Java实现

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 + "]";
	}
	
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值