• leetcode *808. 分汤(2022.11.21)


    【题目】*808. 分汤

    有 A 和 B 两种类型 的汤。一开始每种类型的汤有 n 毫升。有四种分配操作:

    提供 100ml 的 汤A 和 0ml 的 汤B 。
    提供 75ml 的 汤A 和 25ml 的 汤B 。
    提供 50ml 的 汤A 和 50ml 的 汤B 。
    提供 25ml 的 汤A 和 75ml 的 汤B 。
    当我们把汤分配给某人之后,汤就没有了。每个回合,我们将从四种概率同为 0.25 的操作中进行分配选择。如果汤的剩余量不足以完成某次操作,我们将尽可能分配。当两种类型的汤都分配完时,停止操作。

    注意 不存在先分配 100 ml 汤B 的操作。

    需要返回的值: 汤A 先分配完的概率 + 汤A和汤B 同时分配完的概率 / 2。返回值在正确答案 1 0 − 5 10^{-5} 105 的范围内将被认为是正确的。

    示例 1:

    输入: n = 50
    输出: 0.62500
    解释:如果我们选择前两个操作,A 首先将变为空。
    对于第三个操作,A 和 B 会同时变为空。
    对于第四个操作,B 首先将变为空。
    所以 A 变为空的总概率加上 A 和 B 同时变为空的概率的一半是 0.25 *(1 + 1 + 0.5 + 0)= 0.625。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例 2:

    输入: n = 100
    输出: 0.71875
    
    • 1
    • 2

    提示:
    0 <= n <= 109​​​​​​​

    【解题思路1】动态规划

    首先,由于四种分配操作都是 25 的倍数,因此我们可以将 n 除以 25(如果有余数,则补 1),并将四种分配操作变为 (4,0),(3,1),(2,2),(1,3),且每种操作的概率均为 0.25。

    当 n 较小时,我们可以用动态规划来解决这个问题。设 dp(i,j) 表示汤 A 和汤 B 分别剩下 i 和 j 份时所求的概率值,即汤 A 先分配完的概率 + 汤 A 和汤 B 同时分配完的概率除以 2 。 状态转移方程为:

    dp(i,j)=1/4×( dp(i−4,y)+dp(i−3,y−1)+dp(i−2,y−2)+dp(i−1,y−3) )

    我们仔细考虑一下边界条件:

    当满足 i>0,j=0,此时无法再分配,而汤 A 还未分配完成,汤 A 永远无法完成分配,此时 dp(i,j)=0;

    当满足 i=0,j=0,此时汤 A 和汤 B 同时分配完的概率为 1.0,汤 A 先分配完的概率为 0,因此 dp(i,j)=1.0×0.5=0.5;

    当满足 i=0,j>0,此时无法再分配,汤 A 先分配完的概率为 1.0,汤 B 永远无法完成分配,因此 dp(i,j)=1.0;

    因此综上,我们可以得到边界条件如下:

    d p ( i , j ) = { 0 , i>0, j=0 0.5 , i=0, j=0 1 , i=0, j>0 ​ dp(i,j)=

    {0,i>0, j=00.5,i=0, j=01,i=0, j>0" role="presentation">{0,i>0, j=00.5,i=0, j=01,i=0, j>0
    dp(i,j)=0,0.5,1,i>0, j=0i=0, j=0i=0, j>0

    我们可以看到这个动态规划的时间复杂度是 O(n2),即使将 n 除以 25 之后,仍然没法在短时间内得到答案,因此我们需要尝试一些别的思路。 我们可以发现,每次分配操作有 (4,0),(3,1),(2,2),(1,3) 四种,那么在一次分配中,汤 A 平均会被分配的份数期望为 E(A)=(4+3+2+1)×0.25=2.5,汤 B 平均会被分配的份数期望为 E(B)=(0+1+2+3)×0.25=1.5。因此在 n 非常大的时候,汤 A 会有很大的概率比 B 先分配完,汤 A 被先取完的概率应该非常接近 1。事实上,当我们进行实际计算时发现,当 n≥4475 时,所求概率已经大于 0.99999 了(可以通过上面的动态规划方法求出),它和 1 的误差(无论是绝对误差还是相对误差)都小于 1 0 − 5 10^{−5} 105
    。实际我们在进行测算时发现:

    n=4475,p≈0.999990211307
    n=4476,p≈0.999990468596
    因此在 n≥179×25n 时,我们只需要输出 1 作为答案即可。在其它的情况下,我们使用动态规划计算出答案。

    class Solution {
        public double soupServings(int n) {
            n = (int) Math.ceil((double) n / 25);
            if (n >= 179) {
                return 1.0;
            }
            double[][] dp = new double[n + 1][n + 1];
            dp[0][0] = 0.5;
            for (int i = 1; i <= n; i++) {
                dp[0][i] = 1.0;
            }
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    dp[i][j] = (dp[Math.max(0, i - 4)][j] + dp[Math.max(0, i - 3)][Math.max(0, j - 1)] + dp[Math.max(0, i - 2)][Math.max(0, j - 2)] + dp[Math.max(0, i - 1)][Math.max(0, j - 3)]) / 4.0;
                }
            }
            return dp[n][n];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    【解题思路2】DFS

    class Solution {
        private double[][] memo;
    
        public double soupServings(int n) {
            n = (int) Math.ceil((double) n / 25);
            if (n >= 179) {
                return 1.0;
            }
            memo = new double[n + 1][n + 1];
            return dfs(n, n);
        }
    
        public double dfs(int a, int b) {
            if (a <= 0 && b <= 0) {
                return 0.5;
            } else if (a <= 0) {
                return 1;
            } else if (b <= 0) {
                return 0;
            }
            if (memo[a][b] == 0) {
                memo[a][b] = 0.25 * (dfs(a - 4, b) + dfs(a - 3, b - 1) + dfs(a - 2, b - 2) + dfs(a - 1, b - 3));
            }
            return memo[a][b];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    艾美捷重组蛋白酶K,无动物源/AF特异性分析
    第03章 Tableau基础操作
    Updating FreeBSD repository catalogue...
    FDCAN硬件过滤器详解
    深入剖析 Python 最常用数据结构:列表(List) & 元组(Tuple)
    【SpringMVC】深度理解DispatcherServlet
    2019年亚太杯APMCM数学建模大赛A题基于图像分析的二氧化硅熔化表示模型求解全过程文档及程序
    使用Intellij IDEA远程debug服务器Java代码
    基于MUSIC算法的二维超声波成像matlab仿真
    第十三届蓝桥杯 C++ B 组省赛 G 题———积木画(AC)
  • 原文地址:https://blog.csdn.net/XunCiy/article/details/127956729