题目
把 n 个骰子扔在地上,所有骰子朝上一面的点数之和为 s。输入 n,打印出 s 的所有可能的值出现的概率。
思路
解法一:
先把 n 个骰子分为两堆:第一堆只有一个,另一个有 n-1 个。单独的那一个有可能出现从 1 到 6 的点数。我们需要计算从 1 到 6 的每一种点数和剩下的 n-1 个骰子来计算点数和。接下来把剩下的 n-1 个骰子还是分成两堆,第一堆只有一个, 第二堆有 n-2 个。我们把上一轮那个单独骰子的点数和这一轮单独骰子的点数相加, 再和剩下的 n-2 个骰子来计算点数和。分析到这里,我们不难发现这是一种递归的思路,递归结束的条件就是最后只剩下一个骰子。
我们可以定义一个长度为“6n-n+1”的数组, 和为 s 的点数出现的次数保存到数组第 s-n 个元素里。
解法二:
1.现在变量有:骰子个数,点数和。当有c个骰子,点数和为k时,出现次数记为dp(c,k)。
2.当我有c-1个骰子时,再增加一个骰子,这个骰子的点数只可能为1、2、3、4、5或6。那k个骰子得到点数和为n的情况有:
(c-1,k-1):第c个骰子投了点数1
(c-1,k-2):第c个骰子投了点数2
(c-1,k-3):第c个骰子投了点数3
....
(c-1,k-6):第c个骰子投了点数6
在c-1个骰子的基础上,再增加一个骰子出现点数和为k的结果只有这6种情况!所以:dp(c,k)=dp(c-1,k-1)+dp(c-1,k-2)+dp(c-1,k-3)+dp(c-1,k-4)+dp(c-1,k-5)+dp(c-1,k-6)(注意当k<6时的处理越界问题)
3.有1个骰子,dp(1,1)=dp(1,2)=dp(1,3)=dp(1,4)=dp(1,5)=dp(1,6)=1。
因此状态转移方程为
dp[c][k]=sum(dp[c-1][k-m])(1<=m<=6&&m<k)
#include <iostream> #include <cmath> using namespace std; const int max_value=6;//单个骰子的最大值 class Solution { public: void probality(int n);//递归 void probality(int original,int index,int curr_sum,int *value); void cricile_probality(int n);//基于循环解法 }; void Solution::probality(int n) { if(n<1) return; int max_sum=n*max_value;//n个骰子的最大值 int *value=new int[max_sum-n+1];//定义6*n-n+1的数组 for(int i=n;i<=max_sum;++i) value[i-n]=0;//将和为s的点数存放到数组的第s-n个元素里 int curr_sum=0; probality(n,n,curr_sum,value); int sum=pow((double)max_value,n); for(int i=n;i<=max_sum;++i) { double ratio=(double)value[i-n]/sum; cout<<i<<' '<<ratio<<endl; } delete []value; return; } void Solution::probality(int original,int index,int curr_sum,int *value) { if(index==0) { value[curr_sum-original]+=1; return; } for(int i=1;i<=max_value;++i) probality(original,index-1,i+curr_sum,value); } void Solution::cricile_probality(int n) { if(n<1) return; int *value[2]; value[0]=new int[max_value*n+1]; value[1]=new int[max_value*n+1]; for(int i=0;i<max_value*n+1;++i) { value[0][i]=0; value[1][i]=1; } int flag=0;//标记当前使用的是0数组还是1数组 for(int i=1;i<=max_value;++i)//第一次投骰子 value[flag][i]=1; //抛出其他骰子 for(int k=2;k<=n;++k) { for(int i=0;i<k;++i)//如果抛出了k个骰子,那么和为[0, k-1]的出现次数为0 value[1-flag][i]=0; for(int i=k;i<=max_value*k;++i)//抛出k个骰子,所有和的可能 { value[1-flag][i]=0; for(int j=1;j<=i&&j<=max_value;++j) value[1-flag][i]+=value[flag][i-j]; } flag=1-flag; } int sum=pow((double)max_value,n); for(int i=n;i<=max_value*n;++i) { double ratio=(double)value[flag][i]/sum; cout<<i<<' '<<ratio<<endl; } for(int i=0;i<max_value*n+1;++i) { delete []value[0]; delete []value[1]; } return; } int main() { Solution s; s.probality(6); s.cricile_probality(6); return 0; }