要求:把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
思路:动态规划
1、n个骰子的点数和的最小值为n,最大值为6n;
2、n个骰子的所有点数的排列数为。
3、要解决这个问题,需要先统计出每个点数出现的次数,然后把每个点数出现的次数除以,就能求出每个点数出现的概率;
4、 表示状态,设F(n,s)为当骰子数为n,和为s的情况数量。
5、状态转移方程,使用 dp[n][j] 表示投完 n 枚骰子后总点数为 j 的出现次数,那么我们考虑递推关系式,投第 n 枚骰子可以由投第 n-1 枚骰子转换而来,也就是如果投第 n-1 枚骰子后总点数为 j-i (1 ≤ i ≤ 6),那么第 n 次掷出 i 即可满足条件。
当n=1时,F(1,s)=1,其中s=1,2,3,4,5,6
当n≥2时,F(n,s)=F(n−1,s−1)+F(n−1,s−2)+F(n−1,s−3)+F(n−1,s−4)+F(n−1,s−5)+F(n−1,s−6)
数学表达式为:
6、边界处理,就是第一阶段的状态:投掷完 11枚骰子后,它的可能点数分别为 1,2,3,...,6 ,并且每个点数出现的次数都是 1。
for (int i = 1; i <= 6; i ++) dp[1][i] = 1;
public double[] twoSum(int n) {
// +1是因为其实从1开始,即丢骰子从第一次开始,没有0
int dp[][]=new int[n+1][6*n+1];//表示i个骰子掷出s点的次数
for(int i=1;i<=6;i++) {
dp[1][i]=1;//表示一个骰子掷出i点的次数为1
}
for(int i=2;i<=n;i++) {//表示骰子的个数
for(int s=i;s<=6*i;s++) {//表示可能会出现的点数之和
for(int j=1;j<=6;j++) {//表示当前这个骰子可能掷出的点数
if(s-j<i-1) {//当总数还没有j大时,就不存在这种情况
break;
}
dp[i][s]+=dp[i-1][s-j];//当前n个骰子出现的点数之和等于前一次出现的点数之和加上这一次出现的点数
}
}
}
double total = Math.pow((double)6,(double)n);//掷出n次点数出现的所有情况
double[] ans = new double[5*n+1];//因为最大就只会出现5*n+1中点数
for(int i=n;i<=6*n;i++){
ans[i-n]=((double)dp[n][i])/total;//第i小的点数出现的概率
}
return ans;
}
网友评论