有 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。返回值在正确答案
10-5
的范围内将被认为是正确的。
好难…是我做不出来的题 至少也努力看题解并总结了
思路:
由于四种分配方式都是25的倍数,因此可以将n除以25,并向上取整。那么四类操作可以等价为
当n较小时使用动态规划,求出最终概率,定义dp[i][j]为初始状态当汤A有i毫升,汤B有j毫升时,概率p为dp[i][j]。
比如,当n=4时,我们所需要返回的结果即为dp[4][4],其概率取决于dp[0][4]、dp[1][3]、dp[2][2]、dp[3][1]
d
p
[
4
]
[
4
]
=
(
d
p
[
0
]
[
4
]
+
d
p
[
1
]
[
3
]
+
d
p
[
2
]
[
2
]
+
d
p
[
3
]
[
1
]
)
/
4
dp[4][4] =(dp[0][4]+dp[1][3]+dp[2][2]+dp[3][1])/4
dp[4][4]=(dp[0][4]+dp[1][3]+dp[2][2]+dp[3][1])/4
因此,从dp[0][0]到dp[n][n]的过程,可以想象成一个乘汤的过程
当n足够大的时候,根据汤 A平均会被分配的份数期望为 E ( A ) = ( 4 + 3 + 2 + 1 ) × 0.25 = 2.5 E(A)=(4+3+2+1)×0.25=2.5 E(A)=(4+3+2+1)×0.25=2.5 ,汤 B 平均会被分配的份数期望为 E ( B ) = ( 0 + 1 + 2 + 3 ) × 0.25 = 1.5 E(B)=(0+1+2+3)×0.25=1.5 E(B)=(0+1+2+3)×0.25=1.5。因此在 n 非常大的时候,汤 A会有很大的概率比 B先分配完,汤 A被先取完的概率应该非常接近 1。由于返回值在正确答案 10−5的范围内将被认为是正确的,通过实际计算可以确定当 n ≥ 179 ∗ 25 n\geq179*25 n≥179∗25时,只需输出1作为答案即可
记概率 p p p= 汤A 先分配完的概率 + 汤A和汤B** 同时分配完的概率 / 2
确定dp数组(dp table)以及下标的含义
dp[i][j]:汤A剩余i毫升,汤B剩余j毫升时所需要的概率大小, 即汤A 先分配完的概率 + 汤A和汤B 同时分配完的概率 / 2
或者说初始状态当汤A有i毫升,汤B有j毫升时,概率p为dp[i][j]
确定递推公式
当数组下标越界时,将下标视为0
d
p
[
i
]
[
j
]
=
(
d
p
[
i
−
4
]
[
j
]
+
d
p
[
i
−
3
]
[
j
−
1
]
+
d
p
[
i
−
2
]
[
j
−
2
]
+
d
p
[
i
−
1
]
[
j
−
3
]
)
/
4
dp[i][j] =(dp[i-4][j]+dp[i-3][j-1]+dp[i-2][j-2]+dp[i-1][j-3])/4
dp[i][j]=(dp[i−4][j]+dp[i−3][j−1]+dp[i−2][j−2]+dp[i−1][j−3])/4
如何初始化
确定遍历顺序
由dp公式可知,外层正序遍历i,内层正序遍历j
举例推导dp数组
代码 :DP 自下而上 计算了许多不需要的结果
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 j = 1; j <= n; j++) {
dp[0][j] = 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];
}
}
复杂度
减少许多无效状态的计算
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];
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/soup-servings/solutions/1981704/fen-tang-by-leetcode-solution-0yxs/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。