九度OJ-题目1360:乐透之猜数游戏
2015-06-12 16:55
316 查看
题目链接地址:
九度OJ-题目1360:乐透之猜数游戏
题目描述:
六一儿童节到了,YZ买了很多丰厚的礼品,准备奖励给JOBDU里辛劳的员工。为了增添一点趣味性,他还准备了一些不同类型的骰子,打算以掷骰子猜数字的方式发放奖品。例如,有的骰子有6个点数(点数分别为1~6),有的骰子有7个(点数分别为1~7),还有一些是8个点数(点数分别为1~8) 。他每次从中拿出n个同一类型的骰子(假设它们都是拥有m个点数并且出现概率相同)投掷,然后让员工在纸上按优先级(从高到低)的顺序写下3个数上交,表示他们认为这些骰子最有可能的点数之和是多少。第一个数就猜对的人,是一等奖;第二个数才猜对的人是二等奖;如果三个数都不是正确答案,别灰心!YZ还准备了很多棒棒糖。ZL很聪明,他想了想,打算把概率(以保留两位小数的概率计)最高的三个数找出来,如果有概率相同,则选择其中点数和最小的那个数。你觉得ZL会依次写下哪三个数?
输入:
输入有多组数据。
每组数据一行,包含2个整数n(0<=n<=10),m(6<=m<=8),n表示YZ拿出的骰子数,m表示骰子拥有的点数。如果n=0,则结束输入。
输出:
对应每组数据,输出ZL最可能依次写下的点数,以及其对应的概率值。概率值按4舍5入要求保留2位小数。每组数据之间空一行,注意:最后一组数据末尾无空行。
样例输入:
1 6
4 6
3 7
0
样例输出:
1 0.17
2 0.17
3 0.17
13 0.11
14 0.11
15 0.11
12 0.11
10 0.10
11 0.10
解题思路:
这道题的题意很简单,就是找出n个m点骰子的点数和的出现概率最高的三个数。
假设拿出n个骰子数,每个骰子的点数是m,则可以得出以下结论:
(1)n个m点骰子的点数和sum的取值范围是n <= sum <= n * m;
(2)n个m点骰子一共有m ^ n种点数和(m ^ n表示m的n次方);
(3)每种点数和sum出现的概率是 sum / (m ^ n)。
现在的关键问题是如何求出n个m点骰子的每种点数和的出现次数。
想了很久也没有想出好的办法,只好去看作者的博客
程序员面试题精选100题(43)-n个骰子的点数[算法] 了。
看了几遍也没能理解作者的思路,后来就动手举了几个例子,模拟了作者的解题过程,这才彻底明白了作者的解题思路,囧。。。 所以当我们看不懂某个算法时,可以尝试将一个具体的测试用例代入到算法中,然后执行一遍算法,也许就能弄明白算法的原理了,╮(╯▽╰)╭
下面介绍一下计算n个m点骰子的每种点数和的出现次数的过程:
设拿出的骰子数是n,每个骰子的点数是m,dicePointTimes[i][j]表示 i 个骰子点数和是 j 的出现次数;请脑补这样的画面:现在将一堆骰子分为两部分,一部分是已经计算出点数和出现次数的骰子堆A(A堆初始状态下只有1个骰子),剩余部分是还没有计算出点数和出现次数的骰子堆B,每次都从B堆中拿出1个骰子丢到A堆中,然后重新计算A堆的骰子点数和出现次数。那么当B堆中的骰子全部丢到A堆后,就能算出n个m点骰子的每种点数和的出现次数了。
假设现在前i–1个骰子的点数和出现次数已经计算完毕,而后n – (i - 1)个骰子的点数和还没有被计算。现在将第 i 个骰子加入到前i-1个骰子中,求 i 个骰子点数和是 j (i <= j < = i * m)的出现次数的过程如下:
因为增加一个骰子后,点数和可以增加的范围是1 ~ m。
因此可以由以下m种情况可以推导出 i 个骰子点数和为 j:
(1)将前i-1个骰子点数和为j-1加上第 i 个骰子的点数1后,可以得到 i 个骰子点数和为 j;
(2)将前i-1个骰子点数和为j-2加上第 i 个骰子的点数2后,可以得到 i 个骰子点数和为 j;
……
(m)将前i-1个骰子点数和为j-m加上第 i 个骰子的点数m后,可以得到 i 个骰子点数和为 j。
因此可以推出 i 个骰子点数和是 j 的出现次数等于前n-1个骰子点数和为 t (j-m <= t <= j-1)的出现次数之和,
即dicePointTimes[i][j] = dicePointTimes[i - 1][j - m] + ... + dicePointTimes[i - 1][j - 1]
当只有1个骰子时,每个点数和的出现次数都是1,即dicePointTimes[1][j] = 1(1 <= j <= m)。
如果觉得上述的过程生涩难懂,可以自己举个实例,手动模拟算法的执行过程就能很好地理解这个算法了。
此题还有一点需要注意:题目要求先算出各个点数和出现的概率,再将所得到的概率四舍五入并保留两位小数后,才把概率(以保留两位小数的概率计)最高的三个数找出来。
AC代码如下:
九度OJ-题目1360:乐透之猜数游戏
题目描述:
六一儿童节到了,YZ买了很多丰厚的礼品,准备奖励给JOBDU里辛劳的员工。为了增添一点趣味性,他还准备了一些不同类型的骰子,打算以掷骰子猜数字的方式发放奖品。例如,有的骰子有6个点数(点数分别为1~6),有的骰子有7个(点数分别为1~7),还有一些是8个点数(点数分别为1~8) 。他每次从中拿出n个同一类型的骰子(假设它们都是拥有m个点数并且出现概率相同)投掷,然后让员工在纸上按优先级(从高到低)的顺序写下3个数上交,表示他们认为这些骰子最有可能的点数之和是多少。第一个数就猜对的人,是一等奖;第二个数才猜对的人是二等奖;如果三个数都不是正确答案,别灰心!YZ还准备了很多棒棒糖。ZL很聪明,他想了想,打算把概率(以保留两位小数的概率计)最高的三个数找出来,如果有概率相同,则选择其中点数和最小的那个数。你觉得ZL会依次写下哪三个数?
输入:
输入有多组数据。
每组数据一行,包含2个整数n(0<=n<=10),m(6<=m<=8),n表示YZ拿出的骰子数,m表示骰子拥有的点数。如果n=0,则结束输入。
输出:
对应每组数据,输出ZL最可能依次写下的点数,以及其对应的概率值。概率值按4舍5入要求保留2位小数。每组数据之间空一行,注意:最后一组数据末尾无空行。
样例输入:
1 6
4 6
3 7
0
样例输出:
1 0.17
2 0.17
3 0.17
13 0.11
14 0.11
15 0.11
12 0.11
10 0.10
11 0.10
解题思路:
这道题的题意很简单,就是找出n个m点骰子的点数和的出现概率最高的三个数。
假设拿出n个骰子数,每个骰子的点数是m,则可以得出以下结论:
(1)n个m点骰子的点数和sum的取值范围是n <= sum <= n * m;
(2)n个m点骰子一共有m ^ n种点数和(m ^ n表示m的n次方);
(3)每种点数和sum出现的概率是 sum / (m ^ n)。
现在的关键问题是如何求出n个m点骰子的每种点数和的出现次数。
想了很久也没有想出好的办法,只好去看作者的博客
程序员面试题精选100题(43)-n个骰子的点数[算法] 了。
看了几遍也没能理解作者的思路,后来就动手举了几个例子,模拟了作者的解题过程,这才彻底明白了作者的解题思路,囧。。。 所以当我们看不懂某个算法时,可以尝试将一个具体的测试用例代入到算法中,然后执行一遍算法,也许就能弄明白算法的原理了,╮(╯▽╰)╭
下面介绍一下计算n个m点骰子的每种点数和的出现次数的过程:
设拿出的骰子数是n,每个骰子的点数是m,dicePointTimes[i][j]表示 i 个骰子点数和是 j 的出现次数;请脑补这样的画面:现在将一堆骰子分为两部分,一部分是已经计算出点数和出现次数的骰子堆A(A堆初始状态下只有1个骰子),剩余部分是还没有计算出点数和出现次数的骰子堆B,每次都从B堆中拿出1个骰子丢到A堆中,然后重新计算A堆的骰子点数和出现次数。那么当B堆中的骰子全部丢到A堆后,就能算出n个m点骰子的每种点数和的出现次数了。
假设现在前i–1个骰子的点数和出现次数已经计算完毕,而后n – (i - 1)个骰子的点数和还没有被计算。现在将第 i 个骰子加入到前i-1个骰子中,求 i 个骰子点数和是 j (i <= j < = i * m)的出现次数的过程如下:
因为增加一个骰子后,点数和可以增加的范围是1 ~ m。
因此可以由以下m种情况可以推导出 i 个骰子点数和为 j:
(1)将前i-1个骰子点数和为j-1加上第 i 个骰子的点数1后,可以得到 i 个骰子点数和为 j;
(2)将前i-1个骰子点数和为j-2加上第 i 个骰子的点数2后,可以得到 i 个骰子点数和为 j;
……
(m)将前i-1个骰子点数和为j-m加上第 i 个骰子的点数m后,可以得到 i 个骰子点数和为 j。
因此可以推出 i 个骰子点数和是 j 的出现次数等于前n-1个骰子点数和为 t (j-m <= t <= j-1)的出现次数之和,
即dicePointTimes[i][j] = dicePointTimes[i - 1][j - m] + ... + dicePointTimes[i - 1][j - 1]
当只有1个骰子时,每个点数和的出现次数都是1,即dicePointTimes[1][j] = 1(1 <= j <= m)。
如果觉得上述的过程生涩难懂,可以自己举个实例,手动模拟算法的执行过程就能很好地理解这个算法了。
此题还有一点需要注意:题目要求先算出各个点数和出现的概率,再将所得到的概率四舍五入并保留两位小数后,才把概率(以保留两位小数的概率计)最高的三个数找出来。
AC代码如下:
#include<stdio.h> #include<math.h> #define N 11 // N表示骰子数的最大值 #define M 9 // M表示单个骰子点数的最大值 #define MAX (M * N) // MAX表示N个骰子点数和的最大值 double dicePointTimes [MAX]; //保存每种骰子点数出现的次数,dicePointTimes[i][j]表示i个骰子点数和为j的次数 double dicePointProbability[MAX]; //每个骰子点数出现的概率 bool isDicePointSelected[MAX]; //标记排序过程中某个骰子点数是否已经被选择过 /** * 初始化每个点数之和的出现次数 * @param n 表示骰子的数目 * @param m 表示每个骰子的点数 * @return void */ void initDicePointTimes(int n,int m) { int i,j; for(i = 1;i <= n;i++) { for(j = 1;j <= n * m;j++) { dicePointTimes[i][j] = 0; } } } /** * 统计n个m点的骰子的每种点数之和所对应的出现次数 * @param n 表示骰子的数目 * @param m 表示每个骰子的点数 * @return void */ void getDicePointTimes(int n,int m) { int i,j,t; initDicePointTimes(n,m); // 第1个骰子各个点数出现的次数都是1 for(i = 1; i <= m;i++) { dicePointTimes[1][i] = 1; } // 依次添加第i(2 <= i <= n)个后,所得到的骰子点数和的出现次数 for(i = 2; i <= n;i++) { for(j = i;j <= i * m;j++) // j表示i个骰子点数和,j的范围是[i,i * m] { // dicePointTimes[i][j] = dicePointTimes[i - 1][j - m] + ... + dicePointTimes[i - 1][j - 1] for(t = j - m;t <= j - 1;t++) { if(t >= 1) // 防止数组越界,因为骰子点数最小是1,所以t >= 1 dicePointTimes[i][j] += dicePointTimes[i - 1][t]; } } } } /** * 统计每个骰子点数和出现的概率 * @param n 表示骰子的数目 * @param m 表示每个骰子的点数 * @return void */ void getDicePointProbability(int n,int m) { int i; double totalDicePointTimes; // 表示n个点数为m的骰子所有点数和出现的总次数 totalDicePointTimes = pow(m,n); // n个点数为m的骰子所有点数之和出现的总次数为 m ^ n for(i = n; i <= n * m;i++) // n个点数为m的骰子的点数之和范围是[n,n * m] { dicePointProbability[i] = dicePointTimes [i] / totalDicePointTimes; dicePointProbability[i] = ((int)(100 * dicePointProbability[i] + 0.5)) / 100.0; //根据题目要求要对概率值按4舍5入要求保留2位小数 } } /** * 取出3个出现概率最大的点数和 * @param n 表示骰子的数目 * @param m 表示每个骰子的点数 * @return void */ void guessNumber(int n,int m) { int i,j; double maxDicePointProbability; // 用于某次选择过程中保存最大的骰子点数之和出现的最大概率 int dicePoint; // 每次选择出来的骰子点数 getDicePointProbability(n,m); for(i = n;i <= n * m;i++) { isDicePointSelected[i] = false; } // 选出3个出现概率最大的骰子点数之和 for(i = 1;i <= 3;i++) { maxDicePointProbability = -1; for(j = n;j <= n * m;j++) { if(false == isDicePointSelected[j] && dicePointProbability[j] > maxDicePointProbability) { maxDicePointProbability = dicePointProbability[j]; dicePoint = j; } } isDicePointSelected[dicePoint] = true; printf("%d %.2lf\n",dicePoint,dicePointProbability[dicePoint]); } } int main() { int n,m; int nullLine = 0; // 用于控制是否在数据末尾输出空行 while(EOF != scanf("%d",&n) && 0 != n) { scanf("%d",&m); getDicePointTimes(n,m); getDicePointProbability(n,m); if(0 != nullLine) printf("\n"); guessNumber(n,m); nullLine++; } return 0; } /************************************************************** Problem: 1360 User: blueshell Language: C++ Result: Accepted Time:10 ms Memory:1116 kb ****************************************************************/
相关文章推荐
- jodd-StringTemplateParser使用
- 性能计数器
- cocos2d-x 3.6版连连看加载资源
- 1036. Boys vs Girls (25)
- Android控件使用—TabHost底部导航
- Codeforces548A:Mike and Fax
- arm linux 自动登陆
- svn的merge使用例子
- 1036. Boys vs Girls (25)
- 新gre写作经典50句
- php线性表的入栈与出栈实例分析
- 用看板做敏捷开发
- JSP和Servlet面试指导
- [APIO2015]巴厘岛的雕塑(数位dp)
- LDA主题模型学习笔记3.5:变分参数推导
- const在c/c++中的区别
- JQuery的Ajax跨域请求的
- 缓存的常见问题
- CDbConnection failed to open the DB connection: SQLSTATE[28000] [1045] Access denied for user 'root'@'localhost' (using password: YES)
- 构建之法的第十、十一、十二章读书笔记