您的位置:首页 > Web前端

剑指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];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: