愛麗絲參與一個大體基於紙牌遊戲 「21點」 規則的遊戲,描述以下:愛麗絲以 0 分開始,並在她的得分少於 K 分時抽取數字。 抽取時,她從 [1, W] 的範圍中隨機得到一個整數做爲分數進行累計,其中 W 是整數。 每次抽取都是獨立的,其結果具備相同的機率。
當愛麗絲得到很多於 K 分時,她就中止抽取數字。 愛麗絲的分數不超過 N 的機率是多少?
示例
輸入:N = 6, K = 1, W = 10
輸出:0.60000
說明:愛麗絲獲得一張卡,而後中止。
在 W = 10 的 6 種可能下,她的得分不超過 N = 6 分。java
令 dp[x] 表示從分數爲 x 的狀態開始遊戲,最終得分不超過 N 的機率。則本題的目的是求 dp[0].
考慮到不超過K的最高得分爲 K-1,因爲分數超過 K 就結束遊戲,而從當前的 K-1 分再抽一次數字不小於 K 且不超過 N 的分數區間爲 [K, min(N, K + W - 1)]。這裏min在兩種狀況中取小:1)像示例中的抽W=10的前6種可能分數不會超過N;2)在 K-1 的狀態下即便抽到最大的點數W也不會超過N。
這樣,當 \(K \leq x \leq \min (N, K+W-1)\) 讓dp[x]=1,\(x>\min (N, K+W-1)\) 讓dp[x]=0。基於此,能夠獲得 \(0 \leq x < K\) 時的轉移方程:spa
能夠在每次計算 dp[x]的時候從 dp[x+1] 累加到 dp[x+W],但這中間顯然存在重複計算。所以能夠簡化一下計算,對dp的相鄰項作差:code
這樣就獲得新的轉移方程:遊戲
注意 \(0 \leq x <K-1\)
因此這裏dp[K-1]須要單獨計算一下:
因爲只有當 \(K \leq x \leq \min (N, K+W-1)\) 時 dp[x]=1,因此很容易獲得\(dp[K-1] = \frac{min(N, K + W - 1) - (K-1)}{W} = \frac{min(N-K+1, W)}{W}\)io
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; // 注意 * 1.0 作類型轉換 for(int i = K - 2; i >= 0; i--){ dp[i] = dp[i + 1] - (dp[i + 1 + W] - dp[i + 1]) / W; } return dp[0]; } }