leetcode *837. 新21點(從後往前)(待深究)

【題目】*837. 新21點

837. 新21點
983. 最低票價

愛麗絲參與一個大致基於紙牌遊戲 “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

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

【解題思路1】動態規劃

以示例3爲例
最後手上牌的點數最小爲K = 17點,最大爲 K + W - 1 = 26點
定義函數f:小於K點即可以繼續抽牌時,抽完排點數不大於N的概率
當最後手中點數是17的時候,就不能繼續抽排了
在這裏插入圖片描述
f(16):
抽牌範圍是[1, 10],抽到每個牌的概率都是1/10
其中[1, 5]加上16是小於等於21的,[6, 10]加上16是大於21的
所以f(16) = 1/10 * (1+1+1+1+1+0+0+0+0+0)
在這裏插入圖片描述
f(15):
此時抽到1點的概率是1/10,1+15=16,f(16)=1/2,1/10 * 1/2就是15點抽到1點之後再抽一張牌點數小於等於21的概率
此時抽到2點的概率是1/10,2+15=17,終止抽牌,<=21的概率是1
……
此時抽到6點的概率是1/10,6+15=21,終止抽牌,<=21的概率是1
此時抽到7點的概率是1/10,7+15=22,終止抽牌,<=21的概率是0
……
此時抽到10點的概率是1/10,10+15=22,終止抽牌,<=21的概率是0
在這裏插入圖片描述
以此類推,f(x)就是狀態轉移方程,f(0)就是剛開始抽牌時,持續抽牌,最後點數不大於N=21的概率

因爲最後點數在 [ K, K + W - 1 ] 點之間,所以數組大小隻需要一個大小爲 K+W = 27的數組
如上例子,主需要int[27]就可以了,最後點數在 [17, 21]之間肯定<=21,所以初始化這段數組爲1(概率爲1),即f(x)中x∈ [17, 21]時,f=1
然後從後往前計算各個f(x)值,即從16開始往前計算,每次都要計算後面的W = 10個概率和再除以W

改進:
在這裏插入圖片描述
如這邊f(15)和f(16)求和概率S用到後面10格子裏有9個格子都是重複的,所以採取去掉最後一個加上最前面一個格子的方法簡化程序,避免每次重複求和S

問題
在這裏插入圖片描述
若將N改爲28,那麼聲明的數組只到26,但是最開始還用得到27,28
但實際上能抽到的最大點數之和是26,不可能有27, 28,所以27 28的值是沒有用的

  • 這需要在初始化的時候加上一些判斷避免報錯
  • 或者擴大初試數組的大小爲[N+W],因爲K<=N,所以K+W <= N+W

上面的後N=10個概率求和得到的,初始化S = N - K + 1在N=21時爲5(對應f(16)裏面的5個1),但當N=28時,S = 12超過了W = 10這是不可能的,所以初始化S要在N - K + 1和W之間取最小值
在這裏插入圖片描述

class Solution {
    public double new21Game(int N, int K, int W) {
        if (K == 0) {
            return 1.0;
        }
        double[] dp = new double[K + W];
        for (int i = K; i <= N && i < K + W; i++) {
            dp[i] = 1.0;
        }
        for (int i = K - 1; i >= 0; i--) {
            for (int j = 1; j <= W; j++) {
                dp[i] += dp[i + j] / W;
            }
        }
        return dp[0];
    }
}

在這裏插入圖片描述

class Solution {
    public double new21Game(int N, int K, int W) {
        if (K == 0) {
            return 1.0;
        }
        double[] dp = new double[K + W];
        for (int i = K; i <= N && i < K + W; i++) {
            dp[i] = 1.0;
        }
        dp[K - 1] = 1.0 * Math.min(N - K + 1, W) / W;
        for (int i = K - 2; i >= 0; i--) {
            dp[i] = dp[i + 1] - (dp[i + W + 1] - dp[i + 1]) / W;
        }
        return dp[0];
    }
}

其實就是一個從後往前的填格子動態規劃
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章