題目
愛麗絲參與一個大致基於紙牌遊戲 “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
,則該答案將被視爲正確答案通過。 - 此問題的判斷限制時間已經減少。
解題思路
簡單分析一下題目,得分少於 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];
}
}