题目
爱丽丝参与一个大致基于纸牌游戏 “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. 此问题的判断限制时间已经减少。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/new-21-game
解题
思路
题目分析:每次抽取 [1, W] 中的一个数,直到抽出分数总数在>=K的时候<=N的概率。
运用动态规划的思想,设dp[i]表示从已得分i开始游戏可获胜的概率,该题求解的就是dp[0]。 从概率为1的情况和概率为0的情况这两种边界情况倒推出中间各个概率的情况。
在游戏中,dp[i]的i最大取值为结束游戏的那一步的最大取值,假设倒数第二步正好是<K的最大数K-1,最后一步为可选择的最大值W,所以i在该游戏中取值最大值为K-1+W(K-1+W必然>K)。
**概率为0的情况:**当i大于N的时候必然不满足条件,概率为0,i超过游戏中最大步数,即>K-1+W时概率也为0。所以当i>min(K-1+W,N)时,dp[i] = 0。
概率为1的情况:满足游戏条件(i<K-1+W)且结束游戏(i>K)且i小于N是,满足条件,概率为1。所以当i∈[K,min(K-1+W,N)]时,dp[i] = 1。
中间各种概率的情况: 当i∈[0,K) 时,它可以用再走一步再开始游戏的概率递推而来(下一步选择[1,W]中的任意一个数的情况,即dp[i+x] ,x∈[1,W] 总共有W种情况,也有W个dp概率)。可以得到以下状态转移方程
d
p
[
i
]
=
d
p
[
i
+
1
]
+
d
p
[
i
+
2
]
+
d
p
[
i
+
3
]
+
⋯
+
d
p
[
i
+
W
]
W
(
i
∈
[
0
,
K
)
)
dp[i] = \frac{dp[i+1]+dp[i+2]+dp[i+3]+⋯+dp[i+W]}{W} (i∈[0,K))
dp[i]=Wdp[i+1]+dp[i+2]+dp[i+3]+⋯+dp[i+W](i∈[0,K))
因为上述递推公式每一步都要计算其后的W种情况的dp,复杂度太高,会超时,所以对这个递推公式进行简化:
d
p
[
i
]
−
d
p
[
i
+
1
]
=
d
p
[
i
+
1
]
+
d
p
[
i
+
2
]
+
d
p
[
i
+
3
]
+
⋯
+
d
p
[
i
+
W
]
W
−
d
p
[
i
+
2
]
+
d
p
[
i
+
3
]
+
d
p
[
i
+
4
]
+
⋯
+
d
p
[
i
+
W
+
1
]
W
=
d
p
[
i
+
1
]
−
d
p
[
i
+
W
+
1
]
W
dp[i] - dp[i+1] = \frac{dp[i+1]+dp[i+2]+dp[i+3]+⋯+dp[i+W]}{W} - \frac{dp[i+2]+dp[i+3]+dp[i+4]+⋯+dp[i+W+1]}{W} = \frac{dp[i+1]-dp[i+W+1]}{W}
dp[i]−dp[i+1]=Wdp[i+1]+dp[i+2]+dp[i+3]+⋯+dp[i+W]−Wdp[i+2]+dp[i+3]+dp[i+4]+⋯+dp[i+W+1]=Wdp[i+1]−dp[i+W+1]
d
p
[
i
]
=
d
p
[
i
+
1
]
+
d
p
[
i
+
1
]
−
d
p
[
i
+
W
+
1
]
W
=
d
p
[
i
+
1
]
−
d
p
[
i
+
W
+
1
]
−
d
p
[
i
+
1
]
W
(
i
∈
[
0
,
K
−
1
)
)
dp[i] = dp[i+1] + \frac{dp[i+1]-dp[i+W+1]}{W} =dp[i+1]-\frac{dp[i+W+1]-dp[i+1]}{W} (i ∈[0,K-1))
dp[i]=dp[i+1]+Wdp[i+1]−dp[i+W+1]=dp[i+1]−Wdp[i+W+1]−dp[i+1](i∈[0,K−1))
因为存在dp[i+1],所以i+1 ∈[0,K) ,所以 i ∈[0,K-1),简化的递推公式不包含i = K-1的情况。K-1的情况还用原始的递推公式来单独计算:
d
p
[
K
−
1
]
=
d
p
[
K
]
+
d
p
[
K
+
1
]
+
d
p
[
K
+
2
]
+
⋯
+
d
p
[
K
+
W
]
W
dp[K-1] = \frac{dp[K]+dp[K+1]+dp[K+2]+⋯+dp[K+W]}{W}
dp[K−1]=Wdp[K]+dp[K+1]+dp[K+2]+⋯+dp[K+W]
i = K-1的情况由i∈[K,K+W]的情况递推而来,而i∈[K,K+W]的情况有概率为1和概率为0两种情况的dp相加可得(dp[K+W]必然等于0,所以一定存在概率为0的情况),概率为0的忽略,概率为1只有i∈[K,min(K-1+W,N)],所以相加的结果就是min(K-1+W,N)-K+1
d
p
[
K
−
1
]
=
m
i
n
(
K
−
1
+
W
,
N
)
−
K
+
1
W
=
m
i
n
(
W
,
N
−
K
+
1
)
W
dp[K-1] = \frac{min(K-1+W,N)-K+1}{W} = \frac{min(W,N-K+1)}{W}
dp[K−1]=Wmin(K−1+W,N)−K+1=Wmin(W,N−K+1)
综上,对i的所有情况的dp[i]进行总结:
d
p
[
i
]
=
{
d
p
[
i
+
1
]
+
d
p
[
i
+
1
]
−
d
p
[
i
+
W
+
1
]
W
=
d
p
[
i
+
1
]
−
d
p
[
i
+
W
+
1
]
−
d
p
[
i
+
1
]
W
,
i ∈[0,K-1)
m
i
n
(
W
,
N
−
K
+
1
)
W
,
i = K-1
1
,
i ∈ [K,min(K-1+W,N)]
0
,
i ∈ (min(K-1+W,N),∞)
dp[i] = \begin{cases} dp[i+1] + \frac{dp[i+1]-dp[i+W+1]}{W} =dp[i+1]-\frac{dp[i+W+1]-dp[i+1]}{W}, & \text{i ∈[0,K-1)}\\ \frac{min(W,N-K+1)}{W}, & \text{i = K-1}\\ 1, &\text{i ∈ [K,min(K-1+W,N)]}\\ 0, &\text{i ∈ (min(K-1+W,N),∞)} \end{cases}
dp[i]= ⎩⎪⎪⎪⎨⎪⎪⎪⎧ dp[i+1]+Wdp[i+1]−dp[i+W+1]=dp[i+1]−Wdp[i+W+1]−dp[i+1], Wmin(W,N−K+1), 1, 0,i ∈[0,K-1)i = K-1i ∈ [K,min(K-1+W,N)]i ∈ (min(K-1+W,N),∞)
Java实现
class Solution {
public double new21Game(int N, int K, int W) {
//动态规划,从后往前推,从概率为1(必然)到概率为0一步步 一次次抽取的推
//判断特殊情况
if(K == 0) return 1.0;//K=0根本不会开始抽取数字,结束游戏时总分数就为0肯定<=N
//设置动态规划数组
double[] dp = new double[K+W];//dp[i]的i取值从0到K-1+W一共K+W个数
int m = Math.min(K-1+W,N);
//i>m的取值
for(int i = m+1; i<=K-1+W; i++){
dp[i] = 0;
}
//K<=i<=m的取值
for(int i = K; i <= m;i++){
dp[i] = 1;
}
//i = K-1的取值:
dp[K-1] = 1.0*Math.min(W,N-K+1)/W;
//0<=i<=K-2的取值
for(int i = K-2; i >=0; i--){
dp[i] = dp[i+1]-(dp[i+W+1]-dp[i+1])/W;
}
return dp[0];
}
}
参考:力扣官方题解