问题描述
小U和小S分别拥有不同面数的骰子,每个骰子的面数不同,摇到每一面的概率均等。两人同时摇各自的骰子,并将摇出的点数相加,得分较高的一方获胜,得分相同则为平局。我们需要计算小U获胜的概率。
关键点
- 骰子的面数:每个骰子的面数不同,范围在1到k之间。
- 概率均等:每个面出现的概率是
1/a_i或1/b_j。 - 得分计算:两人分别摇骰子,并将点数相加,得分较高的一方获胜。
解题思路
1. 理解问题
我们需要计算小U获胜的概率。为了做到这一点,我们需要知道两人所有可能的得分组合,并计算小U得分高于小S的概率。
2. 数据结构选择
- 动态规划:使用动态规划来计算每个骰子组合的得分概率分布。
- 概率分布:计算每个可能得分出现的概率,然后通过概率分布来计算获胜的概率。
3. 算法步骤
-
计算小U的得分概率分布:
- 初始化一个数组
u_prob_dist,表示小U所有可能得分的概率分布。 - 对于每个骰子,更新
u_prob_dist,计算新的得分概率分布。
- 初始化一个数组
-
计算小S的得分概率分布:
- 初始化一个数组
s_prob_dist,表示小S所有可能得分的概率分布。 - 对于每个骰子,更新
s_prob_dist,计算新的得分概率分布。
- 初始化一个数组
-
计算小U获胜的概率:
- 遍历小U的所有可能得分和小S的所有可能得分,计算小U得分高于小S的概率。
动态规划计算得分概率分布
假设小U有两个骰子,面数分别为 a1 和 a2,我们可以通过动态规划来计算所有可能得分的概率分布。
-
初始状态:
u_prob_dist = [1],表示只有一个得分0的概率为1。 -
第一个骰子:假设
a1 = 3,更新u_prob_dist:- 新的得分概率分布为
[1/3, 1/3, 1/3]。
- 新的得分概率分布为
-
第二个骰子:假设
a2 = 4,更新u_prob_dist:- 新的得分概率分布为
[1/12, 1/6, 1/4, 1/6, 1/12]。
- 新的得分概率分布为
计算获胜概率
遍历小U和小S的所有可能得分,计算小U得分高于小S的概率。
代码详解
def solution(n, m, arrayN, arrayM):
# 计算小U所有可能得分的概率分布
u_prob_dist = [1] # 初始概率分布,只有一个得分0的概率为1
for a in arrayN:
new_prob_dist = [0] * (len(u_prob_dist) + a - 1)
for score, prob in enumerate(u_prob_dist):
for face in range(1, a + 1):
new_prob_dist[score + face] += prob / a
u_prob_dist = new_prob_dist
# 计算小S所有可能得分的概率分布
s_prob_dist = [1] # 初始概率分布,只有一个得分0的概率为1
for b in arrayM:
new_prob_dist = [0] * (len(s_prob_dist) + b - 1)
for score, prob in enumerate(s_prob_dist):
for face in range(1, b + 1):
new_prob_dist[score + face] += prob / b
s_prob_dist = new_prob_dist
# 计算小U获胜的概率
u_wins = 0
for u_score, u_prob in enumerate(u_prob_dist):
for s_score, s_prob in enumerate(s_prob_dist):
if u_score > s_score:
u_wins += u_prob * s_prob
return round(u_wins, 3)
if __name__ == "__main__":
# Add your test cases here
print(solution(1, 3, [8], [2, 3, 4]) == 0.255)
代码解释
-
初始化概率分布:
u_prob_dist = [1]和s_prob_dist = [1]表示初始状态下只有一个得分0的概率为1。
-
动态规划更新概率分布:
- 对于每个骰子,更新概率分布数组,计算新的得分概率分布。
-
计算获胜概率:
- 遍历小U和小S的所有可能得分,计算小U得分高于小S的概率。
总结
在实现这个计算小U获胜概率的程序过程中,我深刻体会到了动态规划在解决这类问题中的强大能力。通过构建得分概率分布,我们能够有效地避免了暴力解法中的重复计算,显著提高了算法的效率。