No.837 LeetCode题目 “新21点”
作者:互联网
文章目录
题目描述
爱丽丝参与一个大致基于纸牌游戏 “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
提示:
0 <= K <= N <= 10000
1 <= W <= 10000
如果答案与正确答案的误差不超过 10^-5,则该答案将被视为正确答案通过。
此问题的判断限制时间已经减少。
解题思路
本题利用动态规划的思想,令dp[i]
表示当手里的数字为i时获胜的概率,本题要求dp[0]
。
因为手中牌最大为K+W-1
(手中牌为K-1
,又抽中了W
),因此初始化一个长度为K+W
的数组。
- 当手中的牌当
N >= i >= K
时,游戏结束,获得胜利(返回1
)。当i>N
时,游戏失败(返回0
)。 - 当手中的牌
i <= K-1
时,dp[i] = (dp[i+1]+...+dp[i+W]) / W
。
由于利用循环求和dp[i+1]+...+dp[i+W]
时会超出时间限制,因此我们利用窗口效应,设一个变量window
来记录当前的dp[i+1]+...+dp[i+W]
。i
每向前移动一格,窗口也会跟着移动一格。如下图所示:
上图参考:
https://leetcode-cn.com/problems/new-21-game/solution/
具体实现细节详见伪代码。
完整代码
class Solution {
public:
double new21Game(int N, int K, int W) {
//如果K为0,最后肯定可以成功。
if(K == 0){
return 1.0;
}
//利用动态规划的思想,dp[i]表示当手里的数字为i时获胜的概率,本题要求dp[0]。
//手中牌最大为K+W-1,因此初始化一个长度为K+W的数组。
double dp[K+W];
/*for(int i =0;i<K+W-1;i++){
dp[i] = 0;
}*/
double window = 0.0;
//当N>=i>=K时,游戏结束,获得胜利。当i>N时,游戏失败。
for(int i = K;i<K+W;i++){
dp[i] = (i > N) ? 0 : 1.0;
window += dp[i];//利用window值记录这一总和(滑动窗口原理)
}
//当i<=K-1时,dp[i] = (dp[i+1]+...+dp[i+W])/W
for(int i = K-1;i>=0;i--){
dp[i] = window/W;
window += dp[i];
window -= dp[i+W];
}
return dp[0];
}
};
性能结果
个人感悟
做了一些动态规划的题,个人感觉确实是状态转移方程最难写。
有些时候需要求dp[i]
,比如No.194 LeetCode题目 “打家劫舍”。
有些时候需要求dp[0]
,比如本题。
这两种思路主要是要考虑从前往后计算,还是从后往前计算。
这道题就是要从后往前计算,因为后一状态决定了前一状态。
同时dp[i]
这个物理量如何解释也是要思考的问题之一,之后遇到动态规划还要多多练习。
标签:10,21,int,window,爱丽丝,No.837,LeetCode,dp 来源: https://blog.csdn.net/qq_39856931/article/details/106533143