1230. 抛掷硬币

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 i1

状态转移,若第 1 1 1 个硬币到第 i i i 个硬币有 j j j 个硬币正面朝上时

  • i i i 个硬币正面朝上,则第 1 1 1 个到第 i − 1 i - 1 i1 个硬币中必须有 j − 1 j - 1 j1 个硬币正面朝上。即 $prob(i - 1)f(i - 1, j - 1) $
  • i i i 个硬币反面朝上,则第 1 1 1 个到第 i − 1 i - 1 i1 个硬币中必须有 j j j 个硬币正面朝上。即 [ 1 − p r o b ( i − 1 ) ] f ( i − 1 , j ) [1- prob(i - 1)]f(i - 1, j) [1prob(i1)]f(i1,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(i1)f(i1,j1)+[1prob(i1)]f(i1,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];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值