一.題目描述ios
把n個骰子仍在地上,全部的骰子朝上的一面的點數之和爲s,輸入n,打印出s全部可能的值出現的機率。
spa
二.題解.net
《劍指offer》上給出的兩種方法,尤爲是代碼,晦澀難懂且沒有註釋。而n個骰子的問題實質就是一個動態規劃問題,因此文本主要從動態規劃的角度來求解這個問題。首先該問題具有DP的兩個特徵:最優子結構性質和子問題的重疊性。具體的表如今:(1)n個骰子的點數依賴於n-1個骰子的點數,至關於在n-1個骰子點數的基礎上再進行投擲。(2)求父問題的同時,須要屢次利用子問題。由此定義狀態轉移方程爲$f(n,k)$表示$n$個骰子點數和爲$k$時出現的次數,因而可得:blog
$$ f(n,k) = f(n- 1, k- 1) + f(n- 1, k- 2) + f(n- 1, k- 3) + f(n- 1, k- 4) + f(n- 1, k- 5) + f(n- 1, k- 6) $$ci
其中 $n > 0$且$k <= 6n$。其中$f(n-1,k-i)$表示的是第n次擲骰子時,骰子的點數爲$i$對應的狀況,因此從$k-1$到$k-6$分別對應第n次擲骰子時骰子正面爲$1$到$6$的狀況。而初始狀態能夠定義爲:get
$$ f(1,1) = f(1,2) = f(1,3) = f(1,4) = f(1,5) = f(1,6) = 1 $$string
因此根據這兩個方程,給出的實現代碼以下:it
#include<iostream> #include<unordered_map> #include<queue> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<sstream> #include<set> #include<map> #include<stack> #define MAX_NUM 100 using namespace std; void FindSum(int n) { if(n <= 0) return; int sum = 0; int arr[n + 1][6 * n + 1]; memset(arr,0,sizeof(arr)); for(int i = 1; i <= 6; i++)//初始狀態 arr[1][i] = 1; for(int i = 2; i <= n; i++)//狀態轉移方程 { for(int j = i; j <= 6*i; j++)//注意j的範圍受i影響 { arr[i][j] += (arr[i - 1][j - 1] + arr[i - 1][j - 2] + arr[i - 1][j - 3] + arr[i - 1][j - 4] + arr[i - 1][j - 5] +arr[i - 1][j - 6]); } } //輸出結果 for(int i = n; i <= 6 * n; i++) { //cout<<"骰子的和爲 "<<i<<" 時,對應的次數爲:"<<arr[n][i]<<endl; sum += arr[n][i]; } cout<<n<<"個骰子總共的次數爲 "<<sum<<endl; for(int i = n; i <= 6 * n; i++) { cout<<"骰子的和爲 "<<i<<" 時,對應的頻率爲:"<<(arr[n][i] * 1.0 / sum)<<endl; } } int main() { int n; cout<<"請輸入骰子的個數:"<<endl; cin>>n; FindSum(n); }
此處的代碼只是樸素dp的實現,用動態規劃來解釋,感受比書上好理解多了....io
參考:https://blog.csdn.net/k346k346/article/details/50988681class