题目描述
有一些不规则的硬币。在这些硬币中,prob[i]
表示第 i
枚硬币正面朝上的概率。
请对每一枚硬币抛掷 一次,然后返回正面朝上的硬币数等于 target
的概率。
样例
输入:prob = [0.4], target = 1
输出:0.40000
输入: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
内,则被视为正确答案。
算法
(动态规划) O(n×(target+1))
- f(i,j) 表示掷了前 i 个硬币,正面朝上次数为 j 的概率。
- 初始时,f(0,0)=1,其余为 0。
- 转移时,枚举这一次的结果,正面则 f(i,j)=f(i,j)+f(i−1,j−1)∗prob[i],反面 f(i,j)=f(i,j)+f(i−1,j)∗(1−prob[i])。注意这里的
prob
的下标是从 1 开始的。 - 最终答案为 f(n,target)。
时间复杂度
- 状态数共有 O(n×(target+1)) 个,转移时间为常数,故总时间复杂度为 O(n×(target+1))。
空间复杂度
- 和状态数相同,故时间复杂度为 O(n×(target+1))。
- 可以通过滚动数组优化到 O(target+1)。
C++ 代码
class Solution {
public:
double probabilityOfHeads(vector<double>& prob, int target) {
int n = prob.size();
vector<vector<double>> f(n + 1, vector<double>(target + 1, 0));
f[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= target; j++) {
if (j > 0)
f[i][j] += f[i - 1][j - 1] * prob[i - 1];
f[i][j] += f[i - 1][j] * (1 - prob[i - 1]);
}
return f[n][target];
}
};
Java版
class Solution { public double probabilityOfHeads(double[] prob, int target) { int n = prob.length;// 硬币的总数 // f[i][j] 表示前i枚硬币中有j枚朝上的概率 double[][] f = new double[n + 1][target + 1]; f[0][0] = 1; for (int i = 1; i <= n; i++){// 枚举第i枚硬币 for (int j = 0; j <= target; j++){// 枚举朝上次数 if (j > 0) f[i][j] += f[i-1][j-1] * prob[i - 1];// 第i枚硬币为正面 f[i][j] += f[i - 1][j] * (1 - prob[i - 1]);// 第i枚硬币为反面 } } return f[n][target]; } }