leetcode周赛:抛掷硬币
5090. 抛掷硬币
有一些不规则的硬币。在这些硬币中,prob[i]
表示第 i
枚硬币正面朝上的概率。
请对每一枚硬币抛掷 一次,然后返回正面朝上的硬币数等于 target
的概率。
示例 1:
输入:prob = [0.4], target = 1
输出:0.40000
示例 2:
输入:prob = [0.5,0.5,0.5,0.5,0.5], target = 0
输出:0.03125
提示:
1 <= prob.length <= 1000
0 <= prob[i] <= 1
0 <= target
<= prob.length
- 如果答案与标准答案的误差在
10^-5
内,则被视为正确答案。
解题思路
解法1(超时):
最开始我是想到用传统的排列组合方法来做的,因为我们在手算的时候也是这样,先从n个硬币中选target 个,让他们为正面,其他为反面,将概率相乘;最后将所有可能的组合相加起来。
但是这样做的话会超时。
解法2(动态规划):
虽然咋一看这道题跟不是求最优化的,貌似跟动态规划风马牛不相及,但其实仔细一想我们在用解法1的时候 其实是有很多重复计算在里面的,这倒是跟动态规划有关了。
状态矩阵:dp[i][j]
表示抛i
个硬币,有j
个向上的概率
状态转移方程:
dp[i][j + 1] += dp[i - 1][j] * p
dp[i][j] += dp[i - 1][j] * (1 - p)
解释:假设已知抛i - 1
个硬币有j
个向上的概率,那么在抛第i
个硬币的时候,还是j
个向上的概率就是第i
个硬币是向下的(1 - p)
;那么j + 1
个向上的概率就是第i个硬币是向上的(p)
。
但是我们要知道抛i
次有j
次向上的组合是有多种的,而最后的概率是这些组合各自的概率的相加,因此
状态转移方程是 +=
而不是 =
此外,我们初始化状态矩阵为全0
,且dp[0][0]
为1
,因为抛0
次只能有0
个向上,概率为1
我们最后得到的状态矩阵是每一行的和都为1
,因为每一行代表了抛i
次,而抛i
次的情况下共有0 ~ i
个
向上的可能,这些概率加起来的和为1
,而i+1
以后的概率都为0
.
也就是说我们最后得到的矩阵是一个下三角矩阵 (对角线以上的元素都为0
)
# -*- coding: utf-8 -*-
# @Time : 2019-10-20 16:07
# @Author : Jayce Wong
# @ProjectName : job
# @FileName : tossCoin.py
# @Blog : https://sysujayce.github.io/
# @Github : https://github.com/SysuJayce
class Solution:
def probabilityOfHeads(self, prob, target: int) -> float:
# 这里对0个向上的情况特殊考虑,因为这个case的计算简单,没必要通过dp,仅仅是为了加速
if target == 0:
ans = 1
for p in prob:
ans *= 1 - p
return ans
length = len(prob) + 1 # 由于我们第一维代表抛的次数,为了保持统一,将下标加一
dp = [[0] * length for _ in range(length)]
dp[0][0] = 1 # 抛0次的话只能有0个为正
# 这里我们通过enumerate来遍历,顺便将下标的起始设为1,保持了状态矩阵各个元素的意义
for i, p in enumerate(prob, 1):
# 抛i次硬币可能会有0, 1, 2, ..., i - 1, i个向上,因此都需要计算
for j in range(i):
# 注意这里状态转移方程是 += ,不是 =
dp[i][j] += dp[i - 1][j] * (1 - p)
dp[i][j + 1] += dp[i - 1][j] * p
return dp[-1][target] # 最后抛n次,有target个向上的概率就是我们要的
def main():
solution = Solution()
prob = [0.2, 0.8, 0, 0.3, 0.5]
target = 3
print(solution.probabilityOfHeads(prob, target))
if __name__ == '__main__':
main()
- 原文作者:Jayce
- 原文链接:https://sysujayce.github.io/posts/tosscoin/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。