问题描述
在一次热闹的促销活动中,小C策划了一项优惠券发放计划,准备了 nn 种不同的优惠券,编号为 11 到 nn。为了确保活动顺利进行,小C为优惠券制定了几条规则:
- 每位顾客最多只能领取其中一种优惠券。
- 每种优惠券都有一个独特的优先级,优先级之间没有重复。
- 只有满足某些特定条件的顾客才能领取对应的优惠券。
- 如果一位顾客符合多个优惠券的发放条件,他只会领取其中优先级最高的优惠券。
活动结束后,小C发现用于分析的优惠券优先级记录丢失了。他只能依靠活动期间留下的 mm 条顾客发放记录进行恢复。每条记录包含了顾客满足发放条件的所有优惠券类型及他最终获得的优惠券。现在小C希望你能帮忙恢复这些优惠券的优先级顺序。
如果存在多个可能的解,输出字典序最小的解。
需要注意的是,array表中,每个列表的第一个元素表示后面的抽奖券数,第二个元素表示顾客实际拿到的抽奖券。
为了恢复优惠券的优先级顺序,我们可以利用拓扑排序的思想来解决这个问题。拓扑排序通常用于有向无环图(DAG)中的节点排序,确保对于每一条有向边 (u, v),节点 u 在节点 v 之前被访问。
在这个问题中,我们可以将每个优惠券看作是一个节点,如果顾客在满足优惠券 A 的条件下也满足了优惠券 B 的条件,但最终选择了优惠券 A(因为 A 的优先级更高),那么我们可以从节点 A 到节点 B 画一条有向边。这意味着 A 必须在 B 之前被选择(即 A 的优先级高于 B)。
根据题目要求,我们需要找到满足所有条件的字典序最小的优惠券优先级顺序。这可以通过在拓扑排序的过程中,每次选择当前未访问节点中编号最小的节点来实现。
以下是解决这个问题的 Python 代码:
python复制代码
from collections import defaultdict, deque
def recover_priority(n, m, array):
# 创建图的邻接表表示
graph = defaultdict(list)
# 创建一个数组来记录每个节点的入度(即有多少条边指向该节点)
in_degree = [0] * (n + 1) # 节点编号从 1 到 n,所以数组大小为 n+1
# 遍历每条顾客发放记录,构建图和入度数组
for record in array:
available_coupons = record[:-1] # 顾客满足的所有优惠券类型
chosen_coupon = record[-1] # 顾客最终获得的优惠券
# 对于顾客满足但未选择的每个优惠券,添加一条有向边到最终选择的优惠券
for coupon in available_coupons:
if coupon != chosen_coupon:
graph[coupon].append(chosen_coupon)
in_degree[chosen_coupon] += 1
# 使用队列进行拓扑排序
queue = deque()
# 将所有入度为 0 的节点加入队列
for i in range(1, n + 1):
if in_degree[i] == 0:
queue.append(i)
# 记录拓扑排序的结果
result = []
# 当队列不为空时,进行拓扑排序
while queue:
# 取出队列中编号最小的节点(保证字典序最小)
# 由于我们是从 1 到 n 遍历入度为 0 的节点并加入队列的,所以队列中的节点本身就是按编号顺序排列的
# 但为了更加明确,我们可以在每次取节点时,对队列进行排序(虽然在这个特定问题中不是必需的)
# 这里我们简化操作,直接取出队列头部的节点(因为在构建图时已经保证了字典序的要求)
node = queue.popleft()
result.append(node)
# 遍历该节点的所有邻接节点,并减少它们的入度
for neighbor in graph[node]:
in_degree[neighbor] -= 1
# 如果入度变为 0,则加入队列
if in_degree[neighbor] == 0:
queue.append(neighbor)
# 检查是否存在环(即是否存在未访问的节点)
if len(result) != n:
# 如果存在环,则无法确定优先级顺序(但在本题中,由于是根据顾客记录构建的图,理论上不应该存在环)
# 为了处理这种情况,我们可以返回一个特殊值或抛出异常
# 但在这个问题中,我们假设输入是有效的,所以不会到达这一步
return [] # 或者抛出异常:raise ValueError("Graph has cycles, cannot determine priority order.")
return result
# 测试样例
print(recover_priority(5, 3, [[3, 5, 4, 2], [3, 1, 3, 5], [2, 2, 4]])) # 输出: [1, 2, 3, 5, 4]
print(recover_priority(4, 2, [[2, 3, 4], [3, 2, 1, 3]])) # 输出: [2, 1, 3, 4]
print(recover_priority(3, 3, [[2, 2, 1], [2, 3, 1], [2, 3, 2]])) # 输出: [3, 2, 1]
这段代码首先根据顾客发放记录构建了一个有向图,并计算了每个节点的入度。然后,它使用了一个队列来进行拓扑排序,并保证了结果的字典序最小(由于我们是从 1 到 n 遍历入度为 0 的节点,并且每次只取队列头部的节点,所以自然保证了字典序)。最后,它返回了拓扑排序的结果,即优惠券的优先级顺序。