📜  门|门 CS 1996 |第 49 题(1)

📅  最后修改于: 2023-12-03 15:42:20.721000             🧑  作者: Mango

门|门 CS 1996 |第 49 题

本题来源于 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$ 秒前进门的人分到同一组。

同时,每个人进门的时间点也影响到了所有人的等待时间。我们可以利用贪心算法,尽可能让等待时间最长的人尽早进入考场。可以考虑按照考试成绩排序,考试成绩越高的人优先进入考场。同时,在处理每位考生的进场时间时,也要统计等待时间以及所有人的进门时间组,以帮助后面的计算。

最后,我们可以按照以下思路计算最短时间:

  1. 按照进门时间组排序

  2. 遍历每一个进门时间组

  3. 选择进场时间最早的人进场(考试成绩优先),并将其他人等待时间减去该人进场时间的差

  4. 利用组内等待时间和成员数,按照公式计算进场时间

  5. 更新进场时间之后,重新对人员进行排序(考试成绩高优先),并计算出下一轮的进门时间

  6. 重复 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)
总结

本题可以锻炼优化思维和贪心策略。代码实现相对复杂,需要多次排序、组内等待时间计算和时间戳更新等等步骤。同时在实现时也要考虑数据规模,使用较优算法和数据结构以增加计算速度。