Medium
首先进行阅读理解:
<K
, 不继续抽牌,积分>=K
时停止抽牌<=N
获胜, 积分>N
失败阅读理解结束,开始思考过程:
掷两次骰子,求两次骰子和在4-6之间的概率
4的可能的组合为: (1,3)、(2,2)、(3,1)
5的可能的组合为: (2,3)、(3,2)
6的可能的组合为: (1,5)、(2,4)、(3,3)、(4,2)、(5,1)
P(sum) = P(4) + P(5) + P(6) = 3/36 + 2/36 + 5/36 = 5/18
<K
,我们还要继续抽卡,这样就是 \(1/W\) 还要在乘上后面的概率。就类似我们上面掷骰子例子中掷骰子的次数。当前和 >= K
使用记忆化递归,TLE
python3
class Solution:
@lru_cache(None)
def dfs(self, cur, N, K, W):
if cur >= K:
if cur <= N:
return 1
else:
return 0
sum = 0
for i in range(1, W+1):
sum += 1.0 / W * self.dfs(cur + i, N, K, W)
return sum
def new21Game(self, N: int, K: int, W: int) -> float:
return self.dfs(0,N,K,W)
TLE,需要更新思路 记忆化递归通常我们可以转成dp,bottom-up方式来优化。
拜读大佬解法,获得新思路
P(i) = P(i-1) * 1/W + P(i-2) * 1/W...P(i-W) * 1/W = (P(i-1)+... + P(i-W)) * 1/W
dp[i]
表示和为i的概率。N+1
,代表和为0~N
的概率。题目是求解 N
范围内的概率,所以后面的不用考虑综上,进行尝试,TLE。我真的是太难了。。
python3
class Solution:
def new21Game(self, N: int, K: int, W: int) -> float:
dp = [None] * (N + 1)
dp[0] = 1
for i in range(1, N + 1):
left = 0 if (i-W) < 0 else (i-W)
right = i if i <= K else K
dp[i] = sum(dp[left:right]) * (1/W)
return sum(dp[K:])
还需进行优化
dp[i]
求\([i-W,i-1]\)范围的值,虽然每次都向右移动了一个,但是还是整个遍历求和了。综上,进行尝试,AC!
python3
class Solution:
def new21Game(self, N: int, K: int, W: int) -> float:
dp = [None] * (N + 1)
dp[0] = 1
presum = 0
for i in range(1, N + 1):
if i-W > 0:
presum -= dp[i-W-1]
if i <= K:
presum += dp[i-1]
dp[i] = presum * (1/W)
return sum(dp[K:])