題目:
把n個骰子扔在地上,所有骰子朝上一面的點數之和為s。輸入n,列印出s的所有可能的值出現的概率。
解法一:遞迴
玩過麻將的都知道,骰子一共6個面,每個面上都有乙個點數,對應的數字是1到6之間的乙個數字。所以,n個骰子的點數和的最小值為n,最大值為6n。因此,乙個直觀的思路就是定義乙個長度為6n-n的陣列,和為s的點數出現的次數儲存到陣列第s-n個元素裡。另外,我們還知道n個骰子的所有點數的排列數6
^n。一旦我們統計出每一點數出現的次數之後,因此只要把每一點數出現的次數除以n^6,就得到了對應的概率。
該思路的關鍵就是統計每一點數出現的次數。要求出n個骰子的點數和,我們可以先把n個骰子分為兩堆:第一堆只有乙個,另乙個有n-1個。單獨的那乙個有可能出現從1到6的點數。我們需要計算從1到6的每一種點數和剩下的n-1個骰子來計算點數和。接下來把剩下的n-1個骰子還是分成兩堆,第一堆只有乙個,第二堆有n-2個。我們把上一輪那個單獨骰子的點數和這一輪單獨骰子的點數相加,再和剩下的n-2個骰子來計算點數和。分析到這裡,我們不難發現,這是一種遞迴的思路。遞迴結束的條件就是最後只剩下乙個骰子了。
int g_maxvalue = 6;
void printsumprobabilityofdices_1(int number)
delete pprobabilities;}
void sumprobabilityofdices(int number, int* pprobabilities)
void sumprobabilityofdices(int original, int current, int value, int tempsum, int* pprobabilities)
else
}}
解法二:雙陣列
上述演算法當
number
比較小的時候表現很優異。但由於該演算法基於遞迴,它有很多計算是重複的,從而導致當
number
變大時效能讓人不能接受。
我們可以考慮換一種思路來解決這個問題。我們可以考慮用兩個陣列來儲存骰子點數每一總數出現的次數。在一次迴圈中,第乙個陣列中的第
n個數字表示骰子和為nn
的骰子出現的次數,應該等於上一次迴圈中骰子點數和為
n-1、
n-2、
n-3、
n-4、
n-5與
n-6的總和。所以我們把另乙個陣列的第
n個數字設為前乙個陣列對應的第
n-1、
n-2、
n-3、
n-4、
n-5與
n-6之和。
void printsumprobabilityofdices_2(int number)
int flag = 0;
for (int i = 1; i <= g_maxvalue; ++i)
pprobabilities[flag][i] = 1;
for (int k = 2; k <= number; ++k)
flag = 1 - flag;
}double total = pow((double)g_maxvalue, number);
for(int i = number; i <= g_maxvalue * number; ++i)
delete pprobabilities[0];
delete pprobabilities[1];
}
值得提出來的是,上述**沒有在函式裡把乙個骰子的最大點數硬編碼(hard code)為6,而是用乙個變數g_maxvalue來表示。這樣做的好處時,如果某個廠家生產了最大點數為4或者8的骰子,我們只需要在**中修改乙個地方,擴充套件起來很方便。如果在面試的時候我們能對面試官提起對程式擴充套件性的考慮,一定能給面試官留下乙個很好的印象。
劍指offer 面試題43 n個骰子的點數
題目 把n個骰子扔在地上,所有骰子朝上一面的點數之和為s。輸入n,列印出s所有可能的值出現的概率。思路 骰子六個點數,所以n個骰子出現的所有可能有6的n次方種。那麼這個問題我們專注於求點數和s可能出現的次數,概率相除就可以了。假設點數和為n,那麼最後一粒骰子可能出現1,2,3,4,5,6這六種可能,...
面試題43 n個骰子的點數
注意點 n 個骰子的面朝上點數和的範圍 n,6n 一共有6n n 1種可能 每個骰子的範圍 1,6 n 個骰子分為兩部分,第一骰子和其餘的n 1個骰子 遞迴的終止條件為當前其餘骰子個數為0個 include include include includeusing namespace std int...
面試題43 n個骰子的點數
題目 把n個骰子扔在地上,所有骰子朝上一面的點數之和為s。輸入n,列印出s所有可能的值出現的概率。該題是典型的動態規劃問題。n個骰子它的和顯然和前面n 1個骰子的狀態有關。可以一步步劃分來求,一般考慮設個數祖,乙個記錄當前是第多少個骰子,乙個記錄總和多少,還要記錄總和的概率,因此此處考慮設個二維陣列...