[Lc]面试题60n个骰子的点数
Contents
题目
题解
这道题有两个方法,递归与动态规划,动态规划会超时。而且我刚开始看递归反而看不懂,因此转为先看动态规划,借助路漫漫我不畏的题解加图解,理解了动态规划的思想,进而明白了递归的思想
1. 动态规划
重点看上面大佬提供的图解
- 时间复杂度$O(n^{2})$,(不确定,感觉是两个n的循环套一个6的循环)
- 空间复杂度$O(n)$,dp数组
class Solution {//一个方法。1.一维dp
public:
vector<double> twoSum(int n) {
vector<int> dp(70, 0);//n<=11,动态规划最大用66个数组这里用70个
//先初始化dp[1],即只有一个骰子的情况,此时前6个数为1
for(int i = 0; i <= 6; ++i){
dp[i] = 1;
}
//进行动态规划,从2个骰子到n个骰子,注意判别条件是小于等于
for(int i = 2; i <= n; ++i){
//从后向前挨个更新dp数组
//从6*i开始,一直更新到第i个数,这个就是投掷i个骰子的数字取值范围
for(int j = 6 * i; j >= i; --j){
//重点1!!!注意要先把该更新的位置设置为0再进行后面的累加,否则会累加上dp[i-1]的数据,出错
dp[j] = 0;
//累加当前这个数的前6个数的dp,这里可以这么理解
//即投第i个骰子有1~6 6种情况,而要累加到当前值,就要借助dp[i-1]~dp[i-6]的值
for(int cur = 1; cur <= 6; ++cur){
//重点2!!!注意累加值会小于i-1,因为i-1个骰子最小值就是i-1
//累加之前的数据其实是上一个dp的值
if(j - cur < i - 1) break;
//累加上述的前6个值
dp[j] += dp[j-cur];
}
}
}
vector<double> res;
//当前存的都是可能出现的情况次数,还要转化为概率
int all = pow(6, n);//所有可能出现的情况的个数
//将最后一个dp的情况出现次数转化为概率
//注意范围是n~6*n
for(int i = n; i <= 6 * n; ++i){
//这里注意要除以一个1.0将int转化为double
res.push_back(dp[i] * 1.0 / all);
}
return res;
}
};
2. 递归
- 时间复杂度$O(6^{n})$,必超时
- 空间复杂度$O(1)$
这个方法必超时,但是我们可以借助这个问题思考一下dp和递归的关系。个人感觉对这道题来说
- 动态规划就是一种带记忆的递归思想
- 动态规划自顶向下,递归自底向上再返回
其实还借助大佬提供的图解,即动态规划每次存下来当前的状态,下一次状态时直接从数组中取值即刻,空间占用多少取决于和前几次的状态有关。
而递归就是每次取前面状态时需要再重新从下到上算一下,推算到只有一个骰子的情况,这样造成了大量的重复计算,因此会超时。
Author ChrisHRZ
LastMod 2020-07-04