📅  最后修改于: 2023-12-03 15:42:20.721000             🧑  作者: Mango
本题来源于 1996 年清华大学吴健雄老师开设的计算机科学课程(CS1996)。
有 N 个人参加了一门考试,考试时有 M 扇门。考试时每个人必须进入门里才算开始考试。一些人可能选择等待其他人一起进入门里,但至少需要有一名考生进入门内,剩下的人才能用该扇门进入考场,需要至少等待 $X$ 秒钟。而进入门的时间 T 和该考生的考试成绩 S 相关,其中 T 与 S 均为正整数。门可以同时被多人使用,且所有人的语言和操作都是一致的,即让一个人进门和让多个人进门所需时间相同,均为 $Y$ 秒。
问:所有考生进入考场并完成考试所需的最短时间。
输入数据第一行两个正整数 N 和 M,分别代表参加考试的人数和考场所在建筑物的门数。
接下来 $N$ 行,每行描述一个考生的信息,包括考生编号 $i$、进入门的时间 $t_i$ 和考试成绩 $s_i$。
接下来一行一个整数 $X$。
最后一行一个整数 $Y$,表示多人同时进出同一扇门所需的时间。
输出一个正整数,表示考试完成的最短时间。
$1 \leqslant N,M \leqslant 10^3$,
$1 \leqslant X,Y \leqslant 100$,
$1 \leqslant t_i,s_i \leqslant 10^5$,
输入保证所有考生的编号 1~N。
首先,我们可以考虑每个人进门的时间点。显然,如果先让进门时间小的人入场,会消耗较多的时间。由于门可以同时被多人进出,因此我们可以将所有人分成若干组,每一组人的进门时间差应尽可能小。容易发现,当按照进门时间顺序排序时,每个人最多与与之前一个人分到一组,最少与与之前 $X$ 秒前进门的人分到同一组。
同时,每个人进门的时间点也影响到了所有人的等待时间。我们可以利用贪心算法,尽可能让等待时间最长的人尽早进入考场。可以考虑按照考试成绩排序,考试成绩越高的人优先进入考场。同时,在处理每位考生的进场时间时,也要统计等待时间以及所有人的进门时间组,以帮助后面的计算。
最后,我们可以按照以下思路计算最短时间:
按照进门时间组排序
遍历每一个进门时间组
选择进场时间最早的人进场(考试成绩优先),并将其他人等待时间减去该人进场时间的差
利用组内等待时间和成员数,按照公式计算进场时间
更新进场时间之后,重新对人员进行排序(考试成绩高优先),并计算出下一轮的进门时间
重复 2~5 步,直到所有人进入考场完成。
具体实现可以参考下面的代码:
import heapq
# 读入数据
n, m = map(int, input().split())
students = []
for i in range(n):
ti, si = map(int, input().split())
students.append((i + 1, ti, si))
x = int(input())
y = int(input())
# 按照进门时间排序
students = sorted(students, key=lambda x: x[1])
# 初始化每个人的等待时间
total_time_stamp = 0
wait_time = [0] * n
groups = [[] for _ in range(m)]
for i, stu in enumerate(students):
# 更新人员所在时间组
min_time_stamp = total_time_stamp
for group_id, group in enumerate(groups):
if len(group) == 0:
min_time_stamp = total_time_stamp
break
if total_time_stamp - group[-1][1] >= x:
min_time_stamp = max(min_time_stamp, group[-1][1])
break
min_time_stamp = max(min_time_stamp, group[0][1])
group.pop(0)
groups[min_time_stamp % m].append((i, stu[1], stu[2]))
# 计算该考生的等待时间
wait_time[i] = total_time_stamp - stu[1]
# 更新时间戳和下一位考生进门时间
total_time_stamp += y
next_stu_idx = i + 1
if next_stu_idx < n and students[next_stu_idx][1] < total_time_stamp:
total_time_stamp = students[next_stu_idx][1]
# 统计总时间
ans = total_time_stamp + max(wait_time)
# 输出结果
print(ans)
本题可以锻炼优化思维和贪心策略。代码实现相对复杂,需要多次排序、组内等待时间计算和时间戳更新等等步骤。同时在实现时也要考虑数据规模,使用较优算法和数据结构以增加计算速度。