題目:把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/