题目描述
爱丽丝参与一个大致基于纸牌游戏 “21点” 规则的游戏,描述如下:
爱丽丝以 0
分开始,并在她的得分少于 K
分时抽取数字。 抽取时,她从 [1, W]
的范围中随机获得一个整数作为分数进行累计,其中 W
是整数。 每次抽取都是独立的,其结果具有相同的概率。
当爱丽丝获得不少于 K
分时,她就停止抽取数字。爱丽丝的分数不超过 N
的概率是多少?
样例
输入:N = 10, K = 1, W = 10
输出:1.00000
说明:爱丽丝得到一张卡,然后停止。
输入:N = 6, K = 1, W = 10
输出:0.60000
说明:爱丽丝得到一张卡,然后停止。
在 W = 10 的 6 种可能下,她的得分不超过 N = 6 分。
输入:N = 21, K = 17, W = 10
输出:0.73278
注意
0 <= K <= N <= 10000
1 <= W <= 10000
- 如果答案与正确答案的误差不超过
10^-5
,则该答案将被视为正确答案通过。 - 此问题的判断限制时间已经减少。
算法
(动态规划) O(n)
- 设 f(i) 表示得到 i 点分数的概率。
- 初始时 f(0)=1,其余为 0。
- 转移时,枚举能从那些值得到 i,即 j∈[i−W,i−1] 且 j>=0,j<K,f(i)=f(i)+f(j)W。
- 最终答案为 f(K)+f(K+1)+⋯+f(N)。
- 暴力枚举转移会超时,需要一个前缀和 s(i) 来维护 f(0)+f(1)+⋯+f(i) 的值,这样转移只需要 O(1) 的时间。
时间复杂度
- 状态数 n 个,转移只需要常数的的时间,故总时间复杂度为 O(n)。
空间复杂度
- 和状态数一致,故空间复杂度为 O(n)。
C++ 代码
class Solution {
public:
double new21Game(int N, int K, int W) {
if (K == 0) return 1.0;
vector<double> f(N + 1, 0), s(N + 1, 0);
f[0] = s[0] = 1;
for (int i = 1; i <= N; i++) {
int l = max(0, i - W), r = min(K - 1, i - 1);
if (l <= r) {
if (l == 0) f[i] = s[r] / W;
else f[i] = (s[r] - s[l - 1]) / W;
}
s[i] = s[i - 1] + f[i];
}
return s[N] - s[K - 1];
}
};
代码中
l
有可能越过K
,需要考虑已修正
代码过不了,要特判一下K是否等于0
对,
K == 0
是平凡情况。