[LeetCode]837. 新21點

題目

愛麗絲參與一個大致基於紙牌遊戲 “21點” 規則的遊戲,描述如下:

愛麗絲以 0 分開始,並在她的得分少於 K 分時抽取數字。 抽取時,她從 [1, W] 的範圍中隨機獲得一個整數作爲分數進行累計,其中 W 是整數。 每次抽取都是獨立的,其結果具有相同的概率。

當愛麗絲獲得不少於 K 分時,她就停止抽取數字。 愛麗絲的分數不超過 N 的概率是多少?

示例 1:

輸入:N = 10, K = 1, W = 10
輸出:1.00000
說明:愛麗絲得到一張卡,然後停止。

示例 2:

輸入:N = 6, K = 1, W = 10
輸出:0.60000
說明:愛麗絲得到一張卡,然後停止。
在 W = 10 的 6 種可能下,她的得分不超過 N = 6 分。

示例 3:

輸入:N = 21, K = 17, W = 10
輸出:0.73278

提示:

  1. 0 <= K <= N <= 10000
  2. 1 <= W <= 10000
  3. 如果答案與正確答案的誤差不超過 10^-5,則該答案將被視爲正確答案通過。
  4. 此問題的判斷限制時間已經減少。

解題思路

簡單分析一下題目,得分少於 K 分時抽取數字,得分大於等於 K 分時停止抽取數字。 抽取數字時,從 [1, W] 的範圍中隨機獲得一個整數作爲分數進行累計(可以重複抽),求最終分數小於等於 N 的概率是多少?
設 dp[x] 爲現有牌面分數爲x時,能獲勝的概率。
x 最多能到 K-1,因爲當大於等於 K 時,愛麗絲會停止抽牌,所以當遊戲結束時,即愛麗絲停止抽牌時,她可能達到的最大牌面是 K-1+W,而一開始她的牌面是 0,所以我們用一個長度爲 K+W 的 dp 數組來保存她在所有面值下的勝率。最後 dp[0],也就是最開始愛麗絲還沒有抽牌,她的牌面爲 0 時的勝率,這個就是我們要求的答案。
所以先將數組分成兩部分 [0,K-1] 和 [K,K+W-1],區別就是 [0,K-1] 時愛麗絲可以抽牌, [K,K-1+W] 時不能抽牌,那麼不能抽牌時她獲勝的概率是多少呢,很顯然牌面小於等於 N 時,概率就是 1,大於 N 概率就是 0,所以可以先直接填滿[ K,K-1+W]。
接下來,從 K-1 開始填 [0,K-1] 部分,因爲小於 K 時可以抽牌,且牌面機會都是均等的,她能抽取的面值在 [1,W] 之間,所以將概率之和平均一下就是 dp[x] 的概率:

dp[x] = 1/w * (dp[x+1]+dp[x+2]+dp[x+3]...+dp[x+w])

當計算下一輪的 dp[x-1] 時,我們可以用 O(1) 的時間複雜度計算 sum(dp[x]……dp[x+W-1])
方法就是先減去最右邊的 dp[x+w] ,再加上一個左邊的 dp(x) 即可。
複雜度分析:
時間複雜度:O(K+W),即dp數組長度。
空間複雜度:O(K+W),即dp數組長度。

代碼

class Solution {
    public double new21Game(int N, int K, int W) {
        double[] dp = new double[K + W];
        double S = 0;
        for(int i=K; i<K+W; i++){
            if(i<=N){
                dp[i] = 1;
                S += dp[i];
            }
            // else{
            //     dp[i] = 0; // 這句寫或不寫都可以,因爲java數組初始化時就是0。
            // }
        }
        for(int i=K-1; i>=0; i--){
            dp[i] = S/W;
            S = S - dp[i+W] + dp[i];
        }
        return dp[0];
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章