开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
有 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 的范围内将被认为是正确的。
示例 1:
输入: n = 50
输出: 0.62500
解释: 如果我们选择前两个操作,A 首先将变为空。
对于第三个操作,A 和 B 会同时变为空。
对于第四个操作,B 首先将变为空。
所以 A 变为空的总概率加上 A 和 B 同时变为空的概率的一半是 0.25 *(1 + 1 + 0.5 + 0)= 0.625。
示例 2:
输入: n = 100
输出: 0.71875
提示:
0 <= n <= 10^9
思路
本题可以使用动态规划 + 记忆化搜索解题。由于四种操作都是25的倍数,因此我们可以将n除以25,并将四种操作变为(4,0)、(3,1)、(2,2)、(1,3),每种操作概率均为0.25。
设汤A和汤B当前的量分别为a和b,初始化a = b = n,一次操作后a和b的量有四种可能:
- 提供
100ml的 汤A 和0ml的 汤B,a = a - 4,b = b - 提供
100ml的 汤A 和0ml的 汤B,a = a - 3,b = b - 1 - 提供
50ml的 汤A 和50ml的 汤B,a = a - 2,b = b - 2 - 提供
25ml的 汤A 和75ml的 汤B,a = a - 1,b = b - 3
用f(a, b)表示汤A 先分配完的概率 + 汤A和汤B 同时分配完的概率 / 2,
f(a, b) = 0.25* (f(a - 4, b) + f(a - 3, b - 1) + f(a - 2, b - 2) + f(a - 1, b - 3))
可以用dp[a][b]存储f(a, b)的值,来避免动态规划过程中存在的重复计算的问题,优化解题过程。
解题
/**
* @param {number} n
* @return {number}
*/
var soupServings = function (n) {
n = Math.ceil(n / 25);
if (n >= 179) {
return 1;
}
const dp = new Array(n + 1).fill(null).map((_) => new Array(n + 1).fill(0));
const dfs = (a, b) => {
if (a <= 0 && b <= 0) {
return 0.5;
} else if (a <= 0) {
return 1;
} else if (b <= 0) {
return 0;
}
if (dp[a][b] === 0) {
dp[a][b] =
0.25 *
(dfs(a - 4, b) +
dfs(a - 3, b - 1) +
dfs(a - 2, b - 2) +
dfs(a - 1, b - 3));
}
return dp[a][b];
};
return dfs(n, n);
};