leetcode 837

问题

Alice plays the following game, loosely based on the card game “21”.

Alice starts with 0 points, and draws numbers while she has less than K points. During each draw, she gains an integer number of points randomly from the range [1, W], where W is an integer. Each draw is independent and the outcomes have equal probabilities.

Alice stops drawing numbers when she gets K or more points. What is the probability that she has N or less points?

Example 1:

1
2
3
Input: N = 10, K = 1, W = 10
Output: 1.00000
Explanation: Alice gets a single card, then stops.

Example 2:

1
2
3
4
Input: N = 6, K = 1, W = 10
Output: 0.60000
Explanation: Alice gets a single card, then stops.
In 6 out of W = 10 possibilities, she is at or below N = 6 points.

Example 3:

1
2
Input: N = 21, K = 17, W = 10
Output: 0.73278

Note:

  1. 0 <= K <= N <= 10000
  2. 1 <= W <= 10000
  3. Answers will be accepted as correct if they are within 10^-5 of the correct answer.
  4. The judging time limit has been reduced for this question.

分析

分析可知获得任意一个点的大小与前面的点的大小是有关系的。因此,我们可以自顶向下递归地求出每一个点的概率,然后将 大于等于 K,小于等于 N 的点的概率相加就可以得到答案。但如果不进行优化答案自然就会TLE,如代码1。分析可知,每个点依赖于其前面的W个点,在我们依次求出 $1-N$ 这些点的概率的时候其实是可以重复利用之前的结果,也就是我们一直维护待求点前的$W$ 个点的概率和,这样求解每一个点的概率的时间复杂度就是 $O(1)$,而不是 $O(W)$ 。

代码1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
double new21Game(int N, int K, int W) {
vector<double> memo(K+W);
// K .. K+i ... N ... W
if (N >= K+W-1 || K == 0) // corner case
return 1;
double ans = 0.0f;
memo[0] = 1;
for (int i = N; i >= K; --i) {
ans += dp(i, K, W, memo);
}
return ans;
}
double dp(int x, int K, int W, vector<double> &memo) {
if (memo[x] != 0) return memo[x];
double ans = 0;
for (int i = 1; i <= W; ++i) {
int y = x-i;
if (y >= K) continue;
if (y < 0) break;
ans += dp(y, K, W, memo);
}
ans /= W;
return memo[x] = ans;
}
};

代码2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
double new21Game(int N, int K, int W) {
vector<double> dp(N+1);
// K .. K+i ... N ... W
if (N >= K+W-1 || K == 0) return 1; // corner case
double ans = 0.0f;
double sumW = 1;
dp[0] = 1;
for (int i = 1; i <= N; ++i) {
dp[i] = sumW/W;
if (i < K)
sumW += dp[i];
else
ans += dp[i];
if (i-W >= 0)
sumW -= dp[i-W];
}
return ans;
}
};