leetcode *837. 新21点(从后往前)(待深究)

【题目】*837. 新21点

837. 新21点
983. 最低票价

爱丽丝参与一个大致基于纸牌游戏 “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,则该答案将被视为正确答案通过。
此问题的判断限制时间已经减少。

【解题思路1】动态规划

以示例3为例
最后手上牌的点数最小为K = 17点,最大为 K + W - 1 = 26点
定义函数f:小于K点即可以继续抽牌时,抽完排点数不大于N的概率
当最后手中点数是17的时候,就不能继续抽排了
在这里插入图片描述
f(16):
抽牌范围是[1, 10],抽到每个牌的概率都是1/10
其中[1, 5]加上16是小于等于21的,[6, 10]加上16是大于21的
所以f(16) = 1/10 * (1+1+1+1+1+0+0+0+0+0)
在这里插入图片描述
f(15):
此时抽到1点的概率是1/10,1+15=16,f(16)=1/2,1/10 * 1/2就是15点抽到1点之后再抽一张牌点数小于等于21的概率
此时抽到2点的概率是1/10,2+15=17,终止抽牌,<=21的概率是1
……
此时抽到6点的概率是1/10,6+15=21,终止抽牌,<=21的概率是1
此时抽到7点的概率是1/10,7+15=22,终止抽牌,<=21的概率是0
……
此时抽到10点的概率是1/10,10+15=22,终止抽牌,<=21的概率是0
在这里插入图片描述
以此类推,f(x)就是状态转移方程,f(0)就是刚开始抽牌时,持续抽牌,最后点数不大于N=21的概率

因为最后点数在 [ K, K + W - 1 ] 点之间,所以数组大小只需要一个大小为 K+W = 27的数组
如上例子,主需要int[27]就可以了,最后点数在 [17, 21]之间肯定<=21,所以初始化这段数组为1(概率为1),即f(x)中x∈ [17, 21]时,f=1
然后从后往前计算各个f(x)值,即从16开始往前计算,每次都要计算后面的W = 10个概率和再除以W

改进:
在这里插入图片描述
如这边f(15)和f(16)求和概率S用到后面10格子里有9个格子都是重复的,所以采取去掉最后一个加上最前面一个格子的方法简化程序,避免每次重复求和S

问题
在这里插入图片描述
若将N改为28,那么声明的数组只到26,但是最开始还用得到27,28
但实际上能抽到的最大点数之和是26,不可能有27, 28,所以27 28的值是没有用的

  • 这需要在初始化的时候加上一些判断避免报错
  • 或者扩大初试数组的大小为[N+W],因为K<=N,所以K+W <= N+W

上面的后N=10个概率求和得到的,初始化S = N - K + 1在N=21时为5(对应f(16)里面的5个1),但当N=28时,S = 12超过了W = 10这是不可能的,所以初始化S要在N - K + 1和W之间取最小值
在这里插入图片描述

class Solution {
    public double new21Game(int N, int K, int W) {
        if (K == 0) {
            return 1.0;
        }
        double[] dp = new double[K + W];
        for (int i = K; i <= N && i < K + W; i++) {
            dp[i] = 1.0;
        }
        for (int i = K - 1; i >= 0; i--) {
            for (int j = 1; j <= W; j++) {
                dp[i] += dp[i + j] / W;
            }
        }
        return dp[0];
    }
}

在这里插入图片描述

class Solution {
    public double new21Game(int N, int K, int W) {
        if (K == 0) {
            return 1.0;
        }
        double[] dp = new double[K + W];
        for (int i = K; i <= N && i < K + W; i++) {
            dp[i] = 1.0;
        }
        dp[K - 1] = 1.0 * Math.min(N - K + 1, W) / W;
        for (int i = K - 2; i >= 0; i--) {
            dp[i] = dp[i + 1] - (dp[i + W + 1] - dp[i + 1]) / W;
        }
        return dp[0];
    }
}

其实就是一个从后往前的填格子动态规划
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章