剑指Offer学习总结-n 个骰子的点数
2018-01-25 20:20
127 查看
剑指Offer学习总结-n 个骰子的点数
本系列为剑指Offer学习总结,主要是代码案例的分析和实现:书籍链接:http://product.dangdang.com/24242724.html
原作者博客:http://zhedahht.blog.163.com/blog/static/254111742011101624433132/
原作者博客链接有完整的项目代码下载。
n 个骰子的点数
题目
题目:把n个骰子仍在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。最初的解法
n个骰子数字的全排列组合,然后计算出和的情况。一个骰子有1-6 6种情况,n个骰子的话就有6^n种情况。这么大且爆炸增长的速度,肯定是不能接收的。
递归分治的解法
通过递归分治的思想将n个骰子的点数累加。要求出n个骰子的点数和,可以先求出前n-1个骰子的点数和,然后加上第n个骰子的点数;
递归结束条件:n=1,此时某个点数和出现的次数+1;
void PrintProbability_Solution1(int number) { if(number < 1) return; int maxSum = number * g_maxValue; //为什么是这个规律 1个骰子的范围是1-6 2个骰子的范围是2-12 3个骰子的范围是3-18 n个骰子的范围是n-6n //所以有6n-n+1个数值 int* pProbabilities = new int[maxSum - number + 1]; //初始化这个数组 for(int i = number; i <= maxSum; ++i) pProbabilities[i - number] = 0; //获取所有可能性 Probability(number, pProbabilities); int total = pow((double)g_maxValue, number); for(int i = number; i <= maxSum; ++i) { double ratio = (double)pProbabilities[i - number] / total; printf("%d: %e\n", i, ratio); } delete[] pProbabilities; } void Probability(int number, int* pProbabilities) { for(int i = 1; i <= g_maxValue; ++i) Probability(number, number, i, pProbabilities); } //第一个参数 总的骰子数 //第二个参数 当前是第几个骰子 //第三个参数 当前的和 //第四个参数 保存和的数组 void Probability(int original, int current, int sum, int* pProbabilities) { if(current == 1) { pProbabilities[sum - original]++; } else { for(int i = 1; i <= g_maxValue; ++i) { Probability(original, current - 1, i + sum, pProbabilities); } } }
动态规划的解法
假设f(m,n)表示投第m个骰子时,点数之和n出现的次数,投第m个骰子时的点数之和只与投第m-1个骰子时有关。递归方程:f(m,n)=f(m-1,n-1)+f(m-1,n-2)+f(m-1,n-3)+f(m-1,n-4)+f(m-1,n-5)+f(m-1,n-6),表示本轮点数和为n出现次数等于上一轮点数和为n-1,n-2,n-3,n-4,n-5,n-6出现的次数之和。
初始条件:第一轮的f(1),f(2),f(3),f(4),f(5),f(6)均等于1.
//首先我们得知道递推公式 //f(m,n) = f(m-1,n-1)+f(m-1,n-2)+f(m-1,n-3)+f(m-1,n-4)+f(m-1,n-5)+f(m-1,n-6), //表示本轮点数和为n出现次数等于上一轮点数 和为n - 1,n - 2,n - 3,n - 4,n - 5,n - 6出现的次数之和。 //初始条件:第一轮的f(1), f(2), f(3), f(4), f(5), f(6)均等于1. //所以我们需要两个一维数组来保存这个结果,第一个保存上一次骰子结果的和数值,第二个保存本次骰子结果的和数值 void PrintProbability_Solution2(int number) { if(number < 1) return; int* pProbabilities[2]; //这里其实这么搞有点麻烦了 直接使用两个一位数组即可 pProbabilities[0] = new int[g_maxValue * number + 1]; pProbabilities[1] = new int[g_maxValue * number + 1]; for(int i = 0; i < g_maxValue * number + 1; ++i) { pProbabilities[0][i] = 0; pProbabilities[1][i] = 0; } int flag = 0; //初始化第一个数组 for (int i = 1; i <= g_maxValue; ++i) pProbabilities[flag][i] = 1; //从两个骰子到number个骰子的情况 for (int k = 2; k <= number; ++k) { //数组元素表示 第n个位置的元素代表和为n的个数 现在将不可能元素的位置置为0 for(int i = 0; i < k; ++i) pProbabilities[1 - flag][i] = 0; //然后考虑骰子和的所有情况 注意这里其实后边的有位置是有可能暂时用不到的 for (int i = k; i <= g_maxValue * k; ++i) { pProbabilities[1 - flag][i] = 0;//先给个初值 //这里应用递推公式 当前和i-(1-6)的情况 并且要保证这个差值是大于等于0的 所有有j<=i的判断 for(int j = 1; j <= i && j <= g_maxValue; ++j) pProbabilities[1 - flag][i] += pProbabilities[flag][i - j]; } //交换flag值重新赋值 flag = 1 - flag; } //算出每种和的情况概率 double total = pow((double)g_maxValue, number); for(int i = number; i <= g_maxValue * number; ++i) { double ratio = (double)pProbabilities[flag][i] / total; printf("%d: %e\n", i, ratio); } delete[] pProbabilities[0]; delete[] pProbabilities[1]; }
相关文章推荐
- 《剑指Offer》学习笔记--面试题43:n个骰子的点数
- 【剑指offer系列】 n个骰子的点数___43
- [置顶] 剑指Offer学习总结-第一章 面试的流程
- 剑指Offer(Java版):n个骰子的点数
- (剑指Offer)面试题43:n个骰子的点数
- 剑指Offer——n个骰子扔出点数和S的概率
- 剑指offer——面试题43:n个骰子的点数
- 剑指Offer学习总结-字符串的排列
- 剑指offer 面试题43—n个骰子的点数
- 剑指offer(47):n个骰子的点数
- 剑指Offer学习总结-从尾到头打印链表
- 剑指Offer(第二版)面试题60:n个骰子的点数
- 面试题43:n个骰子的点数(《剑指offer》)
- 剑指Offer学习总结-数组中只出现一次的数字
- 《剑指offer》43:n个骰子的点数
- 剑指Offer学习总结-打印 1 到最大的 n 位数
- 剑指offer-6-面试43:n 个骰子的点数()
- 剑指Offer学习总结-二叉树的深度
- n个骰子的点数--总结
- 剑指Offer面试题43(Java版):n个骰子的点数