题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,打印出S的所有可能的值出现的概率。
一般来说骰子点数为1~6,n个筛子的点数之和可以这样理解:第n个骰子可能出现的数与前面(n-1)个骰子和的和,前面(n-1个骰子)的和为第(n-1)个骰子可能出现的数与前面(n-2)个骰子和的和。。以此类推。以动态规划的方式求解。
1.用递归解决
建立一个长度[n*g_maxValue]的表,a[n]表示和为n出现的次数,除以6^n即为概率。
#include <stdio.h>
#include <iostream>
#include <time.h>
using namespace std;
int g_maxValue = 6;//定义骰子最大值
void getnum2(int n,int sum,int a[])
{
if(n==0)
{
a[sum]++;
return;
}
for(int i=1;i<=g_maxValue;i++)
{int temp=sum+i;
getnum2(n-1,temp,a);//6个分支 每一个去求前面n个骰子的和,时间复杂度6的指数级递增
}
}
void main()
{
time_t tstart=time(NULL); //获取当前时间
int n=12;
getnum(n);
time_t tend=time(NULL);/获取递归结束后时间
cout<<endl;
cout<<tend-tstart;//打印递归时间
}
2.用数组的方法解决
每增加一个骰子,即在原来的基础上增加了1~6,即和为n的数为(n-1)~(n-6)出现次数之和。
用两个数组表示n-1个骰子的情况和n个骰子的情况。
#include <stdio.h>
#include <iostream>
#include <time.h>
using namespace std;
int g_maxValue = 6;
void getnum(int n)
{
int *allnum[2];
allnum[0]=new int[n*g_maxValue+1];
allnum[1]=new int[n*g_maxValue+1];
for(int i=0;i<=n*g_maxValue;i++)
{
allnum[0][i]=0;
allnum[1][i]=0;
}
int flag=0;
for(int i=1;i<=g_maxValue;i++)
allnum[flag][i] = 1;
for(int k=2;k<=n;k++)
{
for(int i=k*g_maxValue;i>=k;i--)
{
allnum[1-flag][i]=0;
for(int j=1;j<=i&&j<=g_maxValue;j++)
{
allnum[1-flag][i]=allnum[1-flag][i]+allnum[flag][i-j];
}
}
for(int i=0;i<n*g_maxValue;i++)
allnum[flag][i]=0;
flag=1-flag;
}
for(int i=n;i<=n*g_maxValue;i++)
{
if(allnum[0][n]!=0)
cout<<allnum[0][i]<<" ";
else cout<<allnum[1][i]<<" ";
}
delete [] allnum[0];
delete [] allnum[1];
}
void main()
{
time_t tstart=time(NULL);
int n=12;
getnum(n);
time_t tend=time(NULL);;
cout<<endl;
cout<<tend-tstart;
}
方法时间差距很大,在n=12的时候,VS递归的要66s,DP的用0s。
3.在参考的博客评论里还有个更粗暴的
void dice1()
{
const int n = 6;
int sum[n * n + 1] = {0};
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
for(int k = 1; k <= n; k++)
{
for(int x = 1; x <= n; x++)
{
for(int y = 1; y <= n; y++)
{
for(int z = 1; z <= n; z++)
{
sum[i + j + k + x + y + z]++;
}
}
}
}
}
}
}
详情参考:http://zhedahht.blog.163.com/blog/static/254111742009101524946359/