《劍指offer(第二版)》面試題60——n個骰子的點數


一.題目描述

  把n個骰子仍在地上,所有的骰子朝上的一面的點數之和為s,輸入n,打印出s所有可能的值出現的概率。

二.題解

  《劍指offer》上給出的兩種方法,尤其是代碼,晦澀難懂且沒有注釋。而n個骰子的問題實質就是一個動態規划問題,所以文本主要從動態規划的角度來求解這個問題。首先該問題具備DP的兩個特征:最優子結構性質和子問題的重疊性。具體的表現在:(1)n個骰子的點數依賴於n-1個骰子的點數,相當於在n-1個骰子點數的基礎上再進行投擲。(2)求父問題的同時,需要多次利用子問題。由此定義狀態轉移方程為$f(n,k)$表示$n$個骰子點數和為$k$時出現的次數,於是可得:

$$ 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) $$

其中 $n > 0$且$k <= 6n$。其中$f(n-1,k-i)$表示的是第n次擲骰子時,骰子的點數為$i$對應的情況,所以從$k-1$到$k-6$分別對應第n次擲骰子時骰子正面為$1$到$6$的情況。而初始狀態可以定義為:

$$ f(1,1) = f(1,2) = f(1,3) = f(1,4) = f(1,5) = f(1,6) = 1 $$

所以根據這兩個方程,給出的實現代碼如下:

 

#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的實現,用動態規划來解釋,感覺比書上好理解多了....

參考:https://blog.csdn.net/k346k346/article/details/50988681

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM