1230. 抛掷硬币
有一些不规则的硬币。在这些硬币中,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
解法一:二维 DP
思想
状态表示, f ( i , j ) f(i, j) f(i,j) 表示从第 1 1 1 个硬币到第 i i i 个硬币,总计 j j j 个硬币正面朝上的概率
此处 i , j i,j i,j 由 1 1 1 开始,题中 p r o b prob prob 的下标由 0 0 0 开始。也就是 f f f 的 i i i 对应 p r o b prob prob 的 i − 1 i − 1 i−1。
状态转移,若第 1 1 1 个硬币到第 i i i 个硬币有 j j j 个硬币正面朝上时
- 第 i i i 个硬币正面朝上,则第 1 1 1 个到第 i − 1 i - 1 i−1 个硬币中必须有 j − 1 j - 1 j−1 个硬币正面朝上。即 $prob(i - 1)f(i - 1, j - 1) $
- 第 i i i 个硬币反面朝上,则第 1 1 1 个到第 i − 1 i - 1 i−1 个硬币中必须有 j j j 个硬币正面朝上。即 [ 1 − p r o b ( i − 1 ) ] f ( i − 1 , j ) [1- prob(i - 1)]f(i - 1, j) [1−prob(i−1)]f(i−1,j)
状态计算
所以 f ( i , j ) f(i,j) f(i,j) 为以上两种情况的和,即 f ( i , j ) = p r o b ( i − 1 ) f ( i − 1 , j − 1 ) + [ 1 − p r o b ( i − 1 ) ] f ( i − 1 , j ) f(i,j) = prob(i-1)f(i-1,j-1) + [1-prob(i-1)]f(i-1,j) f(i,j)=prob(i−1)f(i−1,j−1)+[1−prob(i−1)]f(i−1,j)
复杂度
时间复杂度: O ( N M ) O(NM) O(NM)
空间复杂度: O ( N M ) O(NM) O(NM)
代码
class Solution {
public double probabilityOfHeads(double[] prob, int m) {
int n = prob.length;
double[][] f = new double[n + 1][m + 1];
f[0][0] = 1;
double p = 1;
for(int i = 1; i <= n; i++){ // 连续 i 个硬币反面朝上时·
p *= 1 - prob[i - 1];
f[i][0] = p;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
f[i][j] = f[i - 1][j - 1] * prob[i - 1] + f[i - 1][j] * (1 - prob[i - 1]);
}
}
return f[n][m];
}
}
解法二:一维 DP
思想
由解法一,不难发现本题实际上是一个 01 背包。所以可以使用 01 背包优化空间的套路。
直接降维,并对降维后的代码做等价变形,使得前后代码的计算顺序一致。
复杂度
时间复杂度: O ( M ) O(M) O(M)
空间复杂度: O ( M ) O(M) O(M)
代码
class Solution {
public double probabilityOfHeads(double[] prob, int m) {
int n = prob.length;
double[] f = new double[m + 1];
f[0] = 1 - prob[0];
if (m > 0) f[1] = prob[0];
for (int i = 1; i < n; i++) {
for (int j = m; j >= 0; j--) {
f[j] = f[j] * (1 - prob[i]);
if (j > 0) f[j] += f[j - 1] * prob[i];
}
}
return f[m];
}
}