把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。
示例 1:
输入: 1 输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667] 示例 2:
输入: 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
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/nge-tou-zi-de-dian-shu-lcof 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:本题考查的是动态规划。但是我想先上一下暴力版本的(超时)。
class Solution { public: vector<double> twoSum(int n) { //超时写法 暴力法 vector<int> temp; for(int i = n;i <=n * 6;++i) //计算每种结果的次数 { temp.push_back(calcu(n,i)); } vector<double> res; int sum = 0; for(auto x : temp) sum += x; for(auto x : temp) res.push_back(static_cast<double>(x) / static_cast<double>(sum)); return res; } int calcu(int n,int sum)//n个骰子 投出来的点数和为sum的次数 { if(sum < 0) return 0; if(n == 0) //n==0时 考虑sum是否为0 return !sum; int times = 0; for(int i = 1;i<=6;++i) { times += calcu(n - 1,sum - i);//sum 是第n个骰子选i这一面 第n-1个骰子再计算 形成递归 } return times; } };思路二:动态规划。
动态规划我们先确定一下状态,从上面暴力版本得到启示: 假设我们用i个骰子。那么对于第i个骰子,这个骰子出现的点数为k,那么判断i个骰子能否凑出点数j的条件是: 用i-1个骰子能否凑出点数j - k。而k的取值范围是1 ~ 6。
所以我们用f[i][j]表示:用i个骰子拼出点数和为j的次数。 那么转移方程就是: f[i][j] = f[i][j] + f[i - 1][j - k](这里的k取值范围如上所述)
初始条件: f[0][0] = 1(注意这里的条件是不符合定义的f[0][0]是不要的数据,仅仅用来启动状态转移)
代码实现:
class Solution { public: vector<double> dicesProbability(int n) { vector<vector<int>> f(n+1,vector<int>(n*6 + 1,0)); f[0][0] = 1; //f[i][j]表示:用i个骰子拼出点数和为j的次数。 for(int i = 1;i <= n;++i)//n个骰子 { //i个骰子 能拼出的点数为(i,i*6) for(int j = i;j <= i * 6;++j)//可以拼出的点数 总数 { for(int k = 1;k <= min(j,6);++k)//取点数1 - 6 { //k <= min(j,6)表示当前选的点数不能超过要求拼凑的点数 //当前骰子取k点 如果前一个骰子能拼出j-k //则i个筛子能拼出和为j的次数又增加了 满足加法原理 f[i][j] = f[i][j] + f[i - 1][j - k]; } } } vector<int> temp; for(int i = n;i <= n * 6;++i) { temp.push_back(f[n][i]); } vector<double> res; int sum = 0; for(auto x : temp) sum += x;//计算总次数 for(auto x : temp)//计算每种和出现的概率 res.push_back(static_cast<double>(x) / static_cast<double>(sum)); return res; } };