把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n, 打印出S的所有可能的值出现的概率。
2016-04-12 15:17
387 查看
分析:本题可以采用“穷举法”和 “动态规划”两种思路解决。
1.穷举法的基本思想是根据题目的部分条件确定答案的大致范围,并在此范围内对所有可能的情况逐一验证,直到全部情况验证完毕 。
这里我们可以统计n个骰子朝上一面的点数和s的个数f(s),则其概率r(s)=f(s)/pow(6, n):
2.动态规划:设定n个骰子顶面点数之和s的组合数f(n,s), 则只考虑最后一个骰子的点数1~6,六种情况:
f(n-1,s-1) 最后一个骰子点数为1;
f(n-1,s-2) 最后一个骰子点数为2;
f(n-1,s-3) 最后一个骰子点数为3;
f(n-1,s-4) 最后一个骰子点数为4;
f(n-1,s-5) 最后一个骰子点数为5;
f(n-1,s-6) 最后一个骰子点数为6;
所以有:
f(n,s)=f(n-1,s-1)+f(n-1,s-2)+f(n-1,s-3)+f(n-1,s-4)+f(n-1,s-5)+f(n-1,s-6);
i, 采用递归方法(比较直接的)
ii.采用循环方法
测试代码:
大致比较了一下,穷举法时间2242,递归法时间10081,循环法时间37.
1.穷举法的基本思想是根据题目的部分条件确定答案的大致范围,并在此范围内对所有可能的情况逐一验证,直到全部情况验证完毕 。
这里我们可以统计n个骰子朝上一面的点数和s的个数f(s),则其概率r(s)=f(s)/pow(6, n):
/*统计骰子点数和的组合数*/ void SumOfCount(int n, int left, int sum, int *arr) { if (left== 0)//计算完n个骰子 { ++(arr[sum]);//面点数之和次数加1 return ; } for (int i = 1; i <= 6; ++i)//当前骰子顶面个数1~6 { SumOfCount(n, left - 1, sum + i, arr); } } /*打印出S的所有可能的值出现的概率*/ void PrintProbability(int n) { int length = 6 * n + 1; int *arr = new int[length]; memset(arr, 0, length * sizeof(int)); SumOfCount(n, n, 0, arr); for (int j = n; j < length; ++j) { printf("sum = %d, rate = %d \n", j, arr[j]); } delete[] arr; arr = NULL; }
2.动态规划:设定n个骰子顶面点数之和s的组合数f(n,s), 则只考虑最后一个骰子的点数1~6,六种情况:
f(n-1,s-1) 最后一个骰子点数为1;
f(n-1,s-2) 最后一个骰子点数为2;
f(n-1,s-3) 最后一个骰子点数为3;
f(n-1,s-4) 最后一个骰子点数为4;
f(n-1,s-5) 最后一个骰子点数为5;
f(n-1,s-6) 最后一个骰子点数为6;
所以有:
f(n,s)=f(n-1,s-1)+f(n-1,s-2)+f(n-1,s-3)+f(n-1,s-4)+f(n-1,s-5)+f(n-1,s-6);
i, 采用递归方法(比较直接的)
//直接递归求n个骰子和为s的组合数 int GetSum(int n, int s) { if (n == 1)//临界状态1,只有一个骰子 { return (s > 0 && s <= 6) ? 1 : 0; } if (n == s)//临界状态2,n个骰子最小s=n { return 1; } if (n > s)//临界保护 { return 0; } int sum = 0; for (int i = 1; i <= 6; ++i) { sum += GetSum(n - 1, s - i); } return sum; } void PrintProbabilityA(int n) { for (int j = n; j < 6 * n + 1; ++j) { printf("sum = %d, rate = %d \n", j, GetSum(n, j)); } }
ii.采用循环方法
void PrintProbabilityB(int n) { int length = 6 * n + 1; int *arr = new int[length]; memset(arr, 0, length * sizeof(int)); for (int i = 1; i <= 6; ++i)//骰子个数为1的情况; { arr[i] = 1; } int j, k; for (j = 2; j <= n; ++j)//骰子个数; { for (k = 6 * j; k >= j; --k)//骰子上面个数和; { arr[k] = 0; for (int m = 1; m <= 6 && k > m; ++m) { arr[k] += arr[k - m]; } } arr[j - 1] = 0; } for (int j = n; j < length; ++j) { printf("sum = %d, rate = %d \n", j, arr[j]); } delete []arr; arr = NULL; }
测试代码:
int _tmain(int argc, _TCHAR* argv[]) { //穷举法测试; clock_t start = clock(); PrintProbability(10); printf("%d\n", clock() - start); //递归法测试; start = clock(); PrintProbabilityA(10); printf("%d\n", clock() - start); //循环法测试; start = clock(); PrintProbabilityB(10); printf("%d\n", clock() - start); return 0; }
大致比较了一下,穷举法时间2242,递归法时间10081,循环法时间37.
相关文章推荐
- android开发问题
- 将HTML5 Canvas的内容保存为图片借助toDataURL实现
- [译]一个灵活的 Trello 敏捷工作流
- 《构建之法》阅读笔记1
- iOS优雅的获取数组的最大值
- Kinect 基于 Windows SDK 1.7
- DIV随滚动条滚动而滚动的实现代码【推荐】
- 完美实现javascript继承
- awk命令求和
- kafka windows环境的搭建之路
- LeetCode 40. Combination Sum II
- 字符串匹配
- Android 最火的快速开发框架androidannotations配置详解
- 单片机基本知识再学习
- 问题error: no such file or directory:xxx的处理方法
- APP系统报错日志反馈机制设计
- 对于微信内置浏览器中不能小窗播放视频原因的分析以及解决
- SQL Server 高性能写入的一些总结
- Javascript的作用域、作用域链以及闭包
- 从此与mathtype和office拜拜!!