一、题目
有n 把钥匙,m 个锁,每把锁只能由一把特定的钥匙打开,其他钥匙都无法打开。一把钥匙可能可以打开多把锁,钥匙也可以重复使用。 对于任意一把锁来说,打开它的钥匙是哪一把是等概率的。但你无法事先知道是哪一把钥匙,只能进行尝试。 已知每次尝试用第i把钥匙打开第j把锁会消耗的时间a ij 秒。 问最优策略下打开所有锁的总期望时间是多少秒。
输入描述 第一行两个以空格分隔的正整数n,m。 接下来m行每行m个空格分隔的正整数aij。 1<=n,m,aij <=500
输出描述 输出一个小数代表答案,你的答案会被认为是正确的当且仅当你的答案与正确答案的绝对误差或相对误差不超过10-6。
二、分析
这个问题涉及到寻找一种最优策略以最小化在平均情况下打开所有锁所需的总时间。具体来说,我们有n把钥匙和m个锁,每把锁由且仅由一把特定的钥匙打开,但每把钥匙可能用于打开多把锁。我们无法事先知道哪把钥匙能打开哪把锁,只能通过尝试来确定。每次尝试用钥匙i打开锁j会消耗时间a_ij秒。我们的目标是在最优策略下计算打开所有锁的总期望时间。这个问题可以通过为每个锁单独寻找最优的钥匙尝试顺序来解决。由于每个锁的正确钥匙是等概率分布的,每个锁的处理可以独立进行。对于每个锁来说,最优的策略是按钥匙的尝试时间从小到大进行排序,这样的顺序能最小化期望时间。
对于每个锁,正确钥匙的位置是均匀分布的,因此期望时间可以通过加权和来计算,其中每个钥匙的权重是其在尝试顺序中的位置。具体来说,对于每个锁j,我们对钥匙按a_ij从小到大排序,然后计算排序后的加权和,其中第i小的钥匙的权重为n-i+1(即从n到1递减)。总期望时间是所有锁的加权和的平均值,即所有锁的加权和相加后除以n。首先读取输入的n和m,然后读取每个锁对应的n个尝试时间。对于每个锁,对尝试时间进行排序,然后计算加权和。最后,将所有锁的加权和相加,并除以n得到总期望时间。代码实现中,我们首先读取输入数据,然后对每个锁的尝试时间进行排序,计算每个锁的加权和,最后累加所有锁的加权和并除以n得到结果。这个方法有效地利用了贪心策略,确保每个锁的处理都是最优的,从而使得总期望时间最小化。
具体来说,对于每把锁j,将它的n个尝试时间a_ij进行升序排序。然后计算排序后的加权和,即第i小的时间乘以权重(n-i+1),并将所有锁的加权和累加起来,最后除以n得到总期望时间。这种方法确保了每个锁的处理都是最优的,从而整体上最小化了期望时间。
三、代码
算法步骤
-
输入处理:读取n(钥匙数)和m(锁数),以及每个锁对应的n个尝试时间。
-
排序:对每个锁的尝试时间进行升序排序。
-
加权和计算:对排序后的每个锁,计算加权和,其中权重从n到1递减。
-
总期望时间:将所有锁的加权和相加后除以n,得到总期望时间。
def main():
import sys
input = sys.stdin.read().split()
ptr = 0
T = int(input[ptr])
ptr += 1
for _ in range(T):
t = int(input[ptr])
ptr += 1
state = list(map(float, input[ptr:ptr+3]))
ptr += 3
P = []
for i in range(3):
row = list(map(float, input[ptr:ptr+3]))
P.append(row)
ptr += 3
current_state = state.copy()
for _ in range(t-1):
new_state = [0.0]*3
for j in range(3):
for k in range(3):
new_state[j] += current_state[k] * P[k][j]
current_state = new_state
if current_state[2] > 0.5:
print(1)
else:
print(0)
if __name__ == "__main__":
main()
-
输入处理:读取钥匙数n和锁数m,以及每个锁对应的n个尝试时间。排序:对每个锁的尝试时间进行排序,确保较小的时间排在前面。加权和计算:每个钥匙的尝试时间乘以其权重(从n到1),累加得到该锁的总加权和。总期望时间:所有锁的加权和相加后除以n,得到最优策略下的总期望时间。
通过这种方法,我们确保每个锁的处理都是最优的,从而最小化总期望时间。