题目链接
题目(中等)
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数 s 的概率。
示例
示例 1:
输入:n = 1
输出:[0.16667,0.16667,0.16667]
示例 2:
输入:n = 2
输出:[0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]
提示:
- 1 <= n <= 11
题解
动态规划
- 时间复杂度 O(n^2)
- 空间复杂度 O(n)
/**
* @param {number} n
* @return {number[]}
*/
var statisticsProbability = function(n) {
// 二维数组,n 个骰子所以有 n 行,而每一行代表有 n 个骰子时,各值的出现次数,即如果2个骰子,各值的取值范围是 2 ~ 12(2 * 6),出现次数取决于上一层的值(1 ~ 6)
// n + 1 的原因是,第 0 行浪费啦,为了便于理解,第一个骰子对应第一行,而不是第 0 行
// 每一行值范围是 n * 6 + 1 的原因跟上面一样,比如 2 个骰子,取值从 2 开始,到 12 结束,所以要多开一列,容纳 12 这个下标
let dp = new Array(n + 1).fill().map(() => new Array(n * 6 + 1).fill(0)), result = [];
// 初始化第一行,即一个骰子时,取值都为 1,因为出现次数只为 1 次,且各值取值只能为 1 ~ 6
for(let i = 1; i <= 6; i++) {
dp[1][i] = 1;
}
// 直接计算从第二个骰子开始,一直到第 n 个骰子的 dp
for(let i = 2; i <= n; i++) {
// 那一层骰子取值范围是 i 到 6 * i,例如第二个骰子,范围是 [2, 12]
for(let j = i; j <= 6 * i; j++) {
// 每个取值时,比如 4 这个取值,可能是上一次是 1 这次是 3,也可能上次 2 这次 2,也可能上次 3 这次 1,所以要 1-6 都遍历一次
for(let cur = 1; cur <= 6; cur++) {
// 出现 = 时只有一种情况,出现点数 0 ,不可能,所以也要排除,而对于 < ,如果此时要取 3 这个值的出现次数,前面的数如果是 4,5,6 这种大过它的也不可能
if(j <= cur)
break;
// 每个取值时,比如 4 这个取值,可能是上一次是 1 这次是 3,也可能上次 2 这次 2,也可能上次 3 这次 1,所以要累加
dp[i][j] += dp[i - 1][j - cur];
}
}
}
let all = Math.pow(6, n);
// 总数为 Math.pow(6, n),即每个骰子都可能出现 [1, 6] 的情况,n 个 [1, 6] 就是 6^n
// dp[n][i] 代表第 n 层的 i 取值出现次数,即 dp[2][12] 代表第二层时 12 的出现次数
// 只取那一层对应索引范围内的出现次数即可,同时除以总数就是平均概率
for(let i = n; i <= n * 6; i++) {
result.push(dp[n][i] / all);
}
return result;
};