题目描述
把n个骰子仍在地上,所有的骰子朝上的一面的点数之和为s,输入n,打印出s所有可能的值出现的概率。
示例
一个骰子的情况
Input: n = 1
Output: [[1, 0.17], [2, 0.17], [3, 0.17], [4, 0.17], [5, 0.17], [6, 0.17]]
两个骰子的情况
Input: n = 2
Output: [[2,0.03],[3,0.06],[4,0.08],[5,0.11],[6,0.14],[7,0.17],[8,0.14],[9,0.11],[10,0.08],[11,0.06],[12,0.03]]
思路
1、问题转换,先求n个骰子点数和为s出现的次数
递归
1、n个骰子点数和为s出现的次数,等于n-1个骰子点数和为 s - 1 的次数 + 点数和为 s - 2 的次数 + … + 点数和为 s - 6 的次数。
2、一个骰子,点数和为1,2,…,6的次数为1。
- 递归公式:
f(n,s) = f(n-1, s-1) + f(n-1, s-2) + ... + f(n - 1, s - 6)
- 递归终止条件:
f(1,1) = f(1, 2) = ... = f(1, 6) = 1
public int count(int n, int s) {
if (n < 1 || s < n || s > 6 * n) /* 非法数据 */
return 0;
if (n == 1)
return 1;
return count(n-1, s-1) + count(n-1, s-2) + count(n - 1, s - 3) + count(n - 1, s - 4) + count(n - 1, s - 5) + count(n - 1, s - 6);
}
动态规划
1、dp[i][j]
表示i个骰子点数和为j的次数
2、初始值,一个骰子,点数和为1,2,…,6的次数为1
3、i个骰子,最小点数和为i
,最大点数和为 6*i
public int count(int n, int s) {
int[][] dp = new int[n + 1][6 * n + 1];
for (int j = 1; j <= 6; j++) { /* 一个骰子的情况 */
dp[1][j] = 1;
}
for (int i = 2; i <= n; i++) {
for (int j = i; j <= 6 * n; j++) {
for (int k = 1; k <= 6 && k < j; k++) { // dp[i][j] = dp[i-1][j-1] + dp[i-1][j-2] + dp[i-1][j-3] + dp[i-1][j-4] + dp[i-1][j-5] + dp[i-1][j-6];
dp[i][j] += dp[i-1][j-k];
}
}
}
return dp[n][s];
}
2、聚合,统计所有s的情况,及出现的概率
- 掷骰子总情况数
totalCount = n ^ 6
- n个骰子,最小点数和为
n
,最大点数和为6*n
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
List<Map.Entry<Integer, Double>> result = new ArrayList<>();
double totalCount = Math.pow(n, 6);
for (int s = n; s <= 6 * n; s++) {
result.add(new AbstractMap.SimpleEntry<>(s, count(n, s) / totalCount));
}
return result;
}
最终版本
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
int[][] dp = new int[n + 1][6 * n + 1];
for (int j = 1; j <= 6; j++) { /* 一个骰子的情况 */
dp[1][j] = 1;
}
for (int i = 2; i <= n; i++) {
for (int j = i; j <= 6 * n; j++) {
for (int k = 1; k <= 6 && k < j; k++) { // dp[i][j] = dp[i-1][j-1] + dp[i-1][j-2] + dp[i-1][j-3] + dp[i-1][j-4] + dp[i-1][j-5] + dp[i-1][j-6];
dp[i][j] += dp[i-1][j-k];
}
}
}
List<Map.Entry<Integer, Double>> result = new ArrayList<>();
double totalCount = Math.pow(n, 6);
for (int s = n; s <= 6 * n; s++) {
result.add(new AbstractMap.SimpleEntry<>(s, dp[n][s] / totalCount));
}
return result;
}