题目:
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,
其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。
-- -- -- -- -- -- -- -- -
示例 :
输入: 1
输出: [ 0.16667 , 0.16667 , 0.16667 , 0.16667 , 0.16667 , 0.16667 ]
输入: 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
-- -- -- -- -- -- -- -- -- -- -- --
思考:
设输入 n 个骰子的解(即概率列表)为 f ( n) ,其中「点数和」 x 的概率为 f ( n, x) 。
如下图所示,为 n = 2 , x = 7 的递推计算示例。
观察发现,以上递推公式虽然可行,但 f ( n - 1 , x - i) 中的 x - i会有越界问题。
例如,若希望递推计算 f ( 2 , 2 ) ,由于一个骰子的点数和范围为 [ 1 , 6 ] ,
因此只应求和 f ( 1 , 1 ) ,即 f ( 1 , 0 ) , f ( 1 , - 1 ) , . . . , f ( 1 , - 4 ) 皆无意义。
此越界问题导致代码编写的难度提升。
如下图所示,以上递推公式是 “逆向” 的,即为了计算 f ( n, x) ,将所有与之有关的情况求和;
而倘若改换为 “正向” 的递推公式,便可解决越界问题。
具体来看,由于新增骰子的点数只可能为 1 至 6 ,
因此概率 f ( n - 1 , x) 仅与 f ( n, x + 1 ) , f ( n, x + 2 ) , . . . , f ( n, x + 6 ) 相关。
因而,遍历 f ( n - 1 ) 中各点数和的概率,并将其相加至 f ( n) 中所有相关项,
即可完成 f ( n - 1 ) 至 f ( n) 的递推。
将 f ( i) 记为动态规划列表形式 dp[ i] ,则 i = 1 , 2 , . . . , n的状态转移过程如下图所示。
class Solution {
public double [ ] dicesProbability ( int n) {
double [ ] dp = new double [ 6 ] ;
Arrays . fill ( dp, 1.0 / 6.0 ) ;
for ( int i = 2 ; i <= n; i++ ) {
double [ ] temp = new double [ i * 5 + 1 ] ;
for ( int j = 0 ; j < dp. length; j++ ) {
for ( int k = 0 ; k < 6 ; k++ ) {
temp[ j + k] += dp[ j] / 6.0 ;
}
}
dp = temp;
}
return dp;
}
}
LC