leetcode - 837. 新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

提示:

  1. 0 <= K <= N <= 10000
  2. 1 <= W <= 10000
  3. 如果答案与正确答案的误差不超过 10^-5,则该答案将被视为正确答案通过。
  4. 此问题的判断限制时间已经减少。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/new-21-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:根据leetcode官方解题思路,使用动态规划,设dp[i]表示从第i个数开始,经过若干轮之后,这个数的值在k到n之间的概率。

根据题意,当初始值i在k到n之间时,dp[i]=1,表示在从k到n之间的任意一个数,其位于k到n之间的概率为1,这也是我们代码中初始化的依据。

对于k到n之间的点,我们得到了其初始化的概率,那么对于值为k-1的数,其值不在k到n之间,那么它需要走一步才能到达k到n之间。但是这里存在一种情况,就是w的值可能大于n-k的值,也就是说k-1+w的值可能大于n,这样得到的值不是我们想要的,得让其概率表示为0;还有一种情况是w的值小于n-k,那么d[k-1]的值就是1了,表示值为k-1的点到k和n之间的概率为1。

对于dp[i],其概率计算公式为 d p [ i ] = 1 w ∗ ( d p [ i + 1 ] + d p [ i + 2 ] + . . . + d p [ i + w ] ) dp[i]=\frac{1}{w}*(dp[i+1]+dp[i+2]+...+dp[i+w]) dp[i]=w1(dp[i+1]+dp[i+2]+...+dp[i+w]),也就是说dp[i]的概率值是有其后面的w个数的概率值来决定的。

但是上面我们也说过了,w和n-k有两种情况,因此在初始化k到n的概率的时候,我们需要更多的内存来表示。具体来说,就是建立数组

vector<double> dp(n+w+1);  //为什么创建的数组不是dp(n+1)而是dp(n+w+1),下面代码中会有分析和解释
for(int i=k;i<=n;i++)  //这里表示从k到n的dp[i]的概率表示为1
{
     dp[i] = 1;
}
//因为每个dp[i]考虑的是其后面的w个数字的概率值,因此使用一个sum计算其后面w个数的概率值之和
//考虑到w和n-k的两种关系,对于k-1这个数,如果w>n-k,那么其实k-1下一步在k到n之间的概率值为w/(n-k),sum的值就表示n-k+1
//对于k-1这个数,如果w>n-k,那么其实k-1下一步在k到n之间的概率值为1sum的值就表示w
if(w>=n-k+1)
    sum = double(n-k+1);
else
    sum = w;

这样我们就确定了dp[k-1]的概率值,对于dp[k-2],其对应的概率值可以表示为 d p [ i ] = 1 w ∗ ( s u m + d p [ i ] − d p [ i + w ] ) dp[i] = \frac{1}{w}* (sum + dp[i] - dp[i+w]) dp[i]=w1(sum+dp[i]dp[i+w])。因为w表示所有可能走的步长,因此每一次迭代更新,只要加上最新的一个,并且减去最后的一个就可以得到我们需要的结果。但是前面说了,因为w和n-k之间存在两种情况,因此为了方面迭代,初始化数组的时候使用了vector dp(n+w+1); 目的就是保证在两种情况下的计算。

其具体的c++代码如下:

class Solution {
public:
    double new21Game(int n, int k, int w) {
        vector<double> dp(n+w+1);  //初始化数组,注意是n+w+1而不是n+1
        double sum=0;  //用于计算w个数对应的概率值之和
        for(int i=k;i<=n;i++)   //将k到n之间的概率值表示为1
        {
            dp[i] = 1;
        }
        if(w>=n-k+1)  //如果w>=n-k+1,sum初始化为n-k+1,意思是w种选择中只有n-k+1种符合需求
            sum = double(n-k+1);
        else    //如果如果w<n-k+1sum初始化为w,意思是w种选择都符合需求
            sum = w;
        for(int i=k-1;i>=0;i--)
        {
            dp[i] = sum/double(w);  //首先计算dp[k-1]的对应的概率值
            sum = sum + dp[i] - dp[i+w];  //然后以k-1这个数为起点,sum加上dp[k-1]的值,减去dp[k-1+w]的值,作为sum的新一轮迭代的值
        }
        return dp[0];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值