PROBLEM

剑指 Offer 60. n个骰子的点数

难度 中等

MY ANSWER

动态规划,res[i][j]代表用 i 个骰子掷出 j 值。时间复杂度为O(n^2),空间复杂度O(n^2)。

递推方程为:res[i][j] = (res[i-1][j-1] + ... + res[i-1][j-6]) / 6

class Solution {
public:
vector<double> dicesProbability(int n) {
vector<vector<double>> res(n + 1, vector<double> (n * 6 + 1, 0));
for(int i = 1; i <= 6; i++) {
res[1][i] = 1.0 / 6.0;
}
for(int i = 2; i <= n; i++) {
for(int j = i; j <= 6 * i; j++) {
double ans = 0;
for(int k = 1; k <= 6; k++) {
if(j - k > 0) {
ans += res[i-1][j-k] / 6.0;
}
}
res[i][j] = ans;
}
}
vector<double> ret;
ret.assign(res[n].begin() + n, res[n].end());
return ret;
}
};

BETTER SOLUTION

不需要储存全部的res,只用两个数组存储当前结果与上一行结果即可,空间复杂度降到O(n)。另外将递推改为正向:tmp[j + k] += dp[j] / 6.0;进行正向计算,省略了条件判断,同时也减少了计算次数。

class Solution {
public:
vector<double> dicesProbability(int n) {
vector<double> dp(6, 1.0 / 6.0);
for (int i = 2; i <= n; i++) {
vector<double> tmp(5 * i + 1, 0);
for (int j = 0; j < dp.size(); j++) {
for (int k = 0; k < 6; k++) {
tmp[j + k] += dp[j] / 6.0;
}
}
dp = tmp;
}
return dp;
}
};

SUMMARY

  • 使用动态规划来计算概率问题,也是空间换时间的一种。
  • 动态规划只需要前一步的结果、不需要前面整个过程的结果时,就可以只存储上一步结果,不需要把整个过程存下来,从而减少空间复杂度。
  • 有时计算可以考虑将逆向改为正向,代码更简洁甚至计算次数更少。