1. 程式人生 > 資訊 >雅虎 CEO 宣佈辭職,交友應用 Tinder CEO 繼任

雅虎 CEO 宣佈辭職,交友應用 Tinder CEO 繼任

劍指 Offer 60. n個骰子的點數

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

你需要用一個浮點數陣列返回答案,其中第 i 個元素代表這 n 個骰子所能擲出的點數集合中第 i 小的那個的概率。

示例 1:

輸入: 1
輸出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]

示例 2:

輸入: 2
輸出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

限制:

  • 1 <= n <= 11

做題思路:

說實話,這道題是看了k神(https://leetcode-cn.com/problems/nge-tou-zi-de-dian-shu-lcof/solution/jian-zhi-offer-60-n-ge-tou-zi-de-dian-sh-z36d/)的程式碼和題解才知道怎麼做的,先放k神程式碼吧。

class Solution {
    public double[] dicesProbability(int n) {
        double[] dp = new double[6];
        Arrays.fill(dp, 1.0 / 6.0);
        for (int i = 2; i <= n; i++) {
            double[] tmp = new double[5 * i + 1];
            for (int j = 0; j < dp.length; j++) {
                for (int k = 0; k < 6; k++) {
                    tmp[j + k] += dp[j] / 6.0;
                }
            }
            dp = tmp;
        }
        return dp;
    }
}

這是根據K神程式碼自己加了些註釋,希望其他人可以看懂。

class Solution {
    public double[] dicesProbability(int n) {
       double[] dp = new double[6];
       //只有一個骰子時,陣列初始化
       Arrays.fill(dp, 1.0/6.0); 
        //骰子的個數進行逐次累加,骰子總數從2開始
       for(int i = 2 ;i <= n;i++){  
        //新陣列存放骰子的每個總數和出現的概率
        //骰子總數為i時,骰子可能出現的點數i~6i,長度6i-i+1
           double[] tmp = new double[5*i+1]; 
            //骰子數為i-1時,所有骰子總數和出現的情況,j值表示陣列中的索引,並不表示骰子總數和的數值。
            for(int j = 0; j < dp.length; j++){ 
                  //新加骰子可能出現的點數1~6,在陣列中影響前面索引加0~5的索引指向的值
                for(int k = 0;k < 6; k++){ 
                    //正向遞推,骰子總數為i-1的陣列出現的每一個點數和,
                    //單獨拿出來依次和新點數(1~6)相加,計算相加後點數和的概率    
                    tmp[j + k] += dp[j]/6;      
                }
            }
            //骰子總數為i的陣列賦值骰子總數為i-1的陣列
            dp = tmp;   
       }
       return dp;
    }
}

這位力友大佬力扣 (leetcode-cn.com)的思路更詳細點,分享出來,希望有用。

解釋思路:

這道題的正向推導其實用概率獨立性去解釋可能更好理解。
假設有兩個骰子A、B,這兩個骰子相互獨立。
在僅有一個骰子A的情況下,6個點出現的概率都為1/6, 同時A的每個點搭配B的每個點的概率也是相同的,所以骰子A為1會分別乘6個1/6去搭配骰子B的1~6, 即這種搭配2~7的概率分別都為1/36; 骰子A的2也會分別去乘6個1/6去搭配骰子B的1~6, 即這種搭配3~8的概率也分別為1/36, 以此類推。
因為這是獨立重複試驗,所以之間的關係是相加,所有實驗相加後,就得到了最終的結果。
其實本質是高中數學,但是由於加了場景並且要程式碼實現,所以變難了。

程式碼:

class Solution {
   public double[] dicesProbability(int n) {
        //因為最後的結果只與前一個動態轉移陣列有關,所以這裡只需要設定一個一維的動態轉移陣列
        //原本dp[i][j]表示的是前i個骰子的點數之和為j的概率,現在只需要最後的狀態的陣列,所以就只用一個一維陣列dp[j]表示n個骰子下每個結果的概率。
        //初始是1個骰子情況下的點數之和情況,就只有6個結果,所以用dp的初始化的size是6個
        double[] dp = new double[6];
        //只有一個數組
        Arrays.fill(dp,1.0/6.0);
        //從第2個骰子開始,這裡n表示n個骰子,先從第二個的情況算起,然後再逐步求3個、4個···n個的情況
        //i表示當總共i個骰子時的結果
        for(int i=2;i<=n;i++){
        //每次的點數之和範圍會有點變化,點數之和的值最大是i*6,最小是i*1,i之前的結果值是不會出現的;
        //比如i=3個骰子時,最小就是3了,不可能是2和1,所以點數之和的值的個數是6*i-(i-1),化簡:5*i+1
            //當有i個骰子時的點數之和的值陣列先假定是temp
            double[] temp = new double[5*i+1];
            //從i-1個骰子的點數之和的值陣列入手,計算i個骰子的點數之和陣列的值
            //先拿i-1個骰子的點數之和陣列的第j個值,它所影響的是i個骰子時的temp[j+k]的值
            for(int j=0;j<dp.length;j++){
            //比如只有1個骰子時,dp[1]是代表當骰子點數之和為2時的概率,它會對當有2個骰子時的點數之和為3、4、5、6、7、8產生影響,因為當有一個骰子的值為2時,另一個骰子的值可以為1~6,產生的點數之和相應的就是3~8;比如dp[2]代表點數之和為3,它會對有2個骰子時的點數之和為4、5、6、7、8、9產生影響;所以k在這裡就是對應著第i個骰子出現時可能出現六種情況,這裡可能畫一個K神那樣的動態規劃逆推的圖就好理解很多
                for(int k=0;k<6;k++){
                    //這裡記得是加上dp陣列值與1/6的乘積,1/6是第i個骰子投出某個值的概率
                    temp[j+k]+=dp[j]*(1.0/6.0);
                }
            }
            //i個骰子的點數之和全都算出來後,要將temp陣列移交給dp陣列,dp陣列就會代表i個骰子時的可能出現的點數之和的概率;用於計算i+1個骰子時的點數之和的概率
            dp = temp;
        }
        return dp;
    }   
}