【剑指 Offer 题解】60. n 个骰子的点数

题目描述

把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;
}
发布了18 篇原创文章 · 获赞 0 · 访问量 508

猜你喜欢

转载自blog.csdn.net/qingqingxiangyang/article/details/104294248
今日推荐