题目描述
把 n 个骰子扔在地上,所有骰子朝上一面的点数之和为 s。输入 n,打印出 s 的所有可能的值出现的概率。
解题思路
此题目解法很多,其中使用动态规划法最好理解,代码也比较简洁
1.现在变量有:骰子个数,点数和。当有c个骰子,点数和为k时,出现次数记为dp(c,k)。那与c-1个骰子阶段之间的关系是怎样的?
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。
代码如下:
#include <iostream>
#include <cmath>
using namespace std;
int **dp;
//n是骰子数
void printProbability(int n){
if(n < 1)
return ;
int total = pow(6,n);
dp = new int*[n+1];
for(int i=0;i<=n;i++)
dp[i] = new int[6*n+1];
for(int i=0;i<=n;++i) //注意 c<=n
for(int j=0;j<=6*n;++j)
dp[i][j] = 0;
for(int i=1;i<=n;++i) //注意 i<=n
for(int j=i;j<=i*6;++j){ //注意 j<=6i
if(i==1 || j==1)
dp[i][j] = 1;
else{
int sum = 0;
for(int k=1;k<=6 && k<j;++k)
sum += dp[i-1][j-k];
dp[i][j] = sum;
}
}
for (int i = 1; i<= n; i++) {
cout << "a[" << i <<"]: ";
for (int j = i; j <= 6*i; j++) {
cout << dp[i][j] << " ";
}
cout<<endl;
}
}
int main(){
printProbability(3);
return 0;
}