837. 新 21 点
837. 新 21 点 - 力扣(LeetCode)
算法思路
- 1.问题分析:游戏规则是每次抽取的点数为1到maxPts的均匀分布。当分数≥k时停止,求最终分数≤n的概率。
- 2.动态规划定义:定义
f[i]
为当前分数为i时,最终停止时分数≤n的概率。 - 3.边界条件:
- •当
i ≥ k
时,停止抽牌。若i ≤ n
,则概率为1(成功);若i > n
,则概率为0(失败)。但数组大小仅为n+1
(0到n),所以只考虑i≤n
的情况:f[i] = 1.0
。
- 4.状态转移(当
i < k
时):
- •需要继续抽牌,概率为所有下一可能状态的均值:
f[i] = (f[i+1] + f[i+2] + ... + f[i+maxPts]) / maxPts
。 - •用滑动窗口维护和:用变量
s
保存当前需要的连续区间和(长度最多为maxPts
)。
- 5.逆序计算:从后往前(
i
从n
递减到0
)计算f[i]
:
- •当前
s
维护的是f[i+1]
到f[i+maxPts]
的和(但不超过数组范围)。 - •计算
f[i]
后,更新s = s + f[i]
(添加新状态)。 - •若窗口超过
maxPts
(即i + maxPts <= n
),则移除末尾元素:s = s - f[i+maxPts]
。
- 6.初始化:
s = 0.0
,数组f
初始化为全0。
class Solution:
def new21Game(self, n: int, k: int, maxPts: int) -> float:
f = [0.0] * (n + 1) # 初始化DP数组,大小为n+1(0到n)
s = 0.0 # 滑动窗口的和
# 从后往前遍历i(n到0)
for i in range(n, -1, -1):
if i >= k:
f[i] = 1.0 # 停止抽牌,i≤n时概率为1
else:
f[i] = s / maxPts # 状态转移:均值
s += f[i] # 将f[i]加入窗口
if i + maxPts <= n:
s -= f[i + maxPts] # 维护窗口大小(移除超出部分)
return f[0] # 返回从0开始的概率