【题目】:
把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,打印出S的所有可能的值出现的概率。
一般来说骰子点数为1~6,n个筛子的点数之和可以这样理解:第n个骰子可能出现的数与前面(n-1)个骰子和的和,前面(n-1个骰子)的和为第(n-1)个骰子可能出现的数与前面(n-2)个骰子和的和。。以此类推。
【解决】
① 递归求解
第一次:将n个骰子分成两堆,第一堆只有1个骰子,第二堆有n-1个骰子
这1个骰子有6种情况,和剩下的n-1个骰子点数相加
第二次:继续把剩下的n-1个骰子分成两堆,一堆1个,一堆n-2个...
同样,这一个骰子还是要分成6种情况与剩下的n-2个骰子的点数相加
同时,这一次还要把这颗骰子的点数与上一颗骰子的点数相加,之后再递归调用下一次分割。
直到:此次递归调用还剩1颗骰子时,
我们用一个probabilities[]数组保存所有点数之和的情况,出现的次数
在对应的probabilities【当前点数之和sum】++;
public class Solution {
/**
* 把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,打印出S的所有可能的值出现的概率。
* 在以下求解过程中,我们把骰子看作是有序的。
* 例如当n=2时,我们认为(1,2)和(2,1)是两种不同的情况
*/
public static void main(String[] args) {
int n = 2;
printProbabilityOfDice(n);//solution 1,递归方式
}
public static void printProbabilityOfDice(int n){
if (n < 1) return;
double total = Math.pow(6,n);//分母,每一个骰子都有6种可能的值,总共的可能右n个6的乘积的个数
int len = n * 6 - n * 1 + 1;//n个骰子的和的范围在n * 1到n * 6之间。
int[] times = new int[len];//记录每个和出现的次数,即分子的大小,和sum存储在下标为sum-n的位置
for (int i = 1;i <= 6;i ++){//初始化第一个骰子,有6种可能的值
probabilityOfDice(n,i,n,0,times);//计算每种和的出现的次数
}
for (int i = 0;i < len;i ++){//打印每种和出现的概率
System.out.println("和:" + (i + n) + "出现的次数:" + times[i] + "/" + total);
}
}
public static void probabilityOfDice(int n,int curDiceVal,int numOfDices,int cursum,int[] times){
if (numOfDices == 1){//剩余骰子的个数
int sum = cursum + curDiceVal;
times[sum - n] ++;//得到了一个和,增加其出现的次数
}else {
int sum = cursum + curDiceVal;
for (int i = 1;i <= 6;i ++){//当前骰子又有6种选择,递归进行计算
probabilityOfDice(n,i,numOfDices - 1,sum,times);
}
}
}
}
② 动态规划
public class Solution {
/**
* 有k-1个骰子时,再增加一个骰子,这个骰子的点数只可能为1、2、3、4、5或6。那k个骰子得到点数和为n的情况有:
* (k-1,n-1):第k个骰子投了点数1
* (k-1,n-2):第k个骰子投了点数2
* (k-1,n-3):第k个骰子投了点数3
* ....
* (k-1,n-6):第k个骰子投了点数6
* 在k-1个骰子的基础上,再增加一个骰子出现点数和为n的结果只有这6种情况!
* 所以:f(k,n)=f(k-1,n-1)+f(k-1,n-2)+f(k-1,n-3)+f(k-1,n-4)+f(k-1,n-5)+f(k-1,n-6)
* 初始化:有1个骰子,f(1,1)=f(1,2)=f(1,3)=f(1,4)=f(1,5)=f(1,6)=1。
*
* 使用dp[i][j]表示i个骰子的和为j出现的次数,dp[1][1] = dp[2][2] = ... = 1;
* dp[i][j] = dp[i - 1][n - 1] + dp[i - 1][n - 2] + ... + dp[i - 1][n - 6];
*/
public static void main(String[] args) {
int n = 2;
printProbabilityOfDice(n);//solution 2,动态规划
}
public static void printProbabilityOfDice(int n){
if (n < 1) return;
double total = Math.pow(6,n);//分母
int maxSum = 6 * n;
int[][] dp = new int[n + 1][6 * n + 1];
for (int i = 1;i <= 6;i ++){//初始化
dp[1][i] = 1;
}
for (int i = 2;i <= n;i ++){
for (int j = n;j <= maxSum;j ++){
for (int k = 1;j - k >= 1 && k <= 6;k ++){
dp[i][j] += dp[i - 1][j - k];//
}
}
}
for (int j = n;j <= maxSum;j ++){
System.out.println("和为:" + j + "出现的次数为" + dp[n][j] + "/" + total);
}
}
}