题目
算法思路
设 n 枚骰子点数和为 x 的概率为
f
(
n
,
x
)
f(n,x)
f(n,x),输入 n 个骰子的点数和概率列表为
f
(
n
)
f(n)
f(n)。显然有递推公式:
f
(
n
,
x
)
=
∑
i
=
1
6
f
(
n
−
1
,
x
−
i
)
×
1
6
f(n,x)=\sum_{i=1}^6f(n-1,x-i)\times \frac{1}{6}
f(n,x)=i=1∑6f(n−1,x−i)×61
需要注意的是这样递推的话,
f
(
n
−
1
,
x
−
i
)
f(n-1,x-i)
f(n−1,x−i) 存在 边界问题,例如 n = x = 2 ,即计算
f
(
2
,
2
)
f(2,2)
f(2,2) 时,
f
(
n
−
1
,
x
−
i
)
f(n-1,x-i)
f(n−1,x−i) 中只有
f
(
1
,
1
)
f(1,1)
f(1,1) 有意义,处理边界问题会增加代码编写难度。
我们可以 将递推换个方向,即,遍历
f
(
n
−
1
)
f(n-1)
f(n−1) ,统计每一项
f
(
n
−
1
,
i
)
f(n-1,i)
f(n−1,i) 对
f
(
n
,
i
+
1
)
f(n,i+1)
f(n,i+1)、
f
(
n
,
i
+
2
)
f(n,i+2)
f(n,i+2)、
f
(
n
,
i
+
3
)
f(n,i+3)
f(n,i+3)、…、
f
(
n
,
i
+
6
)
f(n,i+6)
f(n,i+6) 产生的贡献。
即,遍历
f
(
n
−
1
)
f(n-1)
f(n−1) 中各点数和的概率,加到
f
(
n
)
f(n)
f(n) 中相关项上,即可完成
f
(
n
−
1
)
f(n-1)
f(n−1) 到
f
(
n
)
f(n)
f(n) 的递推。
具体代码
注意:
- 由于 f ( n ) f(n) f(n) 只与 f ( n − 1 ) f(n-1) f(n−1) 有关,所以不需要用二维数组来保存所有阶段的数据,可以只用两个一维数组交替前进。
- 写循环终止条件时记得先画图防止出错!!!
class Solution {
public double[] dicesProbability(int n) {
//初始化数组dp,即设为f(1)
double[] dp = new double[6];
Arrays.fill(dp, 1.0 / 6.0);
//f(n-1)递推到f(n)
for(int i = 2; i <= n; i++){
double[] tmp = new double[5 * i + 1];
for(int j = 0; j < dp.length; j++){
for(int k = 0; k < 6; k++){
tmp[j + k] += dp[j] / 6.0;
}
}
dp = tmp;//继续向后递推
}
return dp;
}
}
复杂度分析
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)。n 个骰子需要状态转移循环 n - 1 轮,每一轮中循环数量依次为 6 × 6 6\times6 6×6、 11 × 6 11\times6 11×6、…、 [ 5 ( n − 1 ) + 1 ] × 6 [5(n-1)+1]\times6 [5(n−1)+1]×6,因此总的时间复杂度为 O ( ( n − 1 ) × 6 + [ 5 ( n − 1 ) + 1 ] 2 × 6 ) = O ( n 2 ) O((n-1)\times\frac{6+[5(n-1)+1]}{2}\times6)=O(n^2) O((n−1)×26+[5(n−1)+1]×6)=O(n2)
- 空间复杂度:
O
(
n
)
O(n)
O(n),辅助数组
tmp
使用的额外空间。